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

KindExampleWhat it is
Local branchmainYours to commit on.
Remote-tracking branchorigin/mainYour 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 remotemain on GitHubThe real thing on the server — you only see changes to it after fetching.
$
git branch -r
Output
  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
Output
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
Output
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
Output
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
Output
  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
Output
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.