Git
Branch Tracking
Remote Repositories
Upstream Branches
Programming Tools

How can I see which Git branches are tracking which remote / upstream branch?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Run git branch -vv to see every local branch alongside its upstream tracking branch, latest commit, and ahead/behind status. This is the fastest single command to answer "what tracks what" across your entire repository.

bash
git branch -vv
text
1  develop  a1b2c3d [origin/develop: ahead 2] Add user service
2  feature  e4f5g6h [origin/feature: behind 1] Update config
3* main     i7j8k9l [origin/main] Merge pull request #42
4  stale    m0n1o2p Fix typo

Branches with [origin/...] have an upstream configured. The stale branch in this example has no upstream, meaning it is a purely local branch. The rest of this article covers every method for inspecting tracking relationships, how to set or change them, and how to diagnose common misconfigurations.

Understanding Tracking Relationships

A tracking relationship links a local branch to a remote-tracking reference (like origin/main). When this relationship exists, Git provides three conveniences:

  1. git pull knows which remote branch to fetch and merge without you specifying it.
  2. git push knows where to push without explicit arguments (depending on push.default config).
  3. git status shows "ahead N / behind M" relative to the upstream.

Without a tracking relationship, you must specify the remote and branch explicitly for push and pull operations.

Tracking is stored in .git/config as two entries per branch:

ini
[branch "main"]
    remote = origin
    merge = refs/heads/main

The remote value identifies which remote to interact with, and merge identifies which branch on that remote.

Method 1: git branch -vv

This is the go-to command for a quick overview:

bash
git branch -vv

The output columns are:

ColumnMeaning
Branch nameYour local branch
Commit hashLatest commit on that branch
[remote/branch]The upstream tracking branch (if configured)
Ahead/behind statusHow many commits differ from the upstream
Commit subjectFirst line of the latest commit message

To include remote-tracking branches in the listing:

bash
git branch -avv

This adds lines like remotes/origin/main to the output, showing all references in the repository.

Method 2: git remote show <remote>

For detailed information about a specific remote, including which local branches track which remote branches:

bash
git remote show origin
text
1* remote origin
2  Fetch URL: [email protected]:user/repo.git
3  Push  URL: [email protected]:user/repo.git
4  HEAD branch: main
5  Remote branches:
6    develop tracked
7    main    tracked
8  Local branches configured for 'git pull':
9    develop merges with remote develop
10    main    merges with remote main
11  Local refs configured for 'git push':
12    develop pushes to develop (up to date)
13    main    pushes to main    (fast-forwardable)

This command contacts the remote to check status, so it requires network access. It is particularly useful when you work with multiple remotes (e.g., origin and upstream in a fork workflow) and want to see the full picture for one remote at a time.

Method 3: git config Queries

You can query tracking information for a specific branch directly from the Git config:

bash
1# Get the remote for a branch
2git config branch.main.remote
3# origin
4
5# Get the merge ref for a branch
6git config branch.main.merge
7# refs/heads/main

To list all branch tracking entries at once:

bash
git config --get-regexp 'branch\..*\.(remote|merge)'
text
1branch.main.remote origin
2branch.main.merge refs/heads/main
3branch.develop.remote origin
4branch.develop.merge refs/heads/develop

This is useful in scripts where you need to programmatically determine tracking relationships.

Method 4: git for-each-ref

For machine-parseable output, git for-each-ref with a custom format is the most reliable option:

bash
git for-each-ref --format='%(refname:short) -> %(upstream:short)' refs/heads
text
1develop -> origin/develop
2feature ->
3main -> origin/main
4stale ->

Branches without an upstream show an empty value after the arrow. This format is ideal for piping into other tools or scripts:

bash
# Find all branches without an upstream
git for-each-ref --format='%(refname:short) %(upstream:short)' refs/heads \
  | awk '$2 == "" { print $1 }'

You can also include ahead/behind counts:

bash
git for-each-ref \
  --format='%(refname:short) -> %(upstream:short) [ahead %(upstream:trackshort)]' \
  refs/heads

