Contents Isolated workspaces and worktrees

Parallel work

Isolated workspaces and worktrees

A worktree gives a parallel agent its own working directory, its own HEAD and its own index. A distinct branch per worktree is a safeguard git applies by default, not a guaranteed property: git worktree add refuses when the requested branch is already checked out in another worktree, though --force overrides that safeguard.2 Beyond the working directory, HEAD and the index, git's rule for what else is per-worktree is precise: all worktrees of a repository share one object database and one config file by default, pseudo refs directly under the git directory such as HEAD are per-worktree, and "all refs starting with refs/ are shared", with only refs/bisect, refs/worktree and refs/rewritten excepted.1 HEAD and the index are per-worktree, so two agents can sit on different commits with different staged content.2

Follow that rule to its conclusion and a surprise falls out. The stash lives in the ref refs/stash, with older entries in that ref's reflog; refs/stash sits under refs/ and is not one of the three exceptions, so the stash stack is shared across every worktree of the repository.31 An agent that stashes is reaching into a stack another agent can pop. Per-worktree configuration is not on by default either. It requires enabling the worktreeConfig extension first.1

The safeguards matter because they are what keeps parallel work apart. git worktree add refuses when the requested branch is already checked out in another worktree unless forced, and git worktree remove refuses on a worktree with modifications or untracked files.2 Claude Code adds runtime checks of its own, blocking edits, command working directories and redirected git invocations that target the main checkout.4

The diagram maps isolation and sharing.

<div class="tl">
  <p class="hd">three worktrees advance and merge on separate lanes (positions illustrative), standing on one shared store</p>
  <div class="ln">
    <span class="nm">worktree A</span>
    <span class="tk"><span class="br" style="width:55%"></span><span class="mk" style="left:55%">merge</span></span>
  </div>
  <div class="ln">
    <span class="nm">worktree B</span>
    <span class="tk"><span class="br b2" style="width:55%"></span><span class="mk m2" style="left:55%">merge</span></span>
  </div>
  <div class="ln">
    <span class="nm">worktree C</span>
    <span class="tk"><span class="br b3" style="width:55%"></span><span class="mk m3" style="left:55%">merge</span></span>
  </div>
  <div class="ln">
    <span class="nm">all three</span>
    <span class="sh">
      <span class="tick t1"></span><span class="tick t2"></span><span class="tick t3"></span>
      <span class="shl">one object store; refs/ shared except refs/bisect, refs/worktree, refs/rewritten; one refs/stash</span>
    </span>
  </div>
  <p class="ft">per-worktree: working directory, HEAD, index. Branch checkout is exclusive by default, unless forced. Everything in the dashed band is shared, so the lanes are separate only above it.</p>
</div>
<style>
.tl { font: 13px system-ui, sans-serif; color: currentColor; padding: 6px 2px; }
.tl .hd, .tl .ft { opacity: 0.7; margin: 4px 0 10px; }
.tl .ft { margin: 14px 0 0; }
.ln { display: flex; align-items: center; gap: 10px; margin: 12px 0; }
.nm { flex: none; width: 88px; opacity: 0.85; }
.tk { position: relative; flex: 1; height: 16px; border-bottom: 1px solid currentColor; }
.br { position: absolute; left: 0; top: 4px; height: 8px; border-radius: 4px;
      background: currentColor; opacity: 0.4; transform-origin: left center;
      animation: grow 7s ease-in-out infinite; }
.b2 { animation-delay: 0.4s; }
.b3 { animation-delay: 0.8s; }
.mk { position: absolute; top: -3px; margin-left: 6px; padding: 0 5px; font-size: 11px;
      border: 1px solid currentColor; border-radius: 3px; opacity: 0.85;
      animation: land 7s ease-in-out infinite; }
.m2 { animation-delay: 0.4s; }
.m3 { animation-delay: 0.8s; }
.sh { position: relative; flex: 1; border: 1px dashed currentColor; border-radius: 4px;
      padding: 5px 8px; opacity: 0.95; animation: band 7s ease-in-out infinite; }
.shl { font-size: 12px; }
.tick { position: absolute; top: -8px; width: 1px; height: 8px; background: currentColor; opacity: 0.5; }
.t1 { left: 16%; }
.t2 { left: 50%; }
.t3 { left: 84%; }
@keyframes grow {
  0% { transform: scaleX(0.02); }
  55% { transform: scaleX(1); }
  100% { transform: scaleX(1); }
}
@keyframes land {
  0%, 45% { opacity: 0; }
  60%, 100% { opacity: 0.85; }
}
@keyframes band {
  0%, 100% { opacity: 0.65; }
  55% { opacity: 1; }
}
</style>

Figure: Worktrees separate working directories, HEADs and indexes, but share objects, most refs and the stash. One practical point rests on weaker ground than the rest. A worktree is a checkout of tracked files, so ignored or otherwise untracked dependency directories and environment files are absent unless separately copied or initialized. Git's own documentation says nothing at all about untracked or ignored files when adding a worktree; that point is supported by vendor documentation only. Claude Code documents a .worktreeinclude file that copies matching ignored files into each worktree, and Cursor declares setup commands in its own config while advising against symlinking dependencies in.45

When this does not hold. The isolation is architectural, not measured. No primary source states a recommended or maximum number of concurrent worktrees, or a measured disk cost against a full clone, and nothing measures whether worktree parallelism improves throughput. The narrow claim a worktree earns is that edits in one cannot collide with edits in another. Everything shared, the objects, the refs and the stash, is still a place where two agents meet.

References

Quizzes
  1. Several worktrees of one repository each stash some work. Where do those stashed entries end up?

    • One stack, shared
    • A private stack inside each worktree's own administrative directory
    • One stack per branch, reachable only from worktrees sitting on that branch

    The stash lives in refs/stash. Everything under refs/ is shared except refs/bisect, refs/worktree and refs/rewritten, so one stack serves every worktree of the repository.

  2. HEAD and the ____ are per-worktree, so two worktrees can sit on different commits with different staged content.

    • index
    • object database
    • config file

    The index records staged content and git keeps it per-worktree alongside HEAD, while the object database and the config file are shared by default.

  3. A branch already checked out in one worktree can be checked out again in a second worktree without using --force.

    • True
    • False

    git worktree add refuses by default when the requested branch is already checked out elsewhere; --force is required to override that safeguard.

Comments

No comments yet. Start the conversation.