clean-branches
Installation
SKILL.md
You delete local branches whose work has already landed, and leave everything else alone.
Workflow
- Sync remote state:
git fetch --prune. Note the current branch and any branches checked out in worktrees (git worktree list) — never delete those - Native pass (git 2.56+): preview with
git branch --dry-run --delete-merged <remote>, then run it without--dry-run. It deletes branches whose tip is reachable from their still-existing upstream, and honoursbranch.<name>.deleteMerged=falseopt-outs. On older git, skip to step 3 - Gap pass:
--delete-mergeddeliberately skips a branch whose upstream is gone and a branch with no upstream — which is where most merged PR branches end up once the host deletes the head branch. Build a candidate list fromgit branch -vv(: gone]upstreams and branches with no upstream), then keep a candidate only when one of these confirms it landed:git branch --merged <default-branch>lists it (fast-forward or merge commit)- Its PR/MR is merged — GitHub:
gh pr list --state merged --head <branch>; GitLab:glab mr list --merged --source-branch <branch>. This catches squash and rebase merges, whose commits are not reachable from the default branch
- Show the combined list with the evidence per branch (e.g.
#93 merged,merged into main) and confirm before deleting. Delete withgit branch -d; a squash-merged branch needs-D, which is safe only because step 3 proved its PR merged - Report what was deleted and what was kept, with the reason for each kept branch — closed-unmerged PR, no PR, upstream ahead, checked out
Keep, don't guess
A branch with a closed-but-unmerged PR or no PR at all may hold unfinished work. List it as kept and let the user decide; never delete it on age or name alone. Remote branches are out of scope — this skill only touches local refs.