Repository navigation
fix(worktree): remove finished checkouts without --force - #632
Merged
Merged
Conversation
Quest-Claim: a4dc2fb0b35a7163
…out --force Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
marked this pull request as ready for review
November 11, 2026 10:53
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 20 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finishes
quest/m0/worktree-ignored-scratch.md.Problem
A /quest-spawn run in moq-dev/moq.pro left 40 finished agent checkouts, and
quest worktree removerefused 38, for two reasons:removerefused any ignored file outside the scratchderivedDefaultsfixed at creation (the main checkout's top-level ignored entries). Sonode_modules,target,.svelte-kit, gate stamps and.tmp/all blocked removal.6bcb980f0is not the PR's merged head.quest landmerged the tested base into it (ba767b4ab, "quest land: merge tested base", first parent6bcb980f0) before squashing.mergedHeadaccepted onlyheadRefOid === commit, so it returned false even though GitHub'sassociatedPullRequestslisted #3771 as merged.Changes
removenow treats every ignored path, in the checkout and in its submodules, as scratch. It no longer prunes the declared scratch first or refuses leftover ignored files. Instead, before deleting anything, it refuses any ignored path that holds a nested repository or worktree (any.gitentry, or a bare repository'sHEAD,objects/andrefs/, below it, without following symlinks). It still refuses modified or untracked files, unreached commits, and the existing submodule cases.git worktree removedeletes the ignored files.mergedHeadis renamedmergedHolds. A merged PR counts when its head is the commit or descends from it. For each merged associated PR whose head differs, it callsGET /compare/{head}...{commit}and acceptsahead_by == 0. Comparing in that direction sends back no commits or files. A force-pushed PR whose head no longer reaches the commit, or a compare that 404s, still counts as no evidence.release(prune) now refuses declared scratch that holds a nested repository, not just a linked worktree, so the same rule holds on both paths. This replaceslinkedWorktreewith the existingrepositoryWithin.quest worktree remove --helpdescribe the new behavior. The quest file is deleted.Choices made without asking
mergedmissed these heads and to fix it. Accepting any merged PR head that descends from the commit is the general fix: it also covers a reviewer or the land fixer pushing on top. It is safe because GitHub keepsrefs/pull/N/headpermanently, so the forge still holds the commit. I rejected special-casing aquest landmerge commit's first parent, since that would tie the core rule to one landing tool.release(including the automatic release when a supervised run ends) deleted a clone under declared scratch, because it checked only for linked worktrees. CONTEXT tells agents to put temporary clones in.tmp/, and.tmpis declared scratch wherever the main checkout ignores it, so this was the same work-loss risk the quest names. A release that hits one now fails and leaves the checkout reserved.removefinds nested checkouts by their.gitentry alone. A registered linked worktree whose.gitfile is gone is already prunable..gitdirectory inside dependency output, such as a package sourced from a repository, now blocks removal. That errs toward keeping data.Public API
None.
quest worktree removerefuses less andquest worktree releaserefuses more; there are no flag, schema or wire changes.Validation
quest gate just checkandquest gate just testpass (34 files, 1222 tests, 5 skipped).quest check --base origin/mainpasses..worktrees/both being refused at remove; a bare repository under ignored scratch; a nested repository under a submodule's ignored path being refused while the submodule's ignored.envis removed; release refusing a temporary clone in scratch; andmergedHoldsaccepting a land-merged head through compare while rejecting a head that no longer reaches the commit (ahead_by1) or a compare that 404s.Caused by
Follow-ups
None recommended.
Models
Size: M
Implementer: Claude Code, claude-opus-5-5, medium effort
Review
Rewritten by
quest reviewafter each round.01a1ca37-9764-7030-a98f-047a39a5e1d9, round 2The bare-repository guard fixes the reported data-loss case, and the incremental changes introduce no new findings.src/cli/worktree.ts:791:Detect bare repositories before deleting ignored paths;repositoryWithin now detects directories containing HEAD, objects/, and refs/, protecting bare repositories through its existing callers, and the added remove test checks refusal for cache/mirror.git.(Written by Claude Opus 5.5 with medium)