claude --worktree fixture pollutes parent worktree's HEAD and resets git identity to Test <test@test.com>
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
The claude --worktree test fixture (which creates temporary worktrees under %TEMP%\worktree-test-* with branches named change/task-alpha, change/task-beta, change/my-task-01, change/reuse-task, change/cleanup-task) performs two unsafe operations against the parent worktree's checkout:
- Resets the parent worktree's git config
user.nameanduser.emailtoTest <test@test.com>— and this reset persists after the fixture exits, so subsequent legitimate commits inherit the bad identity unless caught by a pre-commit hook. - Makes empty commits authored as
Test <test@test.com>directly on top of the parent worktree's current branch HEAD.
Both happen without prompt or explicit user action — solely as a side effect of the fixture running. This violates the isolation guarantee the --worktree flag implies: a worktree's purpose is to give a session its own HEAD and working directory without touching the parent.
The most damaging consequence in our case: we believe uncommitted in-flight work in the parent worktree was destroyed during the fixture's run. The lost work does not appear in git stash list, git fsck --lost-found, or git reflog — so the destruction is opaque and unrecoverable.
What Should Happen?
- The test fixture should never write to the parent worktree's HEAD or branch.
- The test fixture should never modify the parent worktree's git config (
user.name,user.email, or anything else). - All fixture-created worktrees and branches should be cleaned up before exit, regardless of success or failure.
- If the fixture must use a
Testidentity, that should be scoped viaGIT_AUTHOR_NAME/GIT_AUTHOR_EMAILenv vars per-command, never written to persistent git config. - Uncommitted working-tree state in the parent worktree should be untouched, period.
Error Messages/Logs
After the fixture ran, the parent worktree's branch HEAD looked like this:
$ git log -10 --format='%h %an <%ae> %s' future-proofing
<sha-8> Test <test@test.com> init
<sha-7> Test <test@test.com> init
<sha-6> Test <test@test.com> init
<sha-5> Test <test@test.com> init
<sha-4> Test <test@test.com> init
<sha-3> Test <test@test.com> init
<sha-2> Test <test@test.com> init
<sha-1> Test <test@test.com> init
ac4a162e5 Jeff Hoyt <real-email> chore: CI retrigger
7719f19d0 Jeff Hoyt <real-email> fix(okr): chip commit for templates/CSV 500s
$ git config user.email
test@test.com # was jeffrey.r.hoyt@gmail.com before the fixture ran
$ git worktree list
[primary worktree paths...]
%TEMP%\worktree-test-<id1> change/task-alpha
%TEMP%\worktree-test-<id2> change/task-beta
%TEMP%\worktree-test-<id3> change/my-task-01
%TEMP%\worktree-test-<id4> change/reuse-task
%TEMP%\worktree-test-<id5> change/cleanup-task
# branches still in repo after fixture exit:
change/task-alpha
change/task-beta
change/my-task-01
change/reuse-task
change/cleanup-task
$ git stash list
(empty)
$ git fsck --lost-found 2>&1 | grep dangling
(only old/unrelated dangling commits — no recoverable working-tree state)
$ git reflog --date=iso | grep -E 'reset|checkout' | head -10
(no reset --hard or checkout entries pointing at the lost state)
Steps to Reproduce
- Clone any non-trivial repo (we used a monorepo with npm workspaces, ~381 commits on a long-lived feature branch).
- Check out a feature branch and make some uncommitted working-tree changes (representing in-flight work).
- From the parent worktree, run
claude --worktree <some-topic>and trigger whatever internal path causes the test fixture to execute (we did not invoke the fixture explicitly — it ran as part of an internal Claude Code session step; we are unsure of the exact trigger). - Wait for the fixture to complete and Claude Code to exit.
- From the parent worktree, observe:
git log -10 --format='%h %an <%ae> %s'→ 8 empty commits authoredTest <test@test.com>stacked on top of the legitimate HEAD.git config user.email→ reset totest@test.com.git worktree list→ 4–5%TEMP%\worktree-test-*paths still present.git branch | grep change/→ 5 leftoverchange/task-*branches.- Working-tree files that existed before the fixture ran are absent, with no trace in
git stash list,git reflog, orgit fsck --lost-found.
Note: We cannot pinpoint exactly which Claude Code action triggered the fixture, which is itself a problem — the destruction was opaque. If the team can share the conditions under which worktree-test-* temp worktrees get created, that would help users build defensive practice.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.140 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
Impact
Our parent worktree was on a long-lived feature branch (~381 commits, monorepo with npm workspaces). The fixture added 8 empty Test-authored commits to that branch's HEAD. The identity reset persisted after the fixture exited, so subsequent legitimate commits would have been authored Test <test@test.com> if not caught.
We have a husky pre-commit hook that validates Co-Authored-By trailers against a canonical model-name list (.husky/check-claude-coauthor-format). That hook is the only reason the bad identity didn't enter our committed audit-chain — without it, the contamination would have been silent and lasted across many commits.
Forensic notes
Recovery investigation after the incident:
- No stashes created during the fixture's run.
- No dangling commits in
git fsck --lost-found(only old/unrelated dangling commits from prior weeks). - No reflog entries pointing at the lost work.
- The 8 empty commits were stacked directly on the legitimate branch HEAD; removing them required
git reset --hard <pre-fixture-SHA>. - The temporary worktrees under
%TEMP%\worktree-test-*had to be torn down manually viagit worktree remove --force <path>. - The 5
change/task-*branches had to be deleted manually viagit branch -D <name>.
Why this is serious
The opacity of the destruction is the safety issue. With full forensic tooling (reflog, fsck, stash, all-branch log scan, sibling-clone scan), we could not recover. A user who didn't investigate would have lost work silently and might not have noticed the identity reset until much later — possibly after pushing commits under the wrong author.
Workaround for users while this is open
Users running Claude Code on long-lived feature branches with uncommitted work should:
- Commit or stash everything before invoking
claude --worktree. - Audit
git config user.emailafter every Claude Code session. - Audit
git log -5forTest-authored commits before pushing. - Run
git worktree listand look for stray%TEMP%\worktree-test-*entries. - Install a canonical-author pre-commit hook to catch the identity-leak case during commit.
Happy to provide more detail or session telemetry — please let us know what would help triage.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