deadbranch
4 min read
deadbranch finds and removes stale git branches. Written in Rust, MIT-licensed, and safe by default — it only deletes merged branches, never touches protected ones, always confirms, and writes a backup of every SHA it removes.
Its headline feature is a full-screen TUI for picking branches to delete, with Vim navigation, fuzzy search, visual range selection, and a Thanos-style particle dissolution animation when branches are deleted.
sh
brew install armgabrielyan/tap/deadbranch
cargo install deadbranch
npm install -g deadbranch # or: npx deadbranch list
curl -sSf https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/install.sh | sh
Pre-built binaries for Linux (glibc/musl/ARM64), macOS (Intel/Apple Silicon) and Windows are on the releases page. Completions: deadbranch completions bash|zsh|fish. The only requirements are git in PATH and being inside a repo.
Commands
sh
deadbranch list # stale branches, default older than 30 days
deadbranch list --days 60
deadbranch clean --dry-run # preview, prints the exact git commands
deadbranch clean # delete merged stale branches, with confirmation
deadbranch clean -i # interactive TUI
deadbranch stats # branch health overview
list and clean share -d/--days <N>, --local, --remote and --merged. clean adds --force (unmerged branches — dangerous), --dry-run and -y/--yes for scripts.
stats prints a repository table (all / merged / unmerged / stale / safe-to-delete, split local vs remote) and an age distribution. It covers every visible branch regardless of age, so --days only moves the stale threshold rather than hiding anything.
Interactive TUI
sh
deadbranch clean -i [--days N] [--merged] [--local] [--remote] [--force]
The flags pre-seed the filters rather than fixing them; --force unlocks unmerged branches for selection. -i cannot be combined with -y or --dry-run.
| Key | Action |
|---|---|
j/↓, k/↑ | Navigate |
gg, G | Top / bottom |
Ctrl+d / Ctrl+u | Half-page down / up |
Ctrl+f / Ctrl+b | Full page down / up |
Space | Toggle selection |
V | Visual range select |
a / A | Select all merged / all (needs --force) |
n / i | Deselect all / invert |
d | Delete selected |
/ | Fuzzy search by branch name |
s / S | Cycle sort (age → branch → status → type → date → author) / reverse |
m, l, R | Toggle merged-only, local-only, remote-only filters |
? | Help |
q/Esc | Quit |
What makes it nice to use:
• Vim-native — gg/G, half- and full-page scrolling, relative line numbers, and V to anchor a visual range and extend it with the cursor.
• Fuzzy search highlights the matched characters in each branch name.
• Age colouring — green under 30 days, yellow 31–90, red over 90 — so the branches worth killing stand out without reading the numbers.
• Sections — merged and unmerged branches are grouped under headers rather than interleaved.
• Snap dissolution — deletion plays a particle animation with a live progress gauge while the branches are actually deleted in the background.
• Smart confirmation — a simple prompt for safe deletions, but risky ones (unmerged, remote) require typing yes.
Mouse scrolling works too.
Safety
| Guard | Behaviour |
|---|---|
| Merged-only default | unmerged branches need --force |
| Protected branches | main, master, develop, staging, production are never deleted |
| Current branch | never deleted |
| WIP detection | wip/*, draft/*, */wip, */draft excluded by default |
| Confirmation | always prompts; remote deletions prompt again |
| Backups | the SHA of every deleted branch is saved |
| Dry run | --dry-run prints the git branch -d … commands it would run |
Backups & restore
Every clean writes ~/.deadbranch/backups/<repo>/backup-<timestamp>.txt, containing ready-to-run restore commands:
sh
git branch feature/old-api abc1234def5678
Or manage them through the CLI:
sh
deadbranch backup list [--current|--repo <name>]
deadbranch backup restore feature/old-api # from the most recent backup
deadbranch backup restore feature/old-api --from backup-20250201-143022.txt
deadbranch backup restore feature/old-api --as feature/recovered
deadbranch backup stats # storage usage per repo
deadbranch backup clean --current --keep 3 # prune, keeps 10 by default
Configuration
~/.deadbranch/config.toml, edited directly or via deadbranch config show|set|edit|reset.
toml
[general]
default_days = 30
[branches]
protected = ["main", "master", "develop", "staging", "production"]
exclude_patterns = ["wip/*", "draft/*", "*/wip", "*/draft"]
| Key | Description |
|---|---|
days | Default age threshold |
default-branch | Branch used for merge detection (auto-detected if unset) |
protected-branches | Never deleted |
exclude-patterns | Glob patterns to skip |
Exclude patterns are glob-style: wip/* matches wip/experiment, */draft matches feature/draft, feature/*/temp matches feature/foo/temp, and *test* matches anything containing test.
Narrower than the general-purpose git TUIs — see lazygit for full git operations and serie for the commit graph.
Roadmap items not yet shipped: --only-mine, GitHub/GitLab PR detection, JSON/CSV output, and per-repo configuration.