Skip to content

fix(remove): name trash staging dirs by worktree registration - #4361

Merged
max-sixty merged 2 commits into
mainfrom
prune-final-wait
Oct 4, 2026
Merged

max-sixty merged 2 commits into
mainfrom
prune-final-wait

Conversation

@max-sixty

Copy link
Copy Markdown
Owner

A wt step prune printed every per-item line quickly, then sat for about 10s before its Pruned … summary. Two of the pruned worktrees were Codex worktrees, which all live at ~/.codex/worktrees/<id>/<repo> and so share the basename Mackup. Removal staged each worktree at .git/wt/trash/<basename>-<epoch-second>, so the two concurrent removals picked the same path in the same second. The second rename failed onto the existing directory, and prune's synchronous fallback ran git worktree remove in the foreground; the summary waited for that recursive delete. The run's logs show it: nine worktrees got a detached internal/remove.log, the tenth (codex/promptfoo-followups) none.

Staging names now use the worktree's registration id under <common>/worktrees/, which git keeps unique among a repository's worktrees. ensure_holds_this_worktree already resolves and verifies that git dir, so it now returns it. A failed rename also logs its error at debug level instead of discarding it.

The collision predates #4337: v0.80.0 shows the same 3.4s stall on a two-worktree repro (50k ignored files each), which drops to 0.17s with this change. The new prune test relies on the suite's pinned epoch_now(), which makes the collision deterministic under the old naming; it fails without the fix.

This was written by Claude Code on behalf of max-sixty

Removal renamed each worktree to `wt/trash/<basename>-<epoch-second>`.
Worktrees that share a basename — Codex places every worktree at
`~/.codex/worktrees/<id>/<repo>` — collided when `wt step prune` removed
two of them in the same second: the second rename failed onto an
existing directory, and prune's `SynchronousForNonCurrent` fallback ran
`git worktree remove` in the foreground. The summary line then waited
for that recursive delete (about 10s observed on a real prune), with no
signal of why.

The staging name now uses the worktree's registration id under
`<common>/worktrees/`, which git keeps unique among a repository's
worktrees. `ensure_holds_this_worktree` already resolves and verifies
that git dir, so it now returns it. A failed rename logs its error at
debug level instead of discarding it.

The new prune test relies on the suite's pinned `epoch_now()`, which
makes the collision deterministic under the old naming.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Log the rename error through inspect_err so the existing branch keeps its
shape; the match form re-indented an untestable debug line into the diff.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@max-sixty
max-sixty merged commit f844d09 into main Oct 4, 2026
38 checks passed
@max-sixty
max-sixty deleted the prune-final-wait branch October 4, 2026 02:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants