diff --git a/bin/branch_sweep.sh b/bin/branch_sweep.sh index dcd9aed..a39d955 100755 --- a/bin/branch_sweep.sh +++ b/bin/branch_sweep.sh @@ -182,7 +182,13 @@ while read -r b; do fi ;; *) keep+=("$b $word") ;; # UNKNOWN is never safe esac -done < <(g for-each-ref --format='%(refname:short)' refs/remotes/origin | sed 's|^origin/||') + # Full refnames, not `:short`. Git abbreviates refs/remotes/origin/HEAD to + # bare `origin`, which survives an `origin/` strip and a `!= HEAD` guard, + # then reads as a branch named "origin" with verdict UNKNOWN — a symbolic + # ref rendered as an unmerged branch. Harmless (UNKNOWN is never deletable) + # but it is a null result dressed as a finding, which is the D1 mistake. +done < <(g for-each-ref --format='%(refname)' refs/remotes/origin \ + | sed 's|^refs/remotes/origin/||') echo echo "Branch sweep — $OWNER/$NAME (mode: $MODE, base: $BASE)" diff --git a/tests/test_branch_sweep.py b/tests/test_branch_sweep.py index 0f2a52c..28922c8 100644 --- a/tests/test_branch_sweep.py +++ b/tests/test_branch_sweep.py @@ -130,6 +130,22 @@ def test_audit_classifies_every_branch(world): assert "archive/condemned/something\tgut-transit-ref" in out +def test_symbolic_head_is_not_reported_as_a_branch(world): + """refs/remotes/origin/HEAD must not surface as a branch called "origin". + + Git abbreviates that ref to bare `origin`, which slips past an `origin/` + strip and a `!= HEAD` guard and then lands in KEEP with verdict UNKNOWN — + a symbolic ref rendered as unmerged work. It was never deletable, so the + cost is a wrong count and a reader misled about what is outstanding. + """ + clone, _, bin_dir = world + out = _sweep(clone, bin_dir).stdout + assert "origin\tUNKNOWN" not in out + assert "\n origin\t" not in out + # the real answer for this fixture, with nothing invented alongside it + assert "1 unmerged" in out + + def test_audit_never_deletes(world): clone, origin, bin_dir = world before = _git(origin, "for-each-ref", "--format=%(refname)", "refs/heads")