git branch -a lists every branch your local repository knows about, local and remote-tracking. The catch is in that last phrase: it shows what your repository knows, not what the remote currently has. If a branch is missing, you probably need git fetch first — and if a deleted branch is still listed, you need --prune.
Branch listing is straightforward until the list is wrong, and it is wrong more often than people realise because Git caches its view of the remote and never refreshes it on its own. Here are the commands, and the two sync steps that make them accurate.
Table of contents
- The listing commands
- Why a branch is missing from the list
- Pruning branches that no longer exist
- Finding branches worth deleting
- Searching a large branch list
- Keeping the list short in the first place
- How this fits the rest of the stack
- FAQ
The listing commands
git branch # local branches only; * marks current
git branch -r # remote-tracking branches only
git branch -a # both
git branch -v # with last commit SHA and subject
git branch -vv # also shows upstream tracking and ahead/behind
git branch --all --sort=-committerdate # most recently active first
$ git branch -vv
* main a1b2c3d [origin/main] Update dependencies
feature/checkout e4f5g6h [origin/feature/checkout: ahead 2] Add validation
old-experiment i7j8k9l Remove debug logging
hotfix/login m0n1o2p [origin/hotfix/login: gone] Fix session timeout
-vv is the one worth using by default. The bracketed information is the useful part: ahead 2 means you have unpushed commits, and gone means the upstream branch was deleted on the remote — that branch is a cleanup candidate.
The --sort=-committerdate flag is what turns a wall of forty branch names into something readable, with the active work at the top.
Why a branch is missing from the list
Remote-tracking branches are a local cache. git branch -r reads that cache; it does not contact the server.
# Refresh the cache from the remote
git fetch origin
# Refresh from every configured remote
git fetch --all
# Ask the remote directly, bypassing the cache entirely
git ls-remote --heads origin
# Richer view: which branches are tracked, stale, and local-only
git remote show origin
git ls-remote is the definitive answer to what branches exist on the server right now, because it queries the remote rather than reading anything local. When someone says a branch is there and you cannot see it, this settles it in one command.
git remote show origin is the friendlier version — it lists remote branches, which are tracked locally, and which local branches are configured to push where. It is slower because it contacts the network, and it is the best single overview.
Pruning branches that no longer exist
The mirror-image problem: a colleague deleted a merged branch weeks ago and it is still in your git branch -r output. Fetch does not remove stale references by default.
# Fetch and remove remote-tracking refs whose branches are gone
git fetch --prune
# Preview what pruning would remove
git remote prune origin --dry-run
git remote prune origin
# Make it automatic -- do this once and forget about it
git config --global fetch.prune true
Set fetch.prune globally. There is essentially no downside, and it keeps your branch list honest without you thinking about it. Most people’s inflated branch lists are stale references rather than real branches.
Pruning only removes remote-tracking references. Your own local branches survive, which is correct — Git will not delete your work because someone deleted the remote copy.
Finding branches worth deleting
Once the list is accurate, the useful question is which branches are finished.
# Local branches already merged into main
git branch --merged main
# Not yet merged -- do not delete these without looking
git branch --no-merged main
# Remote branches already merged
git branch -r --merged main
# Branches whose upstream was deleted (the 'gone' ones)
git branch -vv | grep ': gone]'
Cleanup, with a safety property worth noting:
# Delete local branches merged into main, excluding main itself
git branch --merged main | grep -vE '^\*|main|master|develop' | xargs -r git branch -d
# Delete the ones whose upstream is gone
git branch -vv | awk '/: gone]/ {print $1}' | xargs -r git branch -d
# Delete a remote branch
git push origin --delete feature/old-thing
Use -d, not -D. Lowercase refuses to delete a branch with unmerged commits; uppercase forces it. The refusal is the feature — it is the only thing standing between a cleanup one-liner and losing work.
If you do delete something by mistake, the reflog still has it:
git reflog | grep 'feature/deleted-branch'
git branch feature/deleted-branch <sha>
Note that --merged is evaluated against the branch you name. A branch merged into develop but not main will not appear in --merged main, which is correct and occasionally surprising.
Searching a large branch list
On a repository with hundreds of branches, for-each-ref gives you formatting and filtering that git branch does not.
# Branches by last commit date, oldest first, with author
git for-each-ref --sort=committerdate refs/remotes/ \
--format='%(committerdate:short) %(authorname) %(refname:short)'
# Anything not touched in six months
git for-each-ref --sort=committerdate refs/remotes/ \
--format='%(committerdate:iso8601) %(refname:short)' \
| awk -v cutoff="$(date -d '6 months ago' +%Y-%m-%d)" '$1 < cutoff'
# Pattern match
git branch -a --list '*checkout*'
git branch -r --list 'origin/release/*'
That second command is the one to run before a branch cleanup meeting. A list of branches nobody has touched in six months, with the author’s name attached, makes the conversation short.
The web interfaces have equivalents — GitHub’s branch list sorts by activity and flags which are merged — but the command line versions script, and cleanup is a scripting job.
Keeping the list short in the first place
Branch sprawl is a process symptom. A few settings remove most of it without anybody doing cleanup work.
- Enable automatic branch deletion on merge. Both GitHub and GitLab offer this and it should be on by default. It eliminates the most common source of stale branches entirely.
- Set
fetch.prune = trueglobally so everyone’s local view stays accurate. - Keep branches short-lived. A branch open for three weeks accumulates conflicts and stops being obviously finished.
- Use a naming convention —
feature/,fix/,release/— so pattern-based listing and cleanup work at all.
There is an operational angle too. Many deployment setups create a preview environment per branch, and a repository with two hundred stale branches can mean two hundred environments consuming resources for work that shipped last quarter.
Deleting the branch should tear down what it created. If it does not, branch hygiene stops being cosmetic and starts showing up on the bill. Worth checking how your deployment maps branches to environments — the GitHub deployment documentation covers that mapping on RunxBuild.
How this fits the rest of the stack
git branch -a after a git fetch --prune gives you an accurate list; -vv tells you which branches are ahead, behind, or orphaned. Set fetch.prune globally, turn on delete-on-merge in your hosting platform, and use -d rather than -D so Git can refuse when you are about to lose something. If you are looking at deployments where branches map to environments, the RunxBuild hosting calculator shows the services as separate line items.
Useful related references:
- Linux tee: See the Output and Save It at the Same Time
- Linux See Processor Usage: top, htop, mpstat, and Reading Load Average
- Mongo List DBs: Every Way to See the Databases, the Sizes, and the Ones the User Can Actually See
- Deploying from GitHub on RunxBuild
FAQ
How do I see all branches including remote ones?
Run git branch -a. Add git fetch first, because remote-tracking branches are a local cache that Git does not refresh automatically — a branch created on the remote will not appear until you fetch.
Why does git branch -r show branches that were deleted?
Fetch does not remove stale remote-tracking references by default. Run git fetch —prune, or set fetch.prune to true globally so it happens on every fetch.
What does gone mean in git branch -vv output?
The upstream branch this local branch tracked has been deleted on the remote, usually because it was merged. Those branches are safe cleanup candidates.
How do I list branches on the remote without fetching?
Use git ls-remote —heads origin, which queries the server directly rather than reading your local cache. git remote show origin gives a friendlier summary of the same information.
What is the difference between git branch -d and -D?
Lowercase -d refuses to delete a branch containing unmerged commits. Uppercase -D forces the deletion. Use -d so Git can stop you from losing work.