Git

What Is Upstream in Git? Explained with Examples

What Is Upstream in Git? Explained with Examples

Run git pull in a fresh clone and it just works, because Git already linked the default branch to its remote counterpart. That link is the upstream, a tracking link between a local branch and one branch on a remote. Commands like git pull and git push read it when you give them no arguments, so they know where to fetch from and where to send work.

You meet it again the first time a new branch needs pushing, and that’s usually when the error messages start.

Plenty of tutorials use the word for the original repository behind a fork, which is a different thing. The git-push manual describes something narrower. It’s a per-branch setting, stored as remote and merge entries in .git/config, and it also drives the ahead and behind counts in git status.

A remote named upstream is only a naming habit layered on top of that.

What Is Upstream in Git?

YouTube player

The upstream sits on a local branch and points at one branch on a remote. Git consults it whenever git pull or git push runs without arguments, which saves you from naming the remote and branch every time.

The branch on the far end is normally a remote-tracking branch such as origin/main. Git also calls the link “tracking information”.

That remote-tracking branch is a local reference recording where a branch on the remote repository stood at your last fetch. Point a local branch at it and you have an upstream.

How the terms fit together

YouTube player

Pro Git calls the local branch that has an upstream configured the tracking branch, and the branch it follows the upstream branch. The local copy of a remote branch, origin/main for example, is a remote-tracking branch, and it normally fills the upstream role.

Tutorials tend to blur all of this.

One blur matters more than the rest. Several popular guides define upstream as the original repository a fork came from. That’s a naming habit for a second remote, not the definition in the git-push manual.

Why is GitHub the heart of open source?

Uncover GitHub statistics: developer community growth, repository trends, collaboration patterns, and the platform that powers modern software development.

Explore GitHub Data →

Where Git stores the upstream

Two entries in .git/config hold it, under a section named after the local branch. The git-push manual shows the layout for a main branch that tracks origin/main.

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

remote is just the remote’s name. merge holds the branch on that remote, and it’s written as a full ref, not as origin/main.

When Git sets the upstream for you

Git sets the upstream for the default branch by itself when you are cloning a repository.

Checking out a branch name that matches a remote-tracking branch creates a local branch and links it too. And if push.autoSetupRemote is set to true, the first git push of a branch sets the link.

A branch created from a local starting point gets nothing. branch.autoSetupMerge defaults to true, and in that mode Git tracks only when the start point is a remote-tracking branch (git-branch manual, Git 2.56.0).

Looking to sharpen your Git skills? Branching, merging, rebasing, and everything else you need - including git rebase, git stash, and commit workflows - is on one page in the Git Cheat Sheet.

How Does the Upstream Change git pull, git push and git status?

YouTube player

Once an upstream exists, plain git pull, git push and git fetch pick the remote and branch without help, and git status reports how far the local branch has drifted from it.

git pull and git fetch

Both commands read branch.<name>.remote to choose the remote. Run git pull with no arguments and Git fetches from that remote, then reads branch.<name>.merge to decide what to merge or rebase onto.

Fetch alone stops earlier. It updates the remote-tracking branch and leaves your local branch where it was.

git push

With no repository argument, git push goes to the upstream remote of the current branch, or to origin if no upstream exists.

push.default=simple has been the default since Git 2.0, and it adds a rule. When you push to the same remote you pull from, the upstream must carry the same name as the local branch, or Git refuses the push.

▶ GIT EXPLAINED

If you commit, branch, or merge code every day, it helps to know how Git came to be. This video covers how Linus Torvalds created Git in just 10 days after a license dispute and explains how its data model actually works.

Linus Torvalds Built Git in Days Out of Spite
Built in 10 Days Out of Spite. Git and its data model explained in plain language.

So a local login-fix tracking origin/develop can’t use a bare git push under the default setting.

git status and git branch -d

Plain git status compares the current branch against its upstream and prints the ahead and behind counts. The --no-ahead-behind option skips the detailed counts, while the default is to show them.

git branch -d reads the upstream too. It demands that the branch is fully merged into its upstream, and falls back to HEAD only when no upstream was set.

The @{upstream} shorthand

@{upstream}, or @{u}, resolves to the remote-tracking branch behind the current branch. Put a branch name in front and it resolves that branch instead, as in master@{upstream}.

The gitrevisions manual shows the result for a branch tracking origin/master:

$ git rev-parse --symbolic-full-name @{upstream}
refs/remotes/origin/master

Pro Git uses the same shorthand to shorten merges, so git merge @{u} stands in for git merge origin/master.

@{push} is a sibling. It reports where git push would send the branch, and it only differs from @{upstream} in a triangular workflow where you pull from one remote and push to another.

