Labels: bug
Priority: blocker
Source: claude
Summary
The --merged pass of clean explicitly protects the default branch. The
--gone pass does not. If the default branch's upstream is reported [gone],
clean --gone --apply will remove its worktree and delete the branch.
Evidence
- Merged pass guards it —
git-trees:472: | grep -vx "$def"
- Gone pass has no equivalent —
git-trees:429-448
Reproduced with a file:// fixture where the upstream main was deleted so
local main reports [gone] after fetch --prune:
$ git for-each-ref --format='%(refname:short) %(upstream:short) %(upstream:track)' refs/heads
doomed origin/doomed [gone]
main origin/main [gone]
$ git trees clean --gone
== branches with gone upstream ==
doomed (/…/proj/doomed)
main (/…/proj/main)
(report only — pass --apply to execute)
With --apply, main gets git worktree remove followed by git branch -d.
Mitigations that limit but do not remove the impact: clean reports unless
--apply is passed, and git branch -d refuses unmerged branches. But when the
default branch is fully merged (the common case for a synced main), -d
succeeds and the worktree is already gone by then. Of everything found in this
review, this is the only issue with real blast radius.
Plausible ways to reach [gone] on a default branch: the remote's default was
renamed (master → main), the branch was deleted and recreated upstream, or
a fork's default differs from the local checkout.
Proposed fix
Skip $def in the gone loop, mirroring line 472. def is already in scope
(assigned at git-trees:426):
while read -r br; do
[ -z "$br" ] && continue
[ "$br" = "$def" ] && continue # never touch the default branch
...
Consider also skipping the branch checked out in the current worktree. The
--older-than pass already protects cwd (git-trees:486-490); the gone and
merged passes do not.
Test plan
Labels: bug
Priority: blocker
Source: claude
Summary
The
--mergedpass ofcleanexplicitly protects the default branch. The--gonepass does not. If the default branch's upstream is reported[gone],clean --gone --applywill remove its worktree and delete the branch.Evidence
git-trees:472:| grep -vx "$def"git-trees:429-448Reproduced with a
file://fixture where the upstreammainwas deleted solocal
mainreports[gone]afterfetch --prune:With
--apply,maingetsgit worktree removefollowed bygit branch -d.Mitigations that limit but do not remove the impact:
cleanreports unless--applyis passed, andgit branch -drefuses unmerged branches. But when thedefault branch is fully merged (the common case for a synced
main),-dsucceeds and the worktree is already gone by then. Of everything found in this
review, this is the only issue with real blast radius.
Plausible ways to reach
[gone]on a default branch: the remote's default wasrenamed (
master→main), the branch was deleted and recreated upstream, ora fork's default differs from the local checkout.
Proposed fix
Skip
$defin the gone loop, mirroring line 472.defis already in scope(assigned at
git-trees:426):Consider also skipping the branch checked out in the current worktree. The
--older-thanpass already protects cwd (git-trees:486-490); the gone andmerged passes do not.
Test plan
nor
--applytouches it--apply(no regression)--mergedbehavior unchanged