deadbranch 0.2.1

Clean up stale git branches safely
deadbranch-0.2.1 is not a library.

deadbranch

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 list

✨ 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.

Platform Architecture Download
Linux x86_64 (glibc) deadbranch-VERSION-x86_64-unknown-linux-gnu.tar.gz
Linux x86_64 (musl/static) deadbranch-VERSION-x86_64-unknown-linux-musl.tar.gz
Linux ARM64 deadbranch-VERSION-aarch64-unknown-linux-gnu.tar.gz
macOS Intel deadbranch-VERSION-x86_64-apple-darwin.tar.gz
macOS Apple Silicon deadbranch-VERSION-aarch64-apple-darwin.tar.gz
Windows x86_64 deadbranch-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]
Option Description
-d, --days <N> Only show branches older than N days (default: 30)
--local Only show local branches
--remote Only show remote branches
--merged Only 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]
Option Description
-d, --days <N> Only delete branches older than N days (default: 30)
--merged Only delete merged branches (this is the default)
--force Force delete unmerged branches (dangerous!)
--dry-run Show what would be deleted without doing it
--local Only delete local branches
--remote Only delete remote branches
-y, --yes Skip 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]
Option Description
-i, --interactive Open interactive TUI for branch selection
-d, --days <N> Pre-filter to branches older than N days
--merged Start with merged-only filter active
--force Unlock unmerged branches for selection
--local Start with local-only filter active
--remote Start with remote-only filter active

Key bindings:

Key Action
j/↓, k/↑ Navigate down/up
gg, G Jump to top/bottom
Ctrl+d/Ctrl+u Half-page down/up
Ctrl+f/Ctrl+b Full-page down/up
Mouse scroll Scroll up/down
Space Toggle selection
V Visual range select (Vim-style)
a Select all merged
A Select all (requires --force)
n Deselect all
i Invert selection
d Delete selected
/ Fuzzy search by branch name
s Cycle sort (age β†’ branch β†’ status β†’ type β†’ date β†’ author)
S Reverse sort direction
m Toggle merged-only filter
l Toggle local-only filter
R Toggle remote-only filter
? Help
q/Esc Quit

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
  • Progressive deletion β€” Live progress bar during batch deletion
  • 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

Key Aliases Description
days default-days, general.default-days Default age threshold in days
default-branch branches.default-branch Branch used for merge detection (auto-detected if unset)
protected-branches branches.protected Branches that are never deleted
exclude-patterns branches.exclude-patterns Glob 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]
Option Description
-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:

Feature Description
Merged-only default Only deletes branches already merged to main/master
Protected branches Never deletes main, master, develop, staging, production
Current branch Never deletes the branch you're currently on
WIP detection Excludes branches matching wip/*, draft/*, etc.
Confirmation prompts Always asks before deleting
Remote warning Extra confirmation for remote deletions
Backup files Saves SHA of every deleted branch for restoration
Dry-run mode Preview 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:

Pattern Matches
wip/* wip/experiment, wip/test
*/draft feature/draft, bugfix/draft
feature/*/temp feature/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.