Method 5: Inspecting .git/config Directly

The raw configuration file shows tracking relationships in their stored form:

bash
cat .git/config

Look for [branch "..."] sections:

ini
1[branch "main"]
2    remote = origin
3    merge = refs/heads/main
4[branch "develop"]
5    remote = origin
6    merge = refs/heads/develop
7[branch "feature"]
8    remote = upstream
9    merge = refs/heads/feature

This is the source of truth that all other commands read from. Editing it manually is possible but error-prone; use git branch --set-upstream-to instead.

Comparison of Methods

MethodNetwork RequiredMachine ParseableShows All BranchesShows Ahead/Behind
git branch -vvNoNoYesYes
git remote show <remote>YesNoPer remoteYes
git config --get-regexpNoYesYesNo
git for-each-refNoYesYesYes (with format)
cat .git/configNoNoYesNo

Setting and Changing Upstream Tracking

Setting Upstream on an Existing Branch

bash
git branch --set-upstream-to=origin/develop develop
# or the shorter form
git branch -u origin/develop develop

Setting Upstream During Push

When pushing a new branch for the first time, use -u to set the upstream simultaneously:

bash
git push -u origin feature

This creates the remote branch and configures the local branch to track it in one step.

Removing an Upstream

To unset a branch's tracking relationship:

bash
git branch --unset-upstream develop

After this, git branch -vv will no longer show a tracking reference for that branch.

Changing to a Different Remote

If you need to switch a branch from tracking origin to tracking upstream (common in fork workflows):

bash
git branch -u upstream/main main

Diagnosing Common Tracking Issues

"Your branch is based on 'origin/feature', but the upstream is gone"

This means the remote branch was deleted but your local tracking reference still points to it. Fix it by updating remote references and optionally resetting the upstream:

bash
1git fetch --prune origin
2git branch -u origin/main feature   # if the branch was merged to main
3# or
4git branch --unset-upstream feature  # if the branch is purely local now

Push Rejected Despite Tracking Being Set

If push.default is set to simple (the default since Git 2.0), Git refuses to push if the local branch name differs from the remote branch name, even with a tracking relationship. Either rename the local branch or set the push config:

bash
1# Option 1: Push to differently named upstream
2git push origin local-name:remote-name
3
4# Option 2: Change push.default
5git config push.default upstream

No Tracking After Clone

Normally, git clone sets up tracking for the default branch automatically. If other branches are not tracking, it is because Git only creates tracking for the branch you check out, not for all remote branches. To check out and track a remote branch:

bash
git checkout -b develop origin/develop
# or in Git 2.23+
git switch -c develop origin/develop

Common Pitfalls

Assuming all branches track automatically. Only the branch created by git clone or checked out from a remote has automatic tracking. New branches created with git checkout -b from a local ref do not track anything by default.

Confusing remote-tracking branches with remote branches. A "remote-tracking branch" like origin/main is a local reference that caches the state of the remote. It is updated by git fetch, not in real time. The actual remote branch may have moved since your last fetch.

Forgetting to fetch before checking status. The ahead/behind count shown by git branch -vv is based on the last git fetch. If you have not fetched recently, the numbers may be stale. Run git fetch --all first for accurate counts.

Using git remote show in scripts. This command contacts the remote over the network, which makes it slow and unreliable in automated pipelines. Use git for-each-ref or git config queries instead.

Editing .git/config by hand. While it works, manually editing branch tracking entries is error-prone. A typo in the merge ref will silently break tracking. Use git branch -u for safe modifications.

Summary

  • git branch -vv is the fastest way to see all tracking relationships interactively.
  • git remote show origin provides a detailed view per remote but requires network access.
  • git for-each-ref and git config --get-regexp are the best choices for scripting.
  • Set upstream tracking with git branch -u or git push -u when pushing a new branch.
  • Remote-tracking branches are local caches updated by git fetch, not live mirrors.
  • Always fetch before relying on ahead/behind counts for accuracy.

Course illustration
Course illustration

All Rights Reserved.