How to Set an Upstream Branch in Git

YouTube player

Run git push -u origin <branch> to push a new branch and set its upstream in one step. If the remote counterpart already exists, git branch --set-upstream-to=origin/<branch> links the pair instead.

CommandPushes commitsRemote branch must existUse it when
git push -u origin <branch>YesNo, it creates itFirst push of a new local branch
git branch -u origin/<branch>NoYes, and fetched locallyLinking a branch pair that already exists
git checkout --track origin/<branch>NoYes, and fetched locallyStarting a local branch from a remote one

With --track, git checkout creates the local branch and the link together. Pro Git demonstrates it with git checkout --track origin/serverfix.

For a brand new branch, the sequence is short.

  1. Create the branch with git switch -c feature/login
  2. Commit your work
  3. Push and link with git push -u origin feature/login
  4. Confirm the link with git branch -vv

The -u flag also works on a branch that is already up to date on the remote. The git-push manual says it adds the upstream reference for every branch that is up to date or successfully pushed.

--set-upstream-to fails when origin/<branch> doesn’t exist locally yet. Run git fetch first, or let push -u create the remote branch.

The older git branch --set-upstream is gone. The git-branch manual (Git 2.56.0, 28 September 2026) states it is no longer supported and points to --track or --set-upstream-to.

I use push -u for branches I create myself. branch -u is for the cases where a teammate already pushed the remote branch.

How to Check, Change or Remove the Upstream

git branch -vv lists every local branch with its upstream, and git rev-parse --abbrev-ref @{u} prints the upstream of the current branch.

Check the upstream

For the current branch, git rev-parse --abbrev-ref @{u} prints a name like origin/main. For every branch at once, use git branch -vv.

The doubled v matters. A single -v shows the commit hash, the commit subject and the ahead and behind relationship, and -vv adds the upstream name (git-branch manual).

On a branch with no upstream, the rev-parse command exits with an error instead of printing an empty line.

Change the upstream

git branch -u origin/other-branch repoints the current branch. Git rewrites the two config entries, and pull and push follow the new target.

Editing the entries with git config gets you the same result but skips the check that the target branch exists.

One catch. Pointing a branch at an upstream with a different name triggers the same-name rule from the earlier push section, so a bare git push stops working under simple.

Remove the upstream

git branch --unset-upstream clears the tracking information for the current branch. Pass a branch name to clear another one.

Afterwards the fallbacks apply. git branch -d checks merges against HEAD, and git push defaults to origin as the remote but can fail outright depending on push.default.

Upstream vs Origin: What Is the Upstream Remote in a Fork Workflow?

YouTube player

origin is simply what git clone calls the remote you cloned from. The fork convention borrows the word upstream for the original repository, but Git attaches no special meaning to either name.

TermWhat it isWho creates it
originA remote namegit clone, by default
upstream remoteA remote name that points at the original repositoryYou, with git remote add
Upstream branchThe tracking link stored on one local branchGit or you, branch by branch

The first two are names for remotes, while the upstream branch is a link stored on one local branch.

Why the name upstream is only a convention

Git never looks for a remote called upstream. The origin remote gets the same treatment: Git 2.30 (27 December 2020) added clone.defaultRemoteName, and git clone --origin sets the name per clone.

So a repository can have no remote called origin at all, and nothing about tracking changes.

A local branch can point at either remote. If main tracks upstream/main, then branch.main.remote reads upstream, and a bare git pull fetches from the original repository.

How to sync a fork with the upstream remote

Until you add the second remote, a fork on GitHub has no way to bring the original project’s new commits into your clone.

  1. Add the remote with git remote add upstream https://github.com/ORIGINAL-OWNER/ORIGINAL-REPOSITORY.git
  2. Verify it with git remote -v, which now lists origin and upstream, two lines each (GitHub Docs shows the same output)
  3. Run git fetch upstream
  4. Check out main, then run git merge upstream/main (or git rebase upstream/main)
  5. Push the result to your fork with git push origin main

The explicit upstream/main in step 4 is deliberate. After a clone, main tracks origin/main, so a bare git pull never reaches the original repository.

Rebasing has a price. A branch you already pushed becomes a non-fast-forward update after the rebase, and the git-push manual says Git rejects those unless you force them.

Which Settings Set the Upstream Automatically?

push.autoSetupRemote covers the first plain push and branch.autoSetupMerge covers branch creation. push.default only decides what a plain push does, and never sets anything.

SettingDefaultSets the upstream when
push.autoSetupRemoteOffA plain push runs on a branch that has none
branch.autoSetupMergetrueA branch starts from a remote-tracking branch
push.defaultsimpleNever, it only picks the push target

