Name

lane Copy-on-write Git worktrees with memory that survives them.

git worktree add gives you a clean checkout and none of the ignored files that make it fast: node_modules, target/, .env. You reinstall and rebuild from scratch, even though those caches are already on disk. Whatever the last branch learned about a file stays in that branch.

lane new clones ignored paths by reference, sharing bytes on disk. The new tree starts quickly with its prewarmed cache(s).

$ curl -fsSL https://lane.lukeed.com | sh

Workflow

Left is the disk: one set of blocks, every tree pointing at it. Right is what you type.

diskone treesharedcopy-on-writelane changesthree laneslandedfreed

main/12,283 files

by reference

fix-login/

agent-b/

agent-c/

shared with main/lane changes

memory

.lane/memory/nothing noted yet1 note · fix-login1 note · content-changed4 notes · 3 lanes4 on main · 1 in the attic

1358.1 MiB shared0 B writtena byte copy: 1358.1 MiB
1358.1 MiB shared12 KiB writtenone file left the shared set
1358.1 MiB shared106.5 MiB writtenthe rebuild, not your edit
1358.1 MiB shared114.9 MiB writtenthree lanes, one set of extents
1358.1 MiB shared3 commits landedrebased, then fast-forwarded
1358.1 MiB shared114.9 MiB freedthe commits and the notes stayed
  1. 01lane new

    Ignored files arrive by reference. The cache is already warm.

    zsh — ~/repo1 of 5
    $ lane new fix-login
    reflink: yes (clonefile)
    12283 files cloned (1358.1 MiB, 0 copied)
    ~/repo/.lane/trees/fix-login
    $ ls node_modules | wc -l
    4612
    $ cargo build
    Finished `dev` profile in 0.21s

    12,283 files, 1358.1 MiB shared, 0 copied. node_modules is there. cargo build finishes in 0.21s because target/ was never missing.

  2. 02lane note

    Record what must stay true. Then edit.

    zsh — ~/…/fix-login2 of 5
    $ lane note add src/auth.rs -a "fn verify" \
    > "early return leaks token length"
    noted -> src/auth.rs#fn verify
    $ $EDITOR src/auth.rs
    $ git commit -am "make verify constant-time"
    [fix-login 4f1a92c] 1 file changed

    The note is a new file. Editing src/auth.rs un-shares only that file. main still reads the original block.

  3. 03lane check

    The rebuild stays in the lane. lane check flags the body.

    zsh — ~/…/fix-login3 of 5
    $ cargo build
    Compiling myapp v0.1.0
    Finished `dev` profile in 10.2s
    $ lane check
    fresh 7
    content-changed 1
    contract-changed 0
    anchor-missing 0

    550 files rewritten — 106.5 MiB now owned by the lane. fn verify kept its signature and changed its body, so the note is content-changed.

  4. 04lane ls

    Three worktrees. One set of extents.

    zsh — ~/repo4 of 5
    $ lane new agent-b && lane new agent-c
    2 lanes cloned (0 copied)
    $ lane ls
    fix-login open dirty 1 pending note(s)
    agent-b open dirty 3 pending note(s)
    agent-c open clean 0 pending note(s)

    Each lane writes only the blocks it touches and carries its own memory notes. Every note is its own file, so two agents can annotate the same function at once.

  5. 05lane merge

    The worktree is removed. Commits and notes stay.

    zsh — ~/…/fix-login5 of 5
    $ lane note replace 01M0B4KQTX7H3EZ8FE7S6BJ91N \
    > "constant-time; no early return"
    replacement queued -> 01M0B4KQTX7H3EZ8FE7S6BJ91N src/auth.rs#fn verify
    $ lane merge # in each lane, in any order
    rebased onto main
    memory: +1 new; checked 8
    7 fresh, 1 content-changed
    0 contract-changed, 0 anchor-missing
    committed memory update
    fast-forwarded main
    removed lane fix-login

    The 114.9 MiB the lanes allocated is freed. The drifted note was rewritten; the old one is in .lane/attic/.

If trunk is protected, use lane push instead of lane merge. After the pull request lands, lane prune removes the worktree.

Description

A second worktree that costs no disk and no cold build.

Lane asks git for every ignored path — nested ones like packages/a/node_modules, single files like .env — and clones each by reference. On APFS, btrfs, and XFS with reflink=1, the two copies share the same extents. The second tree takes no new space, and the build cache is already warm.

