Introduction
A remote-tracking branch is a local ref that mirrors a branch on a remote. They live under refs/remotes/<remote>/ and are updated by git fetch (and operations that imply fetch, like git pull and git clone). You do not commit directly to them.
Where they come from
The default refspec for origin is:
+refs/heads/*:refs/remotes/origin/*
So when the server has refs/heads/main, your clone gets refs/remotes/origin/main. The leading + means "force update".
Listing
git branch -r
git branch -a # local + remote-tracking
git branch -vv # show upstream of locals
Linking a local branch to one
git switch feature/login # auto-creates local from origin/feature/login
git switch -c mine origin/feature/login # explicit
git branch -u origin/main # set upstream of current branch
git branch --unset-upstream
The upstream link drives git status's ahead/behind, and the default targets for git push and git pull.
Pruning
Remote-tracking branches do not vanish when the remote branch is deleted. Prune them:
git fetch --prune
git remote prune origin
git config --global fetch.prune true
The special remote HEAD
Each remote has a symbolic HEAD indicating its default branch:
git symbolic-ref refs/remotes/origin/HEAD
git remote set-head origin --auto # query server
This is what makes origin alone resolve to origin/main (or whatever default).
Diffing against a remote-tracking branch
git fetch
git log HEAD..origin/main --oneline # what is upstream
git log origin/main..HEAD --oneline # what is local-only
git diff @{u} # vs upstream
@{u} (or @{upstream}) is shorthand for the configured upstream of the current branch.
Push behaviour
The default push.default = simple pushes the current branch to its upstream of the same name. If you push a branch with no upstream and have push.autoSetupRemote = true (Git 2.37+), the upstream is created automatically.
Refspec details
The fetch refspec for a remote can be customized to fetch additional refs or to map them differently. For example, to also pull GitHub PR refs and GitLab MR refs:
git config --add remote.origin.fetch '+refs/pull/*/head:refs/remotes/origin/pr/*'
git config --add remote.origin.fetch '+refs/merge-requests/*/head:refs/remotes/origin/mr/*'
The leading + means "force update", needed because PR refs can move (rebases on the PR branch). After fetching, the server's PRs appear as local origin/pr/* refs, ready to git switch -c review/<n> origin/pr/<n>.
Common mistakes
Trying to git checkout origin/main and edit; it puts you in detached HEAD because origin/main is read-only. Create a tracking branch instead. Forgetting to git fetch and being misled by stale ahead/behind counts. Treating remote-tracking branches as if pushing to them updates the server; they are local mirrors. Use git push origin main. Finally, deleting origin/main with git branch -dr and assuming the server branch is gone; it is not. Use git push origin --delete main and only when you mean it.