Once a PR is merged, the local branch behind it is dead weight. After a few months git branch scrolls past a screenful and you can’t find the branch you’re working on. The commands to clean this up are simple. The point is knowing what’s safe to delete.

-d is safe, -D is dangerous

There are two ways to delete a local branch. git branch -d deletes only branches that are already merged. If unmerged commits remain, it refuses.

$ git branch -d feature/login
Deleted branch feature/login (was 13d794b).

$ git branch -d feature/experimental
error: the branch 'feature/experimental' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/experimental'

feature/login was merged into main, so it’s gone instantly. feature/experimental still has unique commits, so the delete is refused. That refusal stops you from throwing away work by accident.

git branch -D ignores merge status and force-deletes. Use it when you ignore the error above and really do want the branch gone.

git branch -D feature/experimental

Use -d by default. When Git refuses, it’s a signal that there’s unfinished work. Reach for -D deliberately, only to discard experimental branches.

To see what’s safe before deleting anything, these two commands tell you.

$ git branch --merged       # merged into current branch -> safe to delete
  feature/login
* main

$ git branch --no-merged    # has unique commits -> delete with care
  feature/experimental

Deleting remote branches

Delete a remote branch with push --delete.

git push origin --delete feature/user-authentication

Deleting it on the remote leaves a stale tracking reference (origin/feature/...) in everyone else’s local clone, and in yours. That leads to the next problem.

‘gone’ branches

A branch that was merged and deleted on the remote leaves a trace locally. git branch -vv shows each branch’s tracking status and exposes it.

$ git branch -vv
  bugfix/typo 884c27d [origin/bugfix/typo: gone] Fix typo
* main        24e0729 [origin/main]

: gone] means the upstream this branch tracked no longer exists, which is the usual aftermath of a merged PR whose remote branch got auto-deleted. Most local cruft is this kind of branch.

First clean up the vanished references with fetch -p, then pick out the locals marked gone and delete them.

git fetch -p && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -D
Deleted branch bugfix/typo (was 884c27d).

If passing -p every time is annoying, configure fetch to prune automatically.

git config --global fetch.prune true

The -D in that command is intentional. A gone branch was merged on the remote, but depending on how it was merged (squash merges especially), it can still look unmerged locally, which would make -d refuse.

Bulk-deleting merged local branches

For branches that don’t end up gone (merged locally, or with auto-delete off), feed the --merged list straight in. Exclude the current branch (*) and any you want to protect.

git branch --merged | grep -Ev '^\*|main|master|develop' | xargs -n 1 git branch -d

Because this uses -d, Git skips anything unmerged that slips through. To preview what would be deleted, drop the xargs part and run just the list.

git branch --merged | grep -Ev '^\*|main|master|develop'

Recovering with reflog

If you nuked an unmerged branch with -D, its last commit is still in the reflog, which records every position HEAD has moved through for 90 days.

$ git reflog
4fc8d7b HEAD@{0}: checkout: moving from feature/user-auth to main
e723b01 HEAD@{1}: commit: Add user authentication feature
559cee6 HEAD@{2}: commit: Create login form
4fc8d7b HEAD@{3}: checkout: moving from main to feature/user-auth

Find the deleted branch’s last commit hash (e723b01) and create a new branch from that point.

git checkout -b feature/user-auth e723b01
$ git log --oneline
e723b01 Add user authentication feature
559cee6 Create login form
4fc8d7b Initial commit

The commits are back. The reflog is local-only and gets cleaned after 90 days, so tag anything you need to keep long-term before deleting it.

git tag archive/feature-user-auth feature/user-authentication