deadbranch

March 14, 2026 Β· View on GitHub

crates.io crates.io downloads npm version npm downloads CI License: MIT

Clean up stale git branches safely.

Table of Contents

deadbranch helps you identify and remove old, unused git branches that clutter your repository. It's designed to be safe by default β€” protecting important branches and requiring explicit confirmation before any deletion.

Demo

deadbranch snap

✨ Features

  • πŸ” List stale branches β€” Find branches older than N days (default: 30)
  • πŸ”’ Safe deletion β€” Only deletes merged branches by default
  • πŸ›‘οΈ Protected branches β€” Never touches main, master, develop, staging, or production
  • 🚧 WIP detection β€” Automatically excludes wip/* and draft/* branches
  • πŸ’Ύ Backup creation β€” Saves deleted branch SHAs for easy restoration
  • πŸ‘οΈ Dry-run mode β€” Preview what would be deleted without making changes
  • 🌐 Local & remote β€” Works with both local and remote branches
  • πŸ–₯️ Interactive TUI mode β€” Full-screen TUI with Vim navigation, fuzzy search, visual range selection, and 6-column sorting

πŸ“¦ Installation

⚑ Quick Install (macOS/Linux)

curl -sSf https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/install.sh | sh

🍺 Homebrew (macOS/Linux)

brew install armgabrielyan/tap/deadbranch

πŸ“¦ npm/npx

# Install globally
npm install -g deadbranch

# Or run directly
npx deadbranch list

πŸ¦€ Cargo (from source)

cargo install deadbranch

⬇️ Manual Download

Download pre-built binaries from the GitHub Releases page.

PlatformArchitectureDownload
Linuxx86_64 (glibc)deadbranch-VERSION-x86_64-unknown-linux-gnu.tar.gz
Linuxx86_64 (musl/static)deadbranch-VERSION-x86_64-unknown-linux-musl.tar.gz
LinuxARM64deadbranch-VERSION-aarch64-unknown-linux-gnu.tar.gz
macOSInteldeadbranch-VERSION-x86_64-apple-darwin.tar.gz
macOSApple Silicondeadbranch-VERSION-aarch64-apple-darwin.tar.gz
Windowsx86_64deadbranch-VERSION-x86_64-pc-windows-msvc.zip

πŸ”¨ Build from Source

git clone https://github.com/armgabrielyan/deadbranch
cd deadbranch
cargo build --release
# Binary will be at target/release/deadbranch

🐚 Shell Completions

deadbranch can generate tab-completion scripts for bash, zsh, and fish.

Bash

mkdir -p ~/.local/share/bash-completion/completions
deadbranch completions bash > ~/.local/share/bash-completion/completions/deadbranch

Requires bash-completion 2.x to be active. On macOS without Homebrew's bash, source the file manually in ~/.bash_profile:

source ~/.local/share/bash-completion/completions/deadbranch

Zsh

mkdir -p ~/.zfunc
deadbranch completions zsh > ~/.zfunc/_deadbranch

Then add the following to your ~/.zshrc before the compinit call (or add it if you don't have one):

fpath=(~/.zfunc $fpath)
autoload -Uz compinit && compinit

Reload your shell or run exec zsh to activate.

Fish

mkdir -p ~/.config/fish/completions
deadbranch completions fish > ~/.config/fish/completions/deadbranch.fish

Fish auto-loads completions from this directory β€” no extra configuration needed.

πŸš€ Quick Start

# List all stale branches (older than 30 days)
deadbranch list

# List branches older than 60 days
deadbranch list --days 60

# Preview what would be deleted
deadbranch clean --dry-run

# Delete merged stale branches (with confirmation)
deadbranch clean

# Delete only local branches
deadbranch clean --local

# Show branch health overview
deadbranch stats

πŸ› οΈ Usage

πŸ“‹ List Stale Branches

deadbranch list

deadbranch list [OPTIONS]
OptionDescription
-d, --days <N>Only show branches older than N days (default: 30)
--localOnly show local branches
--remoteOnly show remote branches
--mergedOnly show merged branches

Example output:

β„Ή Using 'main' as the default branch for merge detection

Local Branches:
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ #  β”‚ Branch               β”‚ Age     β”‚ Status β”‚ Type  β”‚ Last Commit  β”‚ Author       β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1  β”‚ feature/old-api      β”‚ 154d    β”‚ merged β”‚ local β”‚ 2024-09-01   β”‚ Jane Doe     β”‚
β”‚ 2  β”‚ bugfix/header-issue  β”‚ 89d     β”‚ merged β”‚ local β”‚ 2024-11-03   β”‚ John Smith   β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Remote Branches:
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ #  β”‚ Branch                          β”‚ Age     β”‚ Status β”‚ Type   β”‚ Last Commit  β”‚ Author       β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1  β”‚ origin/feature/deprecated       β”‚ 203d    β”‚ merged β”‚ remote β”‚ 2024-07-15   β”‚ Jane Doe     β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ—‘οΈ Delete Stale Branches

deadbranch clean

deadbranch clean [OPTIONS]
OptionDescription
-d, --days <N>Only delete branches older than N days (default: 30)
--mergedOnly delete merged branches (this is the default)
--forceForce delete unmerged branches (dangerous!)
--dry-runShow what would be deleted without doing it
--localOnly delete local branches
--remoteOnly delete remote branches
-y, --yesSkip confirmation prompts (useful for scripts)

Safety features:

  • Only deletes merged branches by default
  • Requires --force to delete unmerged branches
  • Shows confirmation prompt before deletion
  • Extra confirmation for remote branches
  • Creates backup file with branch SHAs

Example:

$ deadbranch clean

Local Branches to Delete:
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ #  β”‚ Branch               β”‚ Age     β”‚ Status β”‚ Type  β”‚ Last Commit  β”‚ Author       β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1  β”‚ feature/old-api      β”‚ 154d    β”‚ merged β”‚ local β”‚ 2024-09-01   β”‚ Jane Doe     β”‚
β”‚ 2  β”‚ bugfix/header-issue  β”‚ 89d     β”‚ merged β”‚ local β”‚ 2024-11-03   β”‚ John Smith   β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Delete 2 local branches? [y/N] y

Deleting local branches...
  βœ“ feature/old-api
  βœ“ bugfix/header-issue

βœ“ Deleted 2 local branches
  β†ͺ Backup: ~/.deadbranch/backups/my-repo/backup-20250201-143022.txt

πŸ–₯️ Interactive Mode

deadbranch interactive

Open a full-screen TUI for browsing, filtering, and selecting branches to delete:

deadbranch clean -i [OPTIONS]
OptionDescription
-i, --interactiveOpen interactive TUI for branch selection
-d, --days <N>Pre-filter to branches older than N days
--mergedStart with merged-only filter active
--forceUnlock unmerged branches for selection
--localStart with local-only filter active
--remoteStart with remote-only filter active

Key bindings:

KeyAction
j/↓, k/↑Navigate down/up
gg, GJump to top/bottom
Ctrl+d/Ctrl+uHalf-page down/up
Ctrl+f/Ctrl+bFull-page down/up
Mouse scrollScroll up/down
SpaceToggle selection
VVisual range select (Vim-style)
aSelect all merged
ASelect all (requires --force)
nDeselect all
iInvert selection
dDelete selected
/Fuzzy search by branch name
sCycle sort (age β†’ branch β†’ status β†’ type β†’ date β†’ author)
SReverse sort direction
mToggle merged-only filter
lToggle local-only filter
RToggle remote-only filter
?Help
q/EscQuit

TUI features:

  • Vim-style navigation β€” gg/G jump, Ctrl+d/Ctrl+u page scrolling, relative line numbers
  • Visual range selection β€” Press V to anchor, move cursor to extend range, Space to toggle
  • Fuzzy search β€” / opens search with matched characters highlighted
  • Age coloring β€” Green (<30d), yellow (31–90d), red (>90d)
  • Sections β€” Merged and unmerged branches are visually grouped with section headers
  • Snap dissolution β€” Thanos-style particle animation on deletion with a live progress gauge; branches are deleted in the background while the animation plays
  • Smart confirmation β€” Simple confirm for safe deletions, typed yes for risky ones (unmerged/remote)

Note: -i cannot be combined with -y (skip confirmation) or --dry-run.

πŸ” Dry Run Mode

Preview deletions without making any changes:

$ deadbranch clean --dry-run

Local Branches to Delete:
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ #  β”‚ Branch               β”‚ Age     β”‚ Status β”‚ Type  β”‚ Last Commit  β”‚ Author       β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1  β”‚ feature/old-api      β”‚ 154d    β”‚ merged β”‚ local β”‚ 2024-09-01   β”‚ Jane Doe     β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

[DRY RUN] Commands that would be executed:
  git branch -d feature/old-api

No branches were actually deleted.

βš™οΈ Configuration

deadbranch config

deadbranch stores its configuration in ~/.deadbranch/config.toml.

# Show current configuration
deadbranch config show

# Set default age threshold
deadbranch config set days 45

# Set default branch for merge detection
deadbranch config set default-branch main

# Set protected branches
deadbranch config set protected-branches main master develop

# Set exclude patterns
deadbranch config set exclude-patterns "wip/*" "draft/*" "temp/*"

# Open config in your editor
deadbranch config edit

# Reset to defaults
deadbranch config reset

Default configuration:

[general]
default_days = 30

[branches]
protected = ["main", "master", "develop", "staging", "production"]
exclude_patterns = ["wip/*", "draft/*", "*/wip", "*/draft"]

