Learn how to delete multiple Git branches quickly and safely using simple cleanup commands. This guide covers deleting old branches, removing all local branches except master or main, bulk branch deletion, merged branch cleanup, and Git maintenance best practices.
Table of Contents
Why You Should Clean Up Old Git Branches
A repository’s branch list is like a shared workspace — manageable when everyone keeps it tidy, overwhelming when they don’t. Most teams start with great intentions, but without a deliberate cleanup habit, branches accumulate fast. Knowing when and how to delete old Git branches in bulk is one of the highest-leverage maintenance habits you can build.
Problems Caused by Too Many Branches
📌 Real-world Scenario: Branch Sprawl at Scale Common Problem
None of this is caused by bad developers — it’s caused by the absence of a cleanup system. The fix takes less than 10 minutes once you know the right commands.
The practical problems of an uncleaned branch list include:
- Cognitive overhead — developers waste time figuring out which branches are active vs abandoned.
- Slower git operations —
git fetch,git branch -a, and tab completion all slow down with hundreds of refs. - CI/CD noise — pipelines triggered on branch patterns inadvertently pick up old branches.
- Merge confusion — stale branches with outdated code get accidentally used as base branches for new work.
- Security risk — old feature branches may contain sensitive data, credentials, or experimental code that was never meant to persist.
Local vs Remote Branch Cleanup
It’s important to understand that deleting a branch in one place does not automatically delete it in another. Git tracks two distinct things:
| Type | What it is | How to list | How to delete |
|---|---|---|---|
| Local branch | A branch pointer in your local clone only | git branch | git branch -d <name> |
| Remote branch | A branch on the server (GitHub, GitLab, etc.) | git branch -r | git push origin --delete <name> |
| Remote-tracking ref | A local cached copy of a remote branch (e.g. origin/feature-x) | git branch -r | git fetch --prune or git remote prune origin |
A complete cleanup typically involves all three: deleting the local branch, deleting the remote branch, and pruning the stale tracking ref from your local cache. This guide covers each step.
When to Delete Old Feature Branches
The right time to delete a feature branch is immediately after its pull or merge request is merged. If that didn’t happen, the next best time is during a periodic cleanup. Good triggers for a bulk cleanup session include:
- After a major release cycle completes
- At the start of a new quarter or project phase
- When onboarding new team members (give them a clean slate)
- When branch tab-completion starts taking more than a second
- Any time your
git branch -aoutput scrolls more than two terminal pages
How to Delete Multiple Git Branches
Git doesn’t have a single built-in command to delete multiple branches by name at once, but you can pass multiple branch names to git branch -d in a single call — and pair it with shell tools like grep, awk, and xargs to build powerful one-liners for bulk deletion.
Delete Several Branches in One Command
The simplest approach: pass multiple branch names directly to git branch -d. Git accepts a space-separated list of branch names as a single command.
# Delete several specific branches in one command
git branch -d feature/login feature/signup feature/profile
# Same pattern — as many branches as needed
git branch -d fix/bug-101 fix/bug-204 hotfix/session-timeoutOutput
Deleted branch feature/signup (was d3b07c1).
Deleted branch feature/profile (was 9fc28a7).
To delete branches matching a pattern — say, all branches starting with fix/ — pipe the output of git branch through grep and xargs:
# Delete all branches starting with "fix/"
git branch | grep 'fix/' | xargs git branch -d
# Delete all branches containing "temp" or "wip"
git branch | grep -E '(temp|wip)' | xargs git branch -d
# Preview first — see which branches would be deleted
git branch | grep 'fix/'| Command Breakdown | Description |
|---|---|
| git branch | Lists all local branch names, one per line, with a leading space or asterisk for the active branch. |
| | grep ‘fix/’ | Filters output to lines containing the pattern. Use -v to invert (exclude lines matching the pattern). |
| | xargs git branch -d | Takes each line of input and passes it as an argument to git branch -d. The -d flag only deletes branches that are already merged. |
Force Delete Multiple Branches
The -d (lowercase) flag refuses to delete a branch if it has unmerged commits — a useful safety check. The -D (uppercase) flag skips that check and deletes unconditionally. Use it carefully, and only after you are certain the work is either merged, backed up, or intentionally discardable.
# Force delete specific branches regardless of merge status
git branch -D experiment/old-approach experiment/deprecated-api
# Force delete all branches matching a pattern (CAUTION)
git branch | grep 'experiment/' | xargs git branch -D-D Deletes Unmerged Work Permanently
-D on a branch with unique commits that don’t exist anywhere else will make those commits unreachable. Git’s garbage collector will eventually purge them. Before force-deleting, run git log <branch> to confirm there’s nothing worth keeping, or create a tag on the tip commit as a backup reference.Verify Deleted Branches
After a bulk delete, always verify your branch list is in the expected state before continuing.
# List all remaining local branches
git branch
# List all local + remote branches
git branch -a
# Count how many local branches remain
git branch | wc -lHow to Delete All Local Branches Except Main or Master
One of the most common bulk cleanup tasks is to delete all branches except master (or main) — wiping the local branch list clean while preserving the default branch. This is especially useful after major merges, project handoffs, or when starting a fresh sprint cycle.
Delete All Branches Except Master
This command uses grep -v to exclude master from the branch list before passing the remaining names to git branch -d:
# Safe delete — only removes merged branches
git branch | grep -v "master" | xargs git branch -d
# Preview first — check which branches would be removed
git branch | grep -v "master"
# Also exclude the currently checked-out branch (shown with *)
git branch | grep -v -E "(\*|master)" | xargs git branch -d| Command Breakdown | Description |
|---|---|
| git branch | Outputs all local branch names. The active branch is prefixed with * (asterisk + space). |
| | grep -v “master” | The -v flag inverts the match — it returns all lines that do not contain “master”. This effectively excludes your protected branch from the list. |
| | xargs git branch -d | Feeds each remaining branch name to git branch -d. The safe -d flag will warn and skip any branch with unmerged commits. |
Make Sure You Are Not on master When Running This
master first with git checkout master, then run the cleanup command.Delete All Branches Except Main
Repositories that use main as the default branch work identically — just swap the pattern:
# First, switch to main
git checkout main
# Safe delete — only merged branches removed
git branch | grep -v "main" | xargs git branch -d
# Force delete — removes ALL other branches including unmerged (use carefully)
git branch | grep -v "main" | xargs git branch -D
# Exclude both "main" and "develop" from deletion
git branch | grep -v -E "(main|develop)" | xargs git branch -dExclude Multiple Protected Branches
-E flag with grep to write an extended regex pattern. For example, grep -v -E "(main|master|develop|staging)" will exclude all four branches from the deletion list in a single command.Windows PowerShell Alternative
The grep | xargs pipeline is a Unix idiom that doesn’t work natively in Windows Command Prompt or standard PowerShell. Here are the PowerShell equivalents:
# Switch to master first
git checkout master
# Delete all other local branches
git branch | Where-Object { $_ -notmatch '^\s*\*?\s*master$' } |
ForEach-Object { git branch -d $_.Trim() }Safety Tips Before Running Bulk Delete Commands
Pre-Cleanup Checklist
git branch -vv to see each branch’s last commit and upstream status. (2) Make sure you’re on a protected branch like main or master. (3) Run the grep command without xargs git branch -d first to preview which branches would be deleted. (4) If uncertain, tag the tip of any branch you’re about to delete as a safety net before running the command.# Step 1: Switch to your protected branch
git checkout main
# Step 2: Pull latest to make sure main is up to date
git pull origin main
# Step 3: DRY RUN — preview what would be deleted
git branch | grep -v "main"
# Step 4: If the preview looks correct, run the actual delete
git branch | grep -v "main" | xargs git branch -d
# Step 5: Verify the result
git branchHow to Delete Old Merged Branches in Git
The safest approach to bulk cleanup is to only delete branches whose commits are already reachable from another branch — in other words, branches that are fully merged. Git makes this easy with the --merged flag.
Find Merged Branches
# List all local branches already merged into current branch (main)
git branch --merged
# List branches merged into a specific branch (not necessarily the current one)
git branch --merged main
# List merged remote-tracking branches
git branch -r --merged mainBranch List Output
feature/payment-v2
fix/header-layout
* main
fix/session-bug
Delete Merged Branches Automatically
Once you’ve confirmed the list looks correct, pipe it through grep to exclude your protected branches and into xargs to delete everything that remains. This is the canonical way to git delete old branches safely:
# The gold standard command for safe bulk cleanup
# Deletes all local merged branches except main, master, and develop
git branch --merged | \
grep -v -E "(\*|main|master|develop)" | \
xargs git branch -d
# Also clean up merged remote branches on origin
git branch -r --merged main | \
grep -v -E "(main|master|develop|HEAD)" | \
sed 's/origin\///' | \
xargs -I{} git push origin --delete {}| Command Breakdown | Description |
|---|---|
| git branch -r –merged main | Lists remote-tracking branches whose commits are already reachable from main. These are safe to delete. |
| | grep -v -E “…” | Excludes protected branches and the HEAD symbolic ref that always appears in remote branch listings. |
| | sed ‘s/origin\///’ | Strips the origin/ prefix from branch names, since git push --delete expects just the branch name, not the remote prefix. |
| | xargs -I{} git push origin –delete {} | Substitutes each branch name for {} and runs git push origin --delete for each one in sequence. |
Keep Important Branches Protected
When writing your grep -v exclusion pattern, think carefully about which branches should never be touched by a bulk cleanup script:
main/master— your default production branch.develop— the integration branch in Gitflow workflows.staging/release— any branch that maps to a deployed environment.release/*— versioned release branches used for hotfixes.- Any branch with an active pull request against it.
How to Remove Stale Remote Tracking Branches
Even after a remote branch is deleted on the server, your local git repository still holds a cached reference to it under refs/remotes/origin/. These stale references clutter your git branch -a output and can cause confusion. Git’s prune mechanism removes them.
Use Git Prune
# Fetch and prune in one command (recommended)
git fetch --prune origin
# Or prune without fetching new data
git remote prune origin
# Dry run — see what would be pruned without actually pruning
git remote prune origin --dry-run
# Configure git to always prune on fetch (set once, applies everywhere)
git config --global fetch.prune truePrune Output
– [deleted] (none) -> origin/feature/dark-mode
– [deleted] (none) -> origin/feature/payment-v2
– [deleted] (none) -> origin/fix/header-layout
Difference Between Local and Remote Tracking Branches
A common point of confusion: when you run git branch -d feature/login, you delete the local branch — the one you can check out and commit to. But the remote-tracking reference (origin/feature/login) is a separate object. Think of remote-tracking refs as your local repository’s memory of where a remote branch was the last time you fetched.
🔍 Seeing the Difference in Practice Concept
feature/auth on GitHub, git branch (local branches) no longer shows it. But git branch -r (remote-tracking refs) still shows origin/feature/auth until you run git fetch --prune.This is why
git branch -a can show branches that appear to exist but can’t actually be checked out — the remote copy is gone, but the local cache hasn’t been cleared yet.Why Deleted Remote Branches Still Appear
Three reasons a deleted remote branch continues to show up locally:
- You haven’t fetched since the deletion — remote-tracking refs only update when you run
git fetch. - Prune is not enabled — by default,
git fetchadds and updates refs but does not delete stale ones. Run with--pruneor setfetch.prune trueglobally. - You still have a local branch tracking it — even after the remote is gone, a local branch configured to track it will still show up in
git branch(notgit branch -r) until you delete it explicitly.
# List all local branches with their tracking status
git branch -vv
# Branches showing ": gone]" are tracking a deleted remote
feature/auth a1b2c3d [origin/feature/auth: gone] Add JWT auth
feature/profile d4e5f6g [origin/feature/profile: gone] User profile page
* main 9h8i7j6 [origin/main] Update README
# Delete all branches whose upstream is gone
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -dGit Delete Current Branch Error Explained
One of the most common errors encountered during branch cleanup is git refusing to delete the branch you currently have checked out. This isn’t a permission issue — it’s a fundamental constraint of how git works.
Why Git Refuses to Delete the Active Branch
Error
Your working directory is always anchored to exactly one branch (or a detached HEAD state). Git tracks which files to give you, where to write new commits, and what HEAD points to — all based on the currently checked-out branch. Deleting the active branch would leave git in an undefined state: HEAD would point to a branch ref that no longer exists.
This protection is intentional and cannot be bypassed with -D or any other flag. The only solution is to switch to a different branch first.
Switch Branches Before Cleanup
# Check which branch you are currently on
git branch --show-current
feature/login
# Switch to main (or master, or any other branch)
git checkout main
# Now delete the previously active branch
git branch -d feature/login
Deleted branch feature/login (was 4a8f1c2).
# When running bulk deletes, always start from main/master
git checkout main && git branch | grep -v "main" | xargs git branch -dWhat About Worktrees?
git worktree, a branch can also be “checked out” in a linked worktree even when it’s not your main working directory. Git will still refuse to delete it. Run git worktree list to see all active worktrees and check out a different branch in the relevant worktree before attempting deletion.Common Git Branch Cleanup Mistakes
Accidentally Deleting Important Branches
git remove all local branches style commands without a dry-run preview is the single most common mistake. Developers write a cleanup one-liner, forget to exclude a branch like develop or release/2.0, and delete critical work.Fix: Always preview with
git branch | grep -v "pattern" before appending | xargs git branch -d. Make the preview step a habit, not an afterthought.Using-D Instead of-d
-D flag skips git’s unmerged-branch safety check. On a single branch, this is sometimes intentional. Applied with xargs across dozens of branches, it can silently delete branches containing work that was never merged.Fix: Start with
-d (lowercase). If git prints a “not fully merged” warning for a specific branch, investigate that branch manually before force-deleting it with -D.Deleting Unmerged Work
-D flag skips git’s unmerged-branch safety check. On a single branch, this is sometimes intentional. Applied with xargs across dozens of branches, it can silently delete branches containing work that was never merged.Fix: Start with
-d (lowercase). If git prints a “not fully merged” warning for a specific branch, investigate that branch manually before force-deleting it with -D.Forgetting Remote Cleanup
git branch -r. The repository still looks cluttered to everyone else.Fix: Always pair local branch cleanup with remote deletion (
git push origin --delete) and ask teammates to run git fetch --prune afterward to sync their local tracking refs.Best Practices for Git Branch Maintenance
Delete Branches After Pull Requests
Use Naming Conventions
feature/, fix/, hotfix/, chore/ make grep-based bulk cleanup trivial and safe. Never name branches ambiguously.Schedule Repository Cleanup
Enable Auto Delete After Merge
Set fetch.prune Globally
git config --global fetch.prune true on every developer machine. Remote-tracking refs will stay synchronized automatically on every fetch.Tag Before Deleting Experiments
git tag archive/experiment-name branch-name. Tags persist indefinitely and can be checked out later.Delete Branches After Pull Requests
The most durable solution to branch sprawl isn’t a cleanup script — it’s establishing a team norm that every feature branch is deleted at the moment its PR is merged. This removes the problem at the source and means bulk cleanup becomes a much smaller task when you do run it.
Use Naming Conventions
Consistent branch naming isn’t just an aesthetics preference — it makes automated cleanup dramatically safer. When all feature branches start with feature/, you can write grep 'feature/' with high confidence that nothing critical will be caught in the filter. Ambiguous names like new-stuff or test are impossible to filter safely.
Schedule Repository Cleanup
Even with auto-delete enabled for PR branches, repositories accumulate branches from direct pushes, abandoned work, and branches from before auto-delete was enabled. A quarterly audit using the --merged filter and the stale branch views in your platform’s UI takes 15 minutes and keeps things manageable indefinitely.
Enable Auto Delete After Merge
On GitHub: Settings → General → Pull Requests → Automatically delete head branches. On GitLab: Settings → General → Merge Requests → Enable “Delete source branch” option by default. On Azure DevOps: set the completion option in branch policies. On Bitbucket: check “Delete branch” in the merge dialog or set it as the repository default. Five minutes of configuration eliminates the need for ongoing manual cleanup.
Frequently Asked Questions
How do I delete all local branches at once?
git branch | grep -v "\*" | xargs git branch -dThe
grep -v "\*" part excludes the currently active branch (which git marks with an asterisk). Use -D instead of -d if you want to force-delete branches with unmerged commits — but verify the list carefully first. To be even more precise, first switch to main or master, then run git branch | grep -v "main" | xargs git branch -d to exclude your default branch explicitly.Can I recover deleted Git branches?
git reflog to find the last commit SHA of the deleted branch, then recreate it with git checkout -b recovered-branch-name SHA. On GitHub and GitLab, branches deleted after a merged PR can also be restored directly from the PR page for 30 days. On Bitbucket and Azure DevOps, recovery depends on whether the commits are reachable from a local clone’s reflog.How do I remove remote tracking branches?
origin/feature-x entries that appear in git branch -r) are pruned with:git fetch --prune originOr alternatively:
git remote prune originTo prevent them from accumulating in the first place, set
git config --global fetch.prune true so that every future git fetch automatically prunes stale tracking refs. Note: this only removes stale tracking refs on your local machine — it does not delete the actual remote branches on the server.Is it safe to bulk delete merged branches?
--merged flag. The command git branch --merged main | grep -v -E "(main|master|develop)" | xargs git branch -d only targets branches whose commits are already reachable from main. The -d (lowercase) flag adds a second safety layer — it will refuse to delete any branch that git considers unmerged. The combination of --merged filtering and the -d safety check makes bulk deletion of merged branches one of the safest git operations you can run.Conclusion
Knowing how to git delete multiple branches efficiently — and safely — is a skill that pays off every single sprint. Whether you’re doing a one-time cleanup of a cluttered repository or establishing a repeatable maintenance workflow for your team, the core commands are straightforward once you understand the building blocks: git branch --merged, grep -v for exclusions, and xargs to pipe the results into deletion.
Quick Recap of Bulk Git Cleanup Commands
| Scenario | Command |
|---|---|
| Delete multiple by name | git branch -d branch1 branch2 branch3 |
| Delete all except master | git branch | grep -v “master” | xargs git branch -d |
| Delete all except main | git branch | grep -v “main” | xargs git branch -d |
| Delete all merged branches | git branch –merged | grep -v -E “(main|master|develop)” | xargs git branch -d |
| Delete by pattern | git branch | grep ‘feature/’ | xargs git branch -d |
| Prune stale remote refs | git fetch –prune origin |
| Find branches tracking gone remotes | git branch -vv | grep ‘: gone]’ | awk ‘{print $1}’ | xargs git branch -d |
| Enable auto-prune globally | git config –global fetch.prune true |
Recommended Branch Cleanup Workflow
- Switch to your default branch —
git checkout main— and pull the latest changes. - Preview merged branches —
git branch --merged | grep -v -E "(main|master|develop)"— to see what’s safe to delete. - Delete merged local branches — append
| xargs git branch -dto the command above. - Prune stale remote-tracking refs —
git fetch --prune origin. - Delete merged remote branches using your platform’s UI or the
git push origin --deletepattern. - Enable auto-delete in your repository settings so future merges clean up automatically.
- Schedule a quarterly review for any branches that slip through — old WIP branches, abandoned experiments, and any branches predating your auto-delete setting.
Also read how to delete Git branches in GitHub, GitLab, Azure DevOps, and Bitbucket with easy step-by-step instructions and screenshots



