Handcrafting Codes← Index

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

Jun 29, 2026 / 3 min read / By the author

COVER IMAGE — 16:9

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  │                  │
 └───────────────────────────┘        └──────────────────┘
  1. The local branch feature, the one you check out and commit to.
  2. The remote branch feature on origin, the one your teammates see.
  3. 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.