Installs just this skill. Get the whole plugin for auto-invocation.
⚡ How it fires
How this skill gets triggered: by you, by Claude, or both.
Fires itselfClaude auto-loads it when your prompt matches the work.
You can call itInvoke it directly when you want it.
Slash command/git-cleanup
👁️ Context preview
The summary Claude sees to decide when to auto-load this skill.
Safely analyzes and cleans up local git branches and worktrees by categorizing them as merged, squash-merged, superseded, or active work.
📊 Stats
Stars6,228
Forks540
LanguagePython
LicenseCC-BY-SA-4.0
📦 Ships with trailofbits-skills
</> SKILL.md
git-cleanup.SKILL.md
---name: git-cleanup
description: "Safely analyzes and cleans up local git branches and worktrees by categorizing them as merged, squash-merged, superseded, or active work."
disable-model-invocation: true
allowed-tools: Bash Read Grep AskUserQuestion
---# Git Cleanup
Safely clean up accumulated git worktrees and local branches by categorizing them into: safely deletable (merged), potentially related (similar themes), and active work (keep).
## When to Use
- When the user has accumulated many local branches and worktrees
- When branches have been merged but not cleaned up locally
- When remote branches have been deleted but local tracking branches remain
## When NOT to Use
- Do not use for remote branch management (this is local cleanup only)
- Do not use for repository maintenance tasks like gc or prune
- Not designed for headless or non-interactive automation (requires user confirmations at two gates)
## Core Principle: SAFETY FIRST
**Never delete anything without explicit user confirmation.** This skill uses a gated workflow where users must approve each step before any destructive action.
## Critical Implementation Notes
### Squash-Merged Branches Require Force Delete
**IMPORTANT:** `git branch -d` will ALWAYS fail for squash-merged branches because git cannot detect that the work was incorporated. This is expected behavior, not an error.
Branches sharing a prefix (e.g., `feature/api`, `feature/api-v2`, `feature/api-refactor`) are almost certainly related iterations. Analyze them as a group:
1. Find the oldest and newest by commit date
2. Check if newer branches contain commits from older ones
3. Check which PRs merged work from each
4. Determine if older branches are superseded
Present related branches together with a clear recommendation, not scattered across categories.
### Thorough PR History Investigation
Don't rely on simple keyword matching. For `[gone]` branches:
```bash
# 1. Get the branch's commits that aren't in default branch
git log --oneline "$default_branch".."$branch"
# 2. Search default branch for PRs that incorporated this work
5. **Block dirty worktree removal** - Refuse without explicit data loss acknowledgment
6. **Group related branches** - Don't scatter them across categories
## Rationalizations to Reject
These are common shortcuts that lead to data loss. Reject them:
| Rationalization | Why It's Wrong |
|-----------------|----------------|
| "The branch is old, it's probably safe to delete" | Age doesn't indicate merge status. Old branches may contain unmerged work. |
| "I can recover from reflog if needed" | Reflog entries expire. Users often don't know how to use reflog. Don't rely on it as a safety net. |
| "It's just a local branch, nothing important" | Local branches may contain the only copy of work not pushed anywhere. |
| "The PR was merged, so the branch is safe" | Squash merges don't preserve branch history. Verify the *specific* commits were incorporated. |
| "I'll just delete all the `[gone]` branches" | `[gone]` only means the remote was deleted. The local branch may have unpushed commits. |
| "The user seems to want everything deleted" | Always present analysis first. Let the user choose what to delete. |
| "The branch has commits not in main, so it has unpushed work" | "Not in main" ≠ "not pushed". A branch can be synced with its remote but not merged to main. Always check `git log origin/<branch>..<branch>`. |