How to Delete Multiple Git Branches | All Old Local Branches Except Master

|

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.

Git CLI
Safety-First
8 min read

🧹 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

A 12-person engineering team ships weekly. After 8 months, their repository has accumulated 340 branches. The branch dropdown is so long that developers stop using it. New engineers spend time investigating branches that were merged and forgotten months ago. CI/CD pipelines occasionally pick up stale branches by pattern match, wasting compute time.
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 operationsgit 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:

TypeWhat it isHow to listHow to delete
Local branchA branch pointer in your local clone onlygit branchgit branch -d <name>
Remote branchA branch on the server (GitHub, GitLab, etc.)git branch -rgit push origin --delete <name>
Remote-tracking refA local cached copy of a remote branch (e.g. origin/feature-x)git branch -rgit 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 -a output 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.

Pass multiple names to a single git branch -d call
# 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-timeout

Output

Deleted branch feature/login (was 4a8f1c2).
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 matching a pattern
# 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 BreakdownDescription
git branchLists 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 -dTakes 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 — use with caution
# 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

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

Verify current branch state
# List all remaining local branches
git branch

# List all local + remote branches
git branch -a

# Count how many local branches remain
git branch | wc -l

🎯 How 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:

Delete all local branches except master
# 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 BreakdownDescription
git branchOutputs 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 -dFeeds 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

If your active branch is one of the branches being deleted, git will refuse to delete it and print an error. Switch to 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:

Delete all local branches except main
# 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 -d

Exclude Multiple Protected Branches

Use the -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:

Delete all local branches except master
# 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

Before running any bulk branch deletion command, work through this checklist: (1) Run 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.
BASH – Safe bulk-delete workflow — always preview first
# 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 branch

🔀 How 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

BASH – List merged branches (preview before deleting)
# 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 main

Branch List Output

feature/dark-mode
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:

BASH – Delete all merged branches except main/master/develop
# 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 BreakdownDescription
git branch -r –merged mainLists 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

BASH – Prune stale remote-tracking references
# 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 true

Prune Output

From github.com:org/repo
– [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

After your teammate deletes 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 fetch adds and updates refs but does not delete stale ones. Run with --prune or set fetch.prune true globally.
  • 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 (not git branch -r) until you delete it explicitly.
BASH – Find local branches tracking a deleted remote
# 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 -d

Git 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

error: Cannot delete branch ‘feature/login’ checked out at ‘/Users/dev/project’

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

BASH – Fix: switch away before deleting
# 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 -d

What About Worktrees?

If you use 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

Running 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

The uppercase -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

The uppercase -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

Deleting local branches only is a half-completed job. The remote branches on GitHub, GitLab, or Bitbucket remain, and teammates’ local repos still see them via 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

Treat branch deletion as a mandatory step in your PR workflow, not an optional cleanup. Every merged PR should end with the source branch deleted.

Use Naming Conventions

Prefixes like feature/, fix/, hotfix/, chore/ make grep-based bulk cleanup trivial and safe. Never name branches ambiguously.

Schedule Repository Cleanup

Set a calendar reminder every quarter to run a full branch audit. Review stale branches older than 90 days and delete or archive them as a team.

Enable Auto Delete After Merge

GitHub, GitLab, Azure DevOps, and Bitbucket all support auto-deletion of source branches after merge. Enable it at the repository level to remove the human memory requirement entirely.

Set fetch.prune Globally

Run git config --global fetch.prune true on every developer machine. Remote-tracking refs will stay synchronized automatically on every fetch.

Tag Before Deleting Experiments

If deleting an unmerged experimental branch, create a lightweight tag first: 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?

To delete all local branches except the one you’re currently on, run:

git branch | grep -v "\*" | xargs git branch -d

The 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?

Yes, usually. Git’s reflog keeps a record of every position HEAD has been at, typically for 30–90 days. Run 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?

Remote-tracking branches (the origin/feature-x entries that appear in git branch -r) are pruned with:

git fetch --prune origin

Or alternatively: git remote prune origin

To 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?

Yes — if you use the --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

ScenarioCommand
Delete multiple by namegit branch -d branch1 branch2 branch3
Delete all except mastergit branch | grep -v “master” | xargs git branch -d
Delete all except maingit branch | grep -v “main” | xargs git branch -d
Delete all merged branchesgit branch –merged | grep -v -E “(main|master|develop)” | xargs git branch -d
Delete by patterngit branch | grep ‘feature/’ | xargs git branch -d
Prune stale remote refsgit fetch –prune origin
Find branches tracking gone remotesgit branch -vv | grep ‘: gone]’ | awk ‘{print $1}’ | xargs git branch -d
Enable auto-prune globallygit config –global fetch.prune true

Recommended Branch Cleanup Workflow

  1. Switch to your default branchgit checkout main — and pull the latest changes.
  2. Preview merged branchesgit branch --merged | grep -v -E "(main|master|develop)" — to see what’s safe to delete.
  3. Delete merged local branches — append | xargs git branch -d to the command above.
  4. Prune stale remote-tracking refsgit fetch --prune origin.
  5. Delete merged remote branches using your platform’s UI or the git push origin --delete pattern.
  6. Enable auto-delete in your repository settings so future merges clean up automatically.
  7. 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

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x