[BUG] Worktree's git index silently emptied to 0 entries mid-session (Windows, CLI 2.1.270) — no git commit involved; later commits then build empty trees that delete the whole repo

Status Closed — not planned
Reported on v2.1.270
Maintainer reply None cached
Activity 4 comments · opened Sep 14, 2026 · closed Sep 14, 2026

[BUG] Worktree's git index silently emptied to 0 entries mid-session (Windows, CLI 2.1.270) — no git commit involved; later commits then build empty trees that delete the whole repo

Summary

In a EnterWorktree-created worktree that had been healthy and committing normally
for hours, .git/worktrees/<name>/index was silently rewritten to a structurally
empty index
(65 bytes, entry count 0x00000000) with no git commit, git add,
git reset, or any other index-writing git command invoked by me
in the window.

The working tree was untouched — all 1202 files remained on disk with correct
content. Only the index was destroyed. Nothing surfaced an error; the worktree
looked usable, and git status simply reported all 918 tracked files as deleted.

Why this is severe, not cosmetic: while the index was in this state, two
subsequent ordinary git commit invocations faithfully committed the empty index,
producing commits whose tree was the empty tree — i.e. commits deleting the
entire repository (918 files, 202,019 deletions). Those commits reached no remote
only by luck: the worktree branch happened to have no upstream, so the post-commit
push failed for unrelated reasons. On a branch with an upstream, a
repository-deleting commit would have been pushed to the shared remote
, and no
existing pre-commit hook in our chain flagged it (all reported "Passed").

Environment

| | |
|---|---|
| Claude Code | 2.1.270 (CLI) |
| OS | Windows 11 Pro, 10.0.26200.9445 |
| git | 2.55.0.windows.2 |
| Shell | Git Bash (MSYS2), CLAUDE_CODE_SHELL=C:\Program Files\Git\bin\bash.exe |
| Worktree | created via EnterWorktree, under .claude/worktrees/ |
| Concurrency | 4 concurrent Claude Code sessions on this host, 3 in separate worktrees of the same repo |
| Submodules | none (git submodule status empty, no .gitmodules) |

Evidence: the corrupted index, byte for byte

.git/worktrees/session-name-collision-fix/index went from 113,824 bytes (927
entries, captured healthy at 18:14:26 UTC in an instrumented trace) to 65 bytes,
mtime 18:20:

$ xxd -l 32 .git/worktrees/session-name-collision-fix/index
00000000: 4449 5243 0000 0002 0000 0000 5452 4545  DIRC........TREE
00000010: 0000 0019 0030 2030 0a4b 825d c642 cb6e  .....0 0.K.].B.n
  • DIRC magic, version 2
  • entry count 0x00000000 — zero entries
  • TREE extension caching 4b825dc6... — git's canonical empty-tree SHA

This is a genuinely empty index written to disk, not a stale read or a display
artifact. It was independently re-verified byte-for-byte by a second session
inspecting the same file from the main checkout.

Working tree confirmed intact at the same moment:

$ find . -type f -not -path './.git/*' | wc -l
1202
$ git rev-parse HEAD
c0bc83b1db698a2f5a167ce05d1312aeddb7ea08     # unchanged, no new reflog entry

Reproduction / timeline

I cannot yet give deterministic repro steps — this is the honest state of it. What I
can give is a precise action timeline, where every step between the last
confirmed-healthy index and the corrupted one is accounted for:

  1. 18:14:26 — instrumented git commit run with a diagnostic pre-commit hook that

dumps state then intentionally exit 1s to abort before any commit object is
built. Index confirmed healthy: 113,824 bytes, 927 files. No commit created.

  1. Read two local log files (no git commands).
  2. git log --oneline -3, git status --short — read-only, state as expected.
  3. Reverted the diagnostic hook from .pre-commit-config.yaml via the Edit tool.

git diff .pre-commit-config.yaml → clean, no output. Index still healthy.

  1. Attempted a second Edit to .pre-commit-config.yaml. **This Edit was DENIED by

the auto-mode permission classifier ("Auto-Mode Bypass"). No file write
occurred** (confirmed by the immediately-following clean diff).

  1. git diff .pre-commit-config.yaml, then git status --short — both read-only.
  2. Step 6's git status is the first observation of corruption: 918 D, 23

??. Index mtime 18:20, placing the on-disk write between step 4 (verified
healthy) and step 6 (corrupted).

