Engineering
Three branches, not one: deleting branches in git without the confusion
The reason deleting a git branch confuses people is that "the branch" isn't one thing
title: "Three branches, not one: deleting branches in git without the confusion" date: 2026-06-26 tags: [git, branches, remotes] draft: true
The reason deleting a git branch confuses people is that "the branch" isn't one thing. When you've been working with a remote, a single branch name refers to three separate objects, and the commands that delete them are all different. Delete the wrong one and the branch looks gone from where you're standing while it's still very much alive somewhere else.
Here are the three:
your machine the server (origin)
┌───────────────────────────┐ ┌──────────────────┐
│ feature (1) │ │ │
│ local branch │ │ feature (2) │
│ │ │ remote branch │
│ origin/feature (3) │◀──────▶│ │
│ remote-tracking branch │ sync │ │
└───────────────────────────┘ └──────────────────┘
- The local branch
feature, the one you check out and commit to. - The remote branch
featureonorigin, the one your teammates see. - The local remote-tracking branch
origin/feature, your machine's cached pointer to where the remote branch was last time you talked to the server.
That third one is the trap. It lives on your machine but it represents the remote, and deleting it changes nothing on the server. People run a command that removes origin/feature locally, see the name disappear, and assume the branch is deleted. It isn't. They just threw away their bookmark to it.
Deleting the local branch
The branch you've been working on:
git branch -d feature # refuses if it isn't merged
git branch -D feature # force, even if unmerged
Use lowercase -d by default. It's a safety check that refuses to delete a branch whose commits aren't merged anywhere, which is git stopping you from silently dropping work. Reach for -D only when you mean to discard unmerged commits, and know that's what you're doing.
Deleting the remote branch
The one on the server, the one that matters to everyone else:
git push origin --delete feature # modern
git push origin -d feature # shorter
git push origin :feature # old syntax, pre-1.7.0
That last form, pushing an empty source to a remote branch, is the historical way and still works. The --delete spelling is clearer about intent, so prefer it.
There's a convenience here worth knowing: deleting the remote branch this way also removes your local origin/feature tracking reference automatically. One command cleans up two of the three. So in practice, git push origin --delete feature followed by a local git branch -d feature handles the whole thing.
Deleting just the stale tracking reference
You only need this when the remote branch is already gone but your machine never got the memo, usually because someone deleted it through the web UI instead of from the command line. Now you've got an origin/feature pointing at a branch that no longer exists.
Clear the stale ones in bulk:
git fetch origin --prune # or -p
This removes every remote-tracking reference whose remote branch is gone. It's the cleanup you want after branches get deleted server-side. You can also target one by hand without a network round-trip:
git branch -dr origin/feature
If you want pruning to happen on every fetch without thinking about it, set it once:
git config --global fetch.prune true
Checking your work
After any of this, two commands tell you what's really left:
git branch -r # remote-tracking branches only
git branch -a # local and remote-tracking together
The confusion goes away the moment you stop thinking "delete the branch" and start thinking "which of the three." Local branch, git branch -d. Remote branch, git push origin --delete, which sweeps the tracking ref along with it. Orphaned tracking ref left over from someone else's cleanup, git fetch -p. Same name, three objects, three commands.