Remote Branches and Tracking
Your repository keeps a read-only record of each branch on the remote, such as origin/main. Understanding these remote-tracking branches — and the link between a local branch and its upstream — explains every "ahead" and "behind" message Git shows you.
Three kinds of branch
| Kind | Example | What it is |
|---|---|---|
| Local branch | main | Yours to commit on. |
| Remote-tracking branch | origin/main | Your copy of where the remote's branch was at your last fetch, pull or push. You can't commit on it; Git moves it when you talk to the server. |
| Branch on the remote | main on GitHub | The real thing on the server — you only see changes to it after fetching. |
git branch -r
origin/HEAD -> origin/main origin/breads origin/main origin/soups
-r lists remote-tracking branches, -a lists everything. origin/HEAD just records which branch is the remote's default.
Upstreams and the -vv view
A local branch can have an upstream — the remote-tracking branch it's compared with, and that git pull and git push use by default. git branch -vv shows each branch's upstream and how far apart they are:
git switch soups echo "Tomato soup: tomatoes, onion, basil" > tomato-soup.txt git commit --quiet -am "Add basil to tomato soup" git branch -vv
branch 'soups' set up to track 'origin/soups'. Switched to a new branch 'soups' main 6b13d64 [origin/main] Add pancake recipe * soups ffe1943 [origin/soups: ahead 1] Add basil to tomato soup
[origin/soups: ahead 1] means one local commit hasn't been pushed. "behind 2" would mean two commits on the remote haven't been pulled; "ahead 1, behind 2" means the branches have diverged. Remember these counts are only as fresh as your last fetch — run git fetch first for an up-to-date picture.
Notice how git switch soups created a local branch with the upstream already set: when a remote-tracking branch with that name exists, Git does this automatically.
Setting or changing the upstream
git switch --quiet -c soups-v2 git branch --set-upstream-to=origin/soups git status -sb
branch 'soups-v2' set up to track 'origin/soups'. ## soups-v2...origin/soups [ahead 1]
The short form is git branch -u origin/soups. To remove the link, use git branch --unset-upstream. Usually you set the upstream with the first git push -u instead.
Shorthands
@{u} (or @{upstream}) means "the upstream of the current branch". Handy for checking what's in and out:
git log --oneline @{u}.. # commits I have that the upstream doesn't (to push)
git log --oneline ..@{u} # commits the upstream has that I don't (to pull)
git diff @{u} # how my working tree differs from the upstream
Deleting remote branches
When a branch has been merged, delete it on the server:
git push origin --delete breads
To https://github.com/sam-rivera/recipes.git - [deleted] breads
This removes it from GitHub and from your own remote-tracking branches. But teammates still have their origin/breads until they prune.
Pruning stale branches
Alex deleted the soups branch on GitHub. A normal fetch doesn't remove Sam's now-stale origin/soups:
git fetch git branch -r
origin/HEAD -> origin/main origin/main origin/soups
--prune (-p) removes remote-tracking branches that no longer exist on the server:
git fetch --prune git branch -vv
From https://github.com/sam-rivera/recipes - [deleted] (none) -> origin/soups main 6b13d64 [origin/main] Add pancake recipe * soups ffe1943 [origin/soups: gone] Add basil to tomato soup
Sam's local soups branch is still there — pruning never deletes local branches — but its upstream is marked gone. That's a hint it's probably merged and can be deleted. Make pruning automatic with:
git config --global fetch.prune true
Checking out a teammate's branch
To try a branch someone else pushed: fetch, then switch to it by name.
git fetch
git switch feature/new-search # creates a local branch tracking origin/feature/new-search
If the same branch name exists on more than one remote, be explicit: git switch -c feature/new-search --track upstream/feature/new-search.
Try it yourself
In a cloned repository, create a branch, push it with -u, make one more commit and check git branch -vv shows "ahead 1". Then delete the branch on the remote, run git fetch --prune, and look for gone in git branch -vv.