push.autoSetupRemote

Turn it on once, globally:

git config --global push.autoSetupRemote true

After that, a bare git push on an untracked branch behaves as if you had typed --set-upstream. The git-push manual limits this to the simple, upstream and current values of push.default.

It does nothing for pushes that name a refspec, such as git push origin feature, or when push.default is matching or nothing. An upstream-mode push to a remote other than the branch’s own is also out, since Git refuses it before any upstream is set.

The git-push manual says the workflows most likely to benefit are simple central ones, the kind of Git workflow where every branch keeps its name on the remote. That fits why the option ships turned off.

Adam Johnson’s write-up dates the option to Git 2.37, released 27 June 2022. The 2.37 release notes don’t list it, so the version number comes from write-ups and not from the notes.

branch.autoSetupMerge

The default true was covered earlier. The git-branch manual lists four other values.

Set it to false and nothing gets tracked automatically. always tracks whether the start point is a local or a remote-tracking branch. inherit copies the start point’s tracking setup onto the new branch, and simple tracks only when the new branch has the same name as the remote branch.

The 2.37 release notes introduce simple and tie it to push.default=simple, since both insist on matching names. Under it, a local work branch started from origin/feature gets no upstream.

push.default

The manual lists nothing, current, upstream, simple and matching, plus tracking as a deprecated synonym for upstream.

matching was the default before Git 2.0 and pushes every branch that exists on both ends.

Pick upstream only when you push to the same repository you pull from. In any other setup, Git refuses and says the remote is not the upstream of the current branch.

Why Does Git Say “Current Branch Has No Upstream Branch”?

YouTube player

Git raises that error when git push runs on a branch with no tracking entries and push.default needs them. The fix is the command Git prints under the error: git push --set-upstream origin <branch>.

The message is hard-coded in Git’s builtin/push.c. It names the branch and the remote, prints the command to run, and points at push.autoSetupRemote when that option is off.

Only two push.default modes can raise it when you run git push with no arguments. simple and upstream need an upstream, while current pushes a same-named branch without one.

SymptomCauseFix
“has no upstream branch”No remote and merge entries, mode simple or upstreamgit push -u origin <branch>
Upstream marked gone in git branch -vvRemote branch deleted and its remote-tracking branch prunedgit branch --unset-upstream, or git branch -u to a live branch
“You are not currently on a branch”Detached HEAD, so no branch holds an upstreamgit switch -c <name>, then git push -u
“upstream branch … does not match the name”Mode simple, upstream named differentlygit push origin HEAD:<upstream-name> or git push origin HEAD
“multiple upstream branches, refusing to push”Several branch.<name>.merge valuesKeep one value

The git-for-each-ref manual explains the gone marker. Its :track output prints [gone] whenever the upstream ref is unknown. Running git remote prune origin or git fetch --prune deletes stale remote-tracking branches, which is how most branches end up there.

Detached HEAD is the odd one out. With a detached HEAD, Git has no current branch to read tracking entries from, so its message suggests git push <remote> HEAD:<name-of-remote-branch> instead.

Turning on push.autoSetupRemote removes the first row for good. The other four rows stay.

Upstream in Git FAQ

What is the difference between upstream and downstream in Git?

Anything that receives work from an upstream sits downstream of it. The terms are relative, so your clone is downstream of the remote it pulls from, and that remote is downstream of any repository it was forked from.

Can a local branch use another local branch as its upstream?

Yes. Run git branch -u main on a feature branch and main becomes its upstream. The git-branch manual stores this with a dot as the remote, meaning the current repository.

Can a branch have more than one upstream?

Not in practice. The merge entry accepts several values for octopus merges, but git push then stops with “multiple upstream branches, refusing to push”.

Does deleting a local branch delete its upstream?

No, the remote branch stays untouched. Git only drops the tracking entries that belonged to the deleted local branch, and you remove the remote copy separately with git push origin --delete <branch>.

Which Remote Should a Fork’s Feature Branch Track?

Point a fork’s feature branch at its own copy on origin, and keep the upstream remote for fetching only.

A branch started from upstream/main tracks that branch automatically, so a bare git push aims at the original repository and trips the same-name check.

The right order avoids this.

  • Fetch upstream, then branch from upstream/main
  • Run git push -u origin feature to move the link to your fork
  • Open the pull request from the fork

There is a cost. git status now measures the branch against your fork, so it stays silent when the original project moves. Only a fresh git fetch upstream shows that drift.

Once the branch sits on your fork, opening a pull request on GitHub is the next step.

This advice was checked against the Git 2.56.0 manuals on 1 October 2026, and a change to the branch.autoSetupMerge default would alter the first step.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.