← all writing

4 min read

Claude Code in a git worktree: what it starts without, what conflicts after

We ran claude --worktree and merged three agents' branches back. The worktree skips unpushed commits and .env, and 2 of 3 merges conflicted on a shared list.

You want two Claude Code sessions working at once without one overwriting the other's files. The usual answer is one flag:

claude --worktree feature-auth

A git worktree is a second working directory attached to the same repository. It has its own files and its own branch. It shares the history, the remotes and .git with your main checkout. Two sessions in two worktrees cannot edit each other's files. They can still collide later, when their branches come back to main.

We measured both ends: what a worktree that Claude Code creates holds when the session starts, and what happens when three worktree branches are merged back. The run used git 2.39.5 and Claude Code 2.1.286, in throwaway repositories. Claude Code ran with an empty config and printed /context, which makes no model call. The merge part is plain git. We ran it twice and got the same output. The script is in this site's repository.

What the new worktree has, and what it does not

Each test repository had one pushed commit, one commit not yet pushed, an uncommitted line in CLAUDE.md, an untracked notes.txt, and a gitignored .env and node_modules/. Then claude --worktree one:

In the main checkoutDefaultWith .worktreeincludeWith baseRef: "head"
The commit you have not pushedabsentabsentpresent
.env (gitignored)absentpresentabsent
node_modules/ (gitignored)absentabsentabsent
notes.txt (untracked)absentabsentabsent
Your uncommitted line in CLAUDE.mdabsentabsentabsent

By default the worktree starts at the last commit you pushed, not at the one you are looking at. Claude Code branches from the default branch on the remote. Work committed and not pushed is not there. In a repository with no remote it falls back to your latest local commit. Setting worktree.baseRef to "head" makes it start from your local HEAD.

The session reads the worktree's own CLAUDE.md. /context listed that file and not the one in the main checkout. A rule you added to CLAUDE.md and did not commit is not in force in the worktree.

.worktreeinclude copies what you list, if the file is gitignored. It does not install anything: node_modules/ was absent in every case. A worktree needs its own install.

Two smaller things:

  • A claude -p --worktree run leaves its worktree behind, locked. The docs say so. git worktree remove refuses it with exit 128 until you run git worktree unlock
  • A branch can be checked out in one worktree only. Git refuses a second worktree on worktree-one, and refuses to delete that branch while the worktree exists

What conflicts when three worktrees come back

Three worktrees cut from one commit, one per agent. Each agent edits its own source file, so the code never overlaps. Each one also records that its task is done. The branches are merged into main one after another. When a merge conflicts, it is resolved the way a careful person would, and the next merge goes ahead.

Where the agent writes "done"Merges that conflicted
A shared checklist, tasks on adjacent lines2 of 3
The same checklist, one unchanged line between tasks0 of 3
A shared log, each agent appends a line2 of 3
Each task's own file, status edited in place0 of 3
A new file per change, nothing edited0 of 3

The source files never conflicted. Every conflict was in the file that records status. The first merge never conflicts, because main has not moved yet. From the second merge on, git sees two edits that touch: on neighbouring lines, or both at the end of the same file. Git will not guess the order, so it stops.

The checklist result is narrower than it looks. With a single unchanged line between tasks, such as an owner: line, all three merges went through. What causes the conflict is two edits next to each other, not the shared file itself. A tidy checklist with one task per line puts every edit next to another one.

Moving status into one file per task removed every conflict here. That is enough for this pattern, where each agent owns its own task. It stops being enough when two agents touch the same task on two branches. Where a task tracker keeps its status measures that case.

A checklist before you start parallel sessions

  • Push first, or set worktree.baseRef to "head". Otherwise the agent starts from code you have already changed
  • Commit CLAUDE.md changes before you open the worktree. The session reads the worktree's copy
  • List .env and other local config in .worktreeinclude, and run the install in each worktree
  • Do not let agents write status on neighbouring lines of one file. Use a file per task, or one line of space between entries
  • After a -p run, unlock and remove the worktree yourself

What this does not cover

  • One machine: macOS, git 2.39.5, Claude Code 2.1.286. Worktree behaviour in Claude Code has changed between versions, and its docs note several of those changes
  • No model ran. Part A reads the worktree Claude Code creates and the context it loads. Part B stands in for agents with scripted edits, so it shows git's rules and not how often real agents trigger them
  • Interactive exits, which ask before removing a worktree, were not tested. Neither were subagents with isolation: worktree or the desktop app's parallel sessions
  • Three agents and one-line edits. More agents add more neighbours

The script and both runs are in this site's repository under docs/search-log/bench/. kadence records every change as a new file, the last row of the second table, so parallel worktrees have nothing to conflict over. Why task files conflict measures how often status files conflict in real repositories.

Where the numbers come from

  • Bench run 2026-10-12 with git 2.39.5 (Apple Git-154) and Claude Code 2.1.286, run twice with identical output apart from paths, pids and hashes. Script and full output in this site's repository: docs/search-log/bench/2026-10-12-claude-code-worktrees.sh and .txt
  • Bench, part A: `claude -p --worktree one "/context"`, run with an empty CLAUDE_CONFIG_DIR and no MCP servers, in throwaway repositories holding one pushed commit, one unpushed commit, an uncommitted edit to CLAUDE.md, an untracked notes.txt and a gitignored .env and node_modules/
  • Bench case A1, defaults: the worktree is at .claude/worktrees/one on branch worktree-one, at the pushed commit; the unpushed commit, .env, node_modules/dep.json and notes.txt are absent; its CLAUDE.md has 2 lines against 3 in the main checkout; /context lists the worktree's CLAUDE.md as the project memory file; the worktree is left locked
  • Bench case A2: with `.env` in .worktreeinclude, .env is present in the worktree and node_modules/dep.json, notes.txt and the unpushed commit are still absent
  • Bench case A3: with worktree.baseRef set to head, the worktree is at the unpushed commit; .env is absent
  • Bench case A4: in a repository with no remote, the worktree is at the latest local commit
  • Bench case A5: `git worktree remove` on the worktree a -p run created exits 128 with 'cannot remove a locked working tree'; after `git worktree unlock` it exits 0
  • Bench case A6: `git worktree add` of branch worktree-one while it is checked out in the first worktree exits 128 with 'is already checked out at'; `git branch -D worktree-one` exits 1 with 'Cannot delete branch'
  • Bench, part B: three worktrees from one commit, each agent edits its own source file and records its task as done; branches merged into main in the order 1, 2, 3; a conflict is resolved by taking main's side and replaying that agent's edit, then the next merge runs
  • Bench case B1, a checklist with tasks on adjacent lines: merge 1 fast-forwards, merges 2 and 3 exit 1 with CONFLICT (content) in TASKS.md
  • Bench case B2, the same checklist with one unchanged line between tasks: all 3 merges exit 0
  • Bench case B3, each agent appends a line to one shared LOG.md: merges 2 and 3 exit 1 with CONFLICT (content) in LOG.md
  • Bench cases B4 and B5, status in each task's own file, or a new file per change: all 3 merges exit 0 in both
  • Claude Code docs, 'Run parallel sessions with worktrees', code.claude.com/docs/en/worktrees, fetched 2026-10-12: worktrees are created under .claude/worktrees/<name>/ on a branch named worktree-<name>; baseRef 'fresh' (default) branches 'from the repository's default branch on the remote'; .worktreeinclude copies files that 'match a pattern and are also gitignored'; '-p' runs have no exit prompt, so 'Claude doesn't clean up their worktrees'
  • Google Trends, read 2026-10-12, worldwide, past 12 months: `claude code git worktree` is the second-largest related query for `git worktree`, and rising (docs/search-demand-trends-2026-10-12.md)

More writing

all writing →