A robust and safe Bash script to prune merged or empty Git branches locally and on remotes.
- Safety First: Defaults to dry-run mode. Never deletes anything without explicit consent.
- Pattern Matching: Supports glob patterns (
*,?,[]) for branch protection and explicit deletion. - Interactive Mode: Select branches to delete using an interactive index-based interface.
- Smart Detection: Detects merged, squash-merged, and cherry-picked branches using commit history, content hashes (patch-id), and tree-hash comparisons. It accurately identifies squash-merges even if the default branch has advanced with other commits.
- Worktree Aware: Prevents deletion of branches currently checked out in any Git worktree.
- Batch Deletion: Optimizes performance by deleting multiple remote branches in a single
git pushcommand.
- Git: Version 2.23.0 or later recommended.
- Bash: Version 4.3 or later (required for associative arrays and namerefs).
- macOS: The default Bash bundled with macOS is outdated (version 3.2). You must upgrade
bashusing Homebrew:brew install bash
- Linux: Most modern distributions come with a compatible Bash version pre-installed.
- Windows: Use Git Bash or WSL2.
./prune-branches.sh [options]| Option | Description | Scope / Default |
|---|---|---|
-m, --delete-merged |
Delete merged branches. | local, remote, both (default: local) |
-e, --delete-empty |
Delete branches with no commits relative to the default branch (includes fast-forward merged). | local, remote, both (default: local) |
-i, --interactive |
Enable interactive selection interface. | - |
-n, --dry-run |
Preview deletions without performing them (default behavior). | - |
| Option | Description | Format |
|---|---|---|
-d, --delete <list> |
Explicitly delete branches matching glob patterns. Each branch is verified if it is clean for safety. | Space-separated list (e.g., feature/*). |
Patterns to protect branches from deletion can be defined across multiple sources. All patterns are combined into a single protection list:
| Source | Description | Format |
|---|---|---|
.prune-branches-protect |
Project-root configuration file. | One pattern per line. Supports # comments (including inline) and empty lines. |
PRUNE_BRANCHES_PROTECT |
Environment variable. | Space-separated list of patterns. |
-p, --protect <list> |
Command-line option. | Space-separated list (quote if multiple). |
Whether defined in a file, environment variable, or CLI option, patterns are matched against branch names using the following rules:
- Pattern without remote prefix (e.g.,
release/*): Matches both local branches (release/v1) and remote branches (origin/release/v1). - Pattern with remote prefix (e.g.,
origin/feature/*): Matches only the specified remote branch. The corresponding local branch will NOT be protected by this pattern.
Example .prune-branches-protect:
# Always protect development and release branches
develop
release/*
# Protect a specific remote-only branch
origin/experimental-long-term # Inline comments are supported
| Option | Description | Default |
|---|---|---|
-r, --remote <name> |
Specify the remote repository to use. | Auto-detected (prioritizes origin) |
-w, --worktree |
Delete associated worktrees when deleting local branches. | - |
When running with -i or --interactive, the script displays a numbered list of candidates to be able to select which
branches to delete:
Branches that can be deleted
┌───┬─────────────────────┬────────────────┬──────────────────────────────────┐
│ # │ Last Commit Date │ Last Committer │ Branch/Worktree Name │
├───┼─────────────────────┼────────────────┼──────────────────────────────────┤
│ 1 │ 2026-02-15 12:00:00 │ Committer 1 │ local(merged): feature/login │
│ 2 │ 2026-02-15 13:00:00 │ Committer 2 │ local(picked): feature/logout │
│ 3 │ 2026-02-15 14:00:00 │ Committer 1 │ remote(squashed): fix/deletion │
│ 4 │ 2026-02-15 15:00:00 │ Committer 3 │ remote(empty): fix/something │
└───┴─────────────────────┴────────────────┴──────────────────────────────────┘
Enter the indices of the branches to delete (e.g., '1,2,5-7'),
or enter 'all' to delete all shown branches.
Press return or enter 'q' to quit without deleting anything.
Selection:
You can specify indices using the following conventions:
| Input | Description |
|---|---|
1,3,5 |
Only items 1, 3, and 5. |
1-5 |
Range of items. |
-5 |
From index 1 to 5 (shorthand). |
10- |
From index 10 to the end (shorthand). |
all |
Select all candidates. |
Copyright © 2025-2026 Hiroshi Muramatsu. Distributed under the MIT License. See the LICENSE.md file for details.