Nothing is ever deleted automatically. prune always asks first
(grouped by risk), and only branches the analyzer can prove dead are
offered: ancestor-merged, or content-merged (the branch's final tree
exists in a base branch's history — the squash/rebase fingerprint).
Unmerged work is only ever flagged as stale, never deleted, unless
you opt in with an explicit force rule (rules: [{ match, mode: "any" }])
— and even then it's local-only and still confirmed. Before any deletion
that loses unique commits, the refs are written into a git bundle;
git-cleanup backup restore (or one git fetch) brings them
back.
git branch --merged miss so many branches?Squash merges, rebase merges, and cherry-picks rewrite the commits, so the branch's SHAs vanish from the base's history. Ancestry checks can't see them. git-cleanup fingerprints the branch's final tree — if that tree appears in a base's history, the branch's content is already in the base, even though its commits aren't.
It's deliberately conservative. A branch is only content-flagged when its tip tree exists in a base's history and differs from the tree at the branch's own starting point — so net-empty branches (fully reverted work, no-op commits) whose tree coincidentally matches an old base tree are never called merged. The golden-history test suite pins rebase, cherry-pick, octopus, and fast-forward scenarios, and the seeded differential fuzz compares verdicts against each branch's true fate on thousands of random histories.
git-cleanup doctor
One diagnostic report: git present? gh present? per-forge tokens set?
remote detected? config loading? Each check comes with a fix hint. Missing
tokens never break scans — the tool degrades to pure-Git detection.
prune --remote refuse to delete my version branch?
Remote deletion is a separate opt-in step, and version-shaped names
(v1, 5.x, release/*…) are protected by default.
Maintenance branches are often merged by ancestry yet still owned
upstream; protect them with a two-line glob if you point the tool at a
shared remote.
Content detection runs against the base branches the repo actually uses
(from the remote's HEAD — main, master, develop,
dev). If the work landed on a different branch, add it to the base
detection or check with scan --verbose to see the reason per branch.
A branch is also kept while it has an open PR or is below the age
threshold.
If you're seeing this from a shell hook, the snippet may set a different
cwd or run before config is readable — run git-cleanup doctor and
the exact failing command by hand. Note --force is an alias for
--yes, and GIT_CLEANUP_YES=1 works too.
Merge detection is fully offline — it only reads the local object
database. Network is used only for PR enrichment and
report-issue, and only when a forge remote and token are available.
Yes — scan --check as a gate, sweep --yes --json in cron,
and the scan-report GitHub Action for scheduled reports. The repo
also ships a structurally-mirrored GitLab CI template. See
Automation.