How to delete a Git branch locally and remotely

A branch exists in up to three places: on your machine, on the remote, and as a remote-tracking ref that remembers what the remote looked like last time you asked. Deleting it properly means dealing with all three.

The two commands

$ git branch -d feature                 # local
$ git push origin --delete feature      # remote

They are genuinely independent. After the first one:

local  : main
remote : feature main   <- still there

Deleting the local branch tells the remote nothing at all.

-d is the one that protects you

git branch -d refuses if the branch holds commits that are not merged anywhere else:

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

-D is the same command with the check switched off. Use -d by default and let it stop you; it is the only thing standing between a typo and losing a branch’s worth of work.

Even -D is usually recoverable

If you do force it, the commits are not gone — the branch was only a pointer to them:

the deleted branch tip was 1602fda
$ git branch recovered 1602fda
1602fda unmerged work

The hash is in git reflog, and git branch <name> <hash> puts a pointer back. Unreferenced commits are garbage collected eventually, so this is a rescue, not a filing system.

Where stale tracking refs actually come from

You will read that you should run git fetch --prune after deleting a remote branch. That is not quite right, and it is worth knowing which case is which.

If you delete it yourself, git cleans up the tracking ref as part of the push. Nothing to prune.

If a colleague deletes it, your origin/<name> survives — even after a fetch:

after they deleted it and we fetched:
  remote really has : main
  we still list     : origin/gone origin/main   <- stale

after 'git fetch --prune':
  we now list       : origin/main

A plain git fetch adds and updates refs but does not remove them, so a branch someone else deleted lingers in your git branch -r output indefinitely. That is what --prune is for.

If you would rather not think about it, make it the default:

$ git config --global fetch.prune true

Deleting many at once

Branches that are already merged into your current branch:

$ git branch --merged | grep -v '\*' | xargs -r git branch -d

-d rather than -D is deliberate: if --merged is wrong about something, you want the refusal rather than the deletion. Check the list before piping it anywhere.

About Netcup (advertisement)

The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).