Config keys

KeyAliasesDescription
daysdefault-days, general.default-daysDefault age threshold in days
default-branchbranches.default-branchBranch used for merge detection (auto-detected if unset)
protected-branchesbranches.protectedBranches that are never deleted
exclude-patternsbranches.exclude-patternsGlob patterns for branches to skip

πŸ’Ύ Backup Management

deadbranch backup

Every deadbranch clean run automatically creates a backup. Use deadbranch backup to manage those backups.

List backups

# Show a summary of all repositories with backups
deadbranch backup list

# Show backups for the current repository
deadbranch backup list --current

# Show backups for a specific repository
deadbranch backup list --repo my-repo

Restore a deleted branch

# Restore from the most recent backup
deadbranch backup restore feature/old-api

# Restore from a specific backup file
deadbranch backup restore feature/old-api --from backup-20250201-143022.txt

# Restore with a different name
deadbranch backup restore feature/old-api --as feature/recovered

# Overwrite an existing branch
deadbranch backup restore feature/old-api --force

Backup statistics

# Show storage usage per repository and overall
deadbranch backup stats

Clean up old backups

# Keep the 10 most recent backups for the current repo (default)
deadbranch backup clean --current

# Keep only the 3 most recent backups
deadbranch backup clean --current --keep 3

# Preview what would be removed
deadbranch backup clean --current --dry-run