Stated explicitly as correlation, not proven causation: the denied Edit (step 5)
is the only non-read-only action in that window, but I have no direct evidence that
the denial/permission pathway performed the write. An unrelated background process
in the same window is equally consistent with the evidence. I'd rather report this
honestly than overclaim a cause.

Control: sibling worktrees were unaffected

Checked at the same moment, same host, same repo, same hook chain:

| index | size | state |
|---|---|---|
| worktrees/session-name-collision-fix | 65 bytes | corrupted |
| worktrees/claim-lock-race-test | 113,141 bytes | healthy |
| worktrees/cr-claim-hook-enforcement | 113,203 bytes | healthy |
| main checkout .git/index | 114,352 bytes | healthy |

So this is not "all worktrees on this host" — it hit exactly one, while two siblings
kept committing normally throughout.

What I ruled out

  • pre-commit / git commit internals. An instrumented trace (`GIT_TRACE=1

GIT_TRACE_SETUP=1) captured at 18:14:26 shows everything healthy at hook time:
GIT_INDEX_FILE correctly resolved to the worktree's own index, all
GIT_TRACE_SETUP paths correct (git_dir, git_common_dir, worktree, cwd),
and pre-commit's own bare
git write-tree returning a correct, fully-populated
tree (
dc2b2bf5...`, 23 top-level entries) — not the empty tree. The corruption
does not happen inside pre-commit's execution window. The empty-tree commits
were the victim of an already-empty index, not its cause.

  • #57048 (Mac "empty-index regression") — not this: Mac-only, occurs only at

worktree bootstrap ("does not occur during normal session operation"), and fails
loudly. Mine is Windows, mid-session on a worktree healthy for hours, and silent.

  • #92019 (checkout.workers=8 Windows regression) — not this: fails at worktree

creation, and that report states it is "not related to git index emptying."

  • Our own statusline. It shells out only to git rev-parse --git-common-dir, a

read-only query that takes no index lock — unlike the git status --porcelain
pattern #47721 identifies as the common offender.

Suspected area

#47721 documents that "Claude Code runs frequent background git commands for
context"
, and #11005 / #28546 / #57102 document that this same background
machinery already corrupts index state on Windows — leaving 0-byte
.git/worktrees/<name>/index.lock files, the same path family as my corrupted
index. Those reports describe locking failures; this is an actual emptying, which
I could not find reported anywhere.

A plausible (unproven) shape: a background git operation racing with, or interrupted
mid-write against, the worktree index, leaving a truncated/empty index rather than
failing cleanly. The concurrency here (4 sessions, 3 worktrees, one repo) would make
such a race far more likely to be hit.

Related but distinct

Searched open/closed issues before filing; found nothing matching this signature.
Nearest neighbours, all materially different:

  • #88862 — concurrent sessions sharing one git clone on Windows, with measured

collisions. Same environmental shape as mine (multi-session, one repo, Windows) but
reports collisions/contention, not an emptied index.

  • #77268 — worktree recycling destroying sibling worktrees. Destructive and

worktree-related, but it removes worktrees; mine leaves the worktree and working
tree fully intact and destroys only the index.

  • #11005 / #28546 / #57102 — 0-byte stale index.lock from background git

operations, including in .git/worktrees/<name>/. Same path family and, I suspect,
the same background machinery — but those are locking failures, not the index
itself being rewritten empty.

  • #57048 / #92019 — both fail at worktree creation, loudly. See "What I ruled

out" above.

Suggested mitigations

  1. **Use GIT_OPTIONAL_LOCKS=0 / --no-optional-locks for all internal read-only

git operations** (already requested in #47721) — read-only context-gathering
should never be able to write or lock the index.

  1. Write the index atomically (write temp + rename) so an interrupted background

operation can never leave a truncated/empty index in place.

  1. Sanity-check before committing an empty tree. A commit whose tree is

4b825dc6... while the working tree is non-empty is essentially never intended.
Even a warning would have prevented two repo-deleting commits here.

Impact

No data was lost in my case — files stayed on disk, and git reset HEAD (mixed)
fully rebuilds the index from HEAD. But the failure is silent, and the resulting
empty-tree commits are pushable. On a tracked branch this would have put a commit
deleting an entire repository onto a shared remote, with every pre-commit hook
reporting success.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