In the new treegit worktree addlane new
tracked fileschecked outchecked out
node_modules, at any depthabsentexists, by reference
target/, dist/, .venv/absentexists, by reference
.env and the rest of .gitignoreabsentexists, by reference
uncommitted workabsentwith --dirty
what earlier branches learnedabsentlane why <file>
what those 12,283 files cost1358.1 MiB, then npm i and a cold build0 B, 4.0s

Edit a file and only that file's blocks un-share. One source edit plus its incremental rebuild rewrote 550 files — 106.5 MiB. The other 1251.6 MiB stayed shared. The 106.5 MiB goes away when the lane does.

Reflink works on APFS, btrfs, XFS with reflink=1, bcachefs, and recent ZFS. Everywhere else, lane init says so up front and lane new leaves a plain worktree — byte-copying a build cache is the expensive thing lane exists to avoid. Opt a path out with git config --add lane.exclude target.

Memory

The reason you did it that way, kept next to the code it constrains.

A note records what must stay true, not what you changed.

$ lane note add src/auth.rs -a "fn verify" \
    "must stay constant-time; early return leaks token length"
noted -> src/auth.rs#fn verify

$ lane why src/auth.rs

[fn verify]
  - 01M0B9MBYB · 2026-08-14
    must stay constant-time; early return leaks token length

Or leave it in the commit you were already writing. lane install hooks reads the Why: trailer and nothing else — no diff, no commit body, no history import:

$ git commit -am "make verify constant-time"
  ...
  Why: src/auth.rs#fn verify | early return leaks token length

The subject says what you did. The trailer says what must stay true.

Anchors — what the note is about

fn verifya declaration, by kind and name
verifya uniquely named declaration
#script, #stylea top-level block in .svelte, .vue, html
## Rate limitinga markdown section
@filethe whole file (default)

Tree-sitter resolves these, not a regular expression. A span ends where the declaration ends. A # inside a code fence is not a heading.

Freshness — what happens as the code moves

marktiermeaningwhat lane does
·freshThe anchored span is unchanged.Nothing.
~content-changedThe implementation changed.Flagged until you resolve it.
!contract-changedThe declaration changed.Flagged until you resolve it.
xanchor-missingThe symbol no longer resolves.Moved to .lane/attic/.

Each anchor is hashed on its own span, normalized, so a formatter stales nothing. Editing #script leaves a note on #style alone. A renamed file is followed — lane audit reads git's own rename detection. A drifted note stays flagged until you confirm, replace, or retire it.

Agents

The next agent reads the constraints before it edits.

Two agents can note the same file, and the same anchor, at the same time. Each note is a new file, so nothing locks. Land them in any order; both notes are there afterwards.

$ lane why src/auth.rs

[fn verify]
  - 01M0B9MBYB · 2026-08-14
    must stay constant-time; early return leaks token length
  - 01M0B9MZQ4 · 2026-08-14
    the token bytes must never reach a log, even at debug

lane init writes a four-line protocol into AGENTS.md. Notes are plain markdown at predictable paths, so an agent finds them with no plugin.

Installation

Get the binary

$ curl -fsSL https://lane.lukeed.com | shmacOS and Linux, arm64 and x86_64. Lands in ~/.local/bin. Set LANE_INSTALL to put it elsewhere, LANE_VERSION to pin a release. Read it first at lane.lukeed.com/install.sh.
$ cargo binstall --git https://github.com/lukeed/lane laneThe same prebuilt binary, if you already run binstall.
$ cargo install --git https://github.com/lukeed/laneBuilds from source. Rust 1.85 or newer, edition 2024.

Once per machine

$ eval "$(lane shellenv)"Put it in .zshrc or .bashrc. Without it, lane new prints a path instead of moving you into the lane.

Once per repository

$ cd yourproject && lane initScaffolds .lane/, adds the union merge rule and the agent protocol, and reports whether this filesystem does reflink. Commit what it writes.
$ lane install hooksCaptures Why: trailers from commit messages. Worktrees share a hooks directory, so this covers every lane. Optional.
$ lane install skillWrites the fuller agent workflow to .agents/skills/lane/SKILL.md. Optional; lane init already leaves a short protocol in AGENTS.md.
$ cargo run -p lane-tour -- start
builds a throwaway repo next to this checkout and prints its path

The tour runs the real workflows — a note drifting, three lanes landing out of order, two landings colliding — and prints every command before it runs.

See also