An open API service indexing awesome lists of open source software.

https://github.com/armgabrielyan/deadbranch

Clean up stale git branches safely.
https://github.com/armgabrielyan/deadbranch

branch cleanup cli git rust

Last synced: 23 days ago
JSON representation

Clean up stale git branches safely.

Awesome Lists containing this project

README

          

# deadbranch

[![crates.io](https://img.shields.io/crates/v/deadbranch.svg)](https://crates.io/crates/deadbranch)
[![crates.io downloads](https://img.shields.io/crates/d/deadbranch.svg)](https://crates.io/crates/deadbranch)
[![npm version](https://img.shields.io/npm/v/deadbranch)](https://www.npmjs.com/package/deadbranch)
[![npm downloads](https://img.shields.io/npm/dm/deadbranch)](https://www.npmjs.com/package/deadbranch)
[![CI](https://github.com/armgabrielyan/deadbranch/actions/workflows/ci.yml/badge.svg)](https://github.com/armgabrielyan/deadbranch/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

**Clean up stale git branches safely.**

## Table of Contents

- [Demo](#demo)
- [Features](#-features)
- [Installation](#-installation)
- [Quick Install (macOS/Linux)](#-quick-install-macoslinux)
- [Homebrew (macOS/Linux)](#-homebrew-macoslinux)
- [npm/npx](#-npmnpx)
- [Cargo (from source)](#-cargo-from-source)
- [Manual Download](#️-manual-download)
- [Build from Source](#-build-from-source)
- [Shell Completions](#-shell-completions)
- [Quick Start](#-quick-start)
- [Usage](#️-usage)
- [List Stale Branches](#-list-stale-branches)
- [Delete Stale Branches](#️-delete-stale-branches)
- [Interactive Mode](#️-interactive-mode)
- [Dry Run Mode](#-dry-run-mode)
- [Configuration](#️-configuration)
- [Backup Management](#-backup-management)
- [Branch Statistics](#-branch-statistics)
- [Safety Features](#️-safety-features)
- [Restoring Deleted Branches](#️-restoring-deleted-branches)
- [Pattern Matching](#-pattern-matching)
- [Requirements](#-requirements)
- [Roadmap](#️-roadmap)
- [License](#-license)
- [Contributing](#-contributing)

`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](https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/demo/list.gif)

## ✨ 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)

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

### 🍺 Homebrew (macOS/Linux)

```bash
brew install armgabrielyan/tap/deadbranch
```

### πŸ“¦ npm/npx

```bash
# Install globally
npm install -g deadbranch

# Or run directly
npx deadbranch list
```

### πŸ¦€ Cargo (from source)

```bash
cargo install deadbranch
```

### ⬇️ Manual Download

Download pre-built binaries from the [GitHub Releases](https://github.com/armgabrielyan/deadbranch/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

```bash
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

```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`:

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

### Zsh

```bash
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):

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

Reload your shell or run `exec zsh` to activate.

### Fish

```bash
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

```bash
# 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](https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/demo/list.gif)

```bash
deadbranch list [OPTIONS]
```

| Option | Description |
|--------|-------------|
| `-d, --days ` | 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](https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/demo/clean.gif)

```bash
deadbranch clean [OPTIONS]
```

| Option | Description |
|--------|-------------|
| `-d, --days ` | 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:**

```bash
$ 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](https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/demo/interactive.gif)

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

```bash
deadbranch clean -i [OPTIONS]
```

| Option | Description |
|--------|-------------|
| `-i, --interactive` | Open interactive TUI for branch selection |
| `-d, --days ` | 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:

```bash
$ 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](https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/demo/config.gif)

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

```bash
# 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:**

```toml
[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](https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/demo/backup.gif)

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

#### List backups

```bash
# 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

```bash
# 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

```bash
# Show storage usage per repository and overall
deadbranch backup stats
```

#### Clean up old backups

```bash
# 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](https://raw.githubusercontent.com/armgabrielyan/deadbranch/main/demo/stats.gif)

Get a health overview of all branches in your repository:

```bash
deadbranch stats [OPTIONS]
```

| Option | Description |
|--------|-------------|
| `-d, --days ` | 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//backup-.txt`.

The backup contains git commands to restore each branch:

```bash
# 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

- [x] πŸ–₯️ 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](https://api.star-history.com/svg?repos=armgabrielyan/deadbranch&type=Date)](https://star-history.com/#armgabrielyan/deadbranch&Date)

## πŸ“„ License

MIT License β€” see [LICENSE](LICENSE) for details.

## 🀝 Contributing

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