GitDeleting Branches

Deleting Branches

Branches are cheap, but they pile up. Deleting old branches keeps the list manageable and prevents confusion (“is this branch alive or dead?”). Deletion in Git is safe by default — it refuses to delete branches whose commits aren’t already merged somewhere, so you cannot easily lose work.

Delete a merged branch (safe)

Bash
git branch -d feature-x
# Deleted branch feature-x (was 1f9ab2c).

-d (lowercase) only works if the branch’s tip is already reachable from another branch — usually because it has been merged. If not, Git refuses:

The safety check

Text
error: The branch 'feature-x' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-x'.
Force-delete an unmerged branch

-D (capital) bypasses the safety check

Bash
git branch -D feature-x
# Deleted branch feature-x (was 1f9ab2c).
Warning
`-D` deletes the branch even if it has unmerged commits. Those commits become *unreachable* — still in the repo for a while (recoverable via `git reflog`), but gone after garbage collection.
Cannot delete the branch you are on

Bash
git branch -d main
# error: Cannot delete branch 'main' checked out at '/Users/you/my-repo'

# Switch away first
git switch other-branch
git branch -d main
Delete a remote branch

Delete on the server

Bash
git push origin --delete feature-x
# - [deleted]   feature-x

# Short form
git push origin :feature-x

The empty source (:branch) form is older but still works everywhere. Newer Git users prefer the explicit --delete.

Remove stale local pointers to deleted remote branches

After a teammate deletes a remote branch

Bash
git fetch --prune
# - [deleted] (none) -> origin/feature-x

# Or auto-prune on every fetch
git config --global fetch.prune true
Bulk-delete merged branches

Clean up all merged branches except main

Bash
# Show what would be deleted
git branch --merged | grep -vE '(^\*|main|master|develop)'

# Actually delete them
git branch --merged | grep -vE '(^\*|main|master|develop)' | xargs git branch -d

Save it as an alias for easy cleanup:

Bash
git config --global alias.cleanup "!git branch --merged | grep -vE '(^\\*|main|master|develop)' | xargs -n 1 git branch -d"

# Then: git cleanup
Delete every remote branch matching a pattern

Use with care

Bash
# List first
git branch -r | grep 'feature/'

# Delete
git branch -r | grep 'feature/' | sed 's|origin/||' | xargs -I {} git push origin --delete {}
Recovering a deleted branch

Even after deletion, commits hang around in the reflog and the object database for ~90 days. Recovery is usually possible.

Resurrect a branch

Bash
# Find its last hash in the reflog
git reflog | grep deleted-branch
# 1f9ab2c HEAD@{42}: branch: Created from ...

# Recreate the branch at that commit
git switch -c deleted-branch 1f9ab2c
What -d vs -D actually do
  • git branch -d — refuses unless the branch is merged into the upstream OR into the current branch. Safest option.

  • git branch -D — deletes unconditionally. Same as git branch --delete --force.

  • Neither command deletes commits — they delete the pointer. The commits are GC’d later when nothing references them.

Workflow recap

The clean post-merge cycle

Bash
# After your PR is merged on GitHub:
git switch main
git pull --prune          # bring in the merge, prune stale refs
git branch -d feature-x   # delete locally
# 'git push origin --delete' if you didn't tick "delete after merge" on GitHub
GitHub auto-deletes branches
In GitHub repository settings → General → Pull Requests, enable “Automatically delete head branches.” After every PR merge, the source branch is deleted on the remote. You only need to clean up locally.
Tip
Make branch deletion part of your daily rhythm. After every PR merge, run git switch main && git pull --prune && git branch -d <branch>. Two weeks of this and your branch list stays sane forever.