# Skip confirmation prompt
deadbranch backup clean --current --yes

# Clean backups for a specific repository by name
deadbranch backup clean --repo my-repo

πŸ“Š Branch Statistics

deadbranch stats

Get a health overview of all branches in your repository:

deadbranch stats [OPTIONS]
OptionDescription
-d, --days <N>Age threshold for "stale" classification (default: from config or 30)

Example output:

β„Ή Using 'main' as the default branch for merge detection

Repository Statistics:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Category       β”‚ Total β”‚ Local β”‚ Remote β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ All branches   β”‚ 12    β”‚ 7     β”‚ 5      β”‚
β”‚ Merged         β”‚ 8     β”‚ 5     β”‚ 3      β”‚
β”‚ Unmerged       β”‚ 4     β”‚ 2     β”‚ 2      β”‚
β”‚ Stale (>30d)   β”‚ 6     β”‚ 4     β”‚ 2      β”‚
β”‚ Safe to delete β”‚ 5     β”‚ 3     β”‚ 2      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Age Distribution:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Age Range   β”‚ Count β”‚ Status β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ < 7 days    β”‚ 2     β”‚ fresh  β”‚
β”‚ 7–30 days   β”‚ 4     β”‚ fresh  β”‚
β”‚ 30–90 days  β”‚ 3     β”‚ stale  β”‚
β”‚ > 90 days   β”‚ 3     β”‚ stale  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ’‘ Run 'deadbranch clean' to remove 5 safe-to-delete branches

Stats cover all visible branches (respecting protected and exclude patterns) regardless of age, so --days only shifts the stale/safe-to-delete threshold β€” it doesn't hide branches.

πŸ›‘οΈ Safety Features

deadbranch is designed to prevent accidental data loss:

FeatureDescription
Merged-only defaultOnly deletes branches already merged to main/master
Protected branchesNever deletes main, master, develop, staging, production
Current branchNever deletes the branch you're currently on
WIP detectionExcludes branches matching wip/*, draft/*, etc.
Confirmation promptsAlways asks before deleting
Remote warningExtra confirmation for remote deletions
Backup filesSaves SHA of every deleted branch for restoration
Dry-run modePreview changes without risk

♻️ Restoring Deleted Branches

Every deletion creates a backup file at ~/.deadbranch/backups/<repo>/backup-<timestamp>.txt.

The backup contains git commands to restore each branch:

# From the backup file:
git branch feature/old-api abc1234def5678
git branch bugfix/header-issue 987654fedcba

You can restore branches manually by running those commands, or use the deadbranch backup restore command.

πŸ”€ Pattern Matching

Exclude patterns support glob-style wildcards:

PatternMatches
wip/*wip/experiment, wip/test
*/draftfeature/draft, bugfix/draft
feature/*/tempfeature/foo/temp, feature/bar/temp
*test*test, testing, my-test-branch

πŸ“‹ Requirements

  • Git (installed and accessible in PATH)
  • A git repository (run from within a repo)

πŸ—ΊοΈ Roadmap

  • πŸ–₯️ Interactive TUI mode
  • πŸ‘€ --only-mine flag for personal branches
  • πŸ”— GitHub/GitLab PR detection
  • πŸ“Š Multiple output formats (JSON, CSV)
  • πŸ“ Per-repo configuration

⭐ Star History

Star History Chart

πŸ“„ License

MIT License β€” see LICENSE for details.

🀝 Contributing

Contributions are welcome! See CONTRIBUTING.md for development setup, commit message conventions, testing requirements, and how to submit a pull request.