Worktree isolation silently lost when repo has core.worktree in shared config + extensions.worktreeConfig
Summary
When a repository has core.worktree in its shared config and extensions.worktreeConfig = true, every linked worktree resolves to the main checkout instead of its own directory. A Claude Code session created in such a worktree believes it is isolated but is actually reading and writing the main checkout.
I hit this in practice: two sessions ended up operating on the same working directory simultaneously. Both committed to main in that directory, and one ran git reset while the other was mid-task. Nothing was lost, but only because the concurrent work happened to be committed already.
Why this combination occurs
Neither ingredient is a mistake on its own:
core.worktreein the shared config is normal for a repo with a separated gitdir (git init --separate-git-dir,git clone --separate-git-dir, or a clone repaired after object-store corruption). The.gitfile points elsewhere andcore.worktreenames the checkout.extensions.worktreeConfig = trueis enabled by tooling that wants per-worktree config.
Together they break isolation, because enabling the extension removes git's exception that confines core.worktree to the main worktree. git-worktree(1) documents this explicitly:
Note that in this file, the exception forcore.bareandcore.worktreeis gone. If they exist in$GIT_DIR/config, you must move them to theconfig.worktreeof the main worktree. -core.worktreeshould never be shared.
Reproduction
git 2.50.1 (Apple Git-155), macOS. Runs verbatim:
R=/tmp/wt-repro; mkdir -p $R/checkout $R/gitdirs; cd $R
git init -q --separate-git-dir=$R/gitdirs/repo.git checkout
cd checkout && git commit -q --allow-empty -m init
# normal for a separated gitdir:
git config --file $R/gitdirs/repo.git/config core.worktree $R/checkout
# the trigger:
git config --file $R/gitdirs/repo.git/config extensions.worktreeConfig true
git worktree add -q $R/wt -b wt
(cd $R/wt && git rev-parse --show-toplevel)
Actual: /private/tmp/wt-repro/checkout — the main checkout
Expected: /private/tmp/wt-repro/wt — the worktree itself
(/private/tmp is just macOS resolving /tmp; the point is checkout vs wt.)
Control — same repo, extension removed, worktree resolves correctly:
git config --file $R/gitdirs/repo.git/config --unset extensions.worktreeConfig
git worktree add -q $R/wt2 -b wt2
(cd $R/wt2 && git rev-parse --show-toplevel) # -> /private/tmp/wt-repro/wt2
Why it is easy to miss
The failure is silent. git worktree list shows the worktree at its correct path, the directory exists and is populated with checked-out files, and file-based tooling reading paths under the worktree sees plausible content. Only git rev-parse --show-toplevel from inside the worktree reveals that git operations target the main checkout.
Suggested fix
When creating a worktree, detect core.worktree present in the shared config while extensions.worktreeConfig is enabled, and either:
- move
core.worktreeinto the main worktree'sconfig.worktree(the remedygit-worktree(1)prescribes), or - refuse and warn loudly.
The documented remedy is two commands and is safe if ordered so the main worktree is never left without a pointer:
git config --worktree core.worktree /path/to/checkout # write the new location FIRST
git config --file <gitdir>/config --unset core.worktree # then remove the shared one
I applied exactly this to the affected repo and verified: the main checkout still resolves, new worktrees isolate correctly, pre-existing broken worktrees recover without recreation, and a file written in a worktree no longer appears in the main checkout.
A session that believes it is isolated but silently shares the user's main checkout is a data-loss hazard — a git reset --hard or branch checkout from one session can destroy another's uncommitted work.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