Multiple Remotes & Upstream Tracking

Manage multiple remotes and upstream tracking in Git. Hands-on guide for developers learning step by step — practical exercises, troubleshooting, and next steps.

Focus: manage multiple remotes and upstream tracking

Sponsored

You cloned a repo, pushed a few branches, and life was good. Then you forked a project on GitHub, added the original as a second remote, and suddenly git push fails with fatal: The current branch has no upstream branch. Or you’re juggling an internal mirror and a public GitHub repo, and you push to the wrong one by accident. Managing multiple remotes and upstream tracking is the difference between a Git setup that feels like a superpower and one that sends you scrambling for git log to figure out where your commits actually went. This lesson gives you a precise mental model and hands-on commands to tame multiple remotes, so you can push, pull, and collaborate without fear.

The problem this lesson solves

You’ve probably felt this pain: you’re working on a feature branch, you run git push, and Git refuses, telling you the branch has no upstream. Or you type git pull and changes come from a remote you didn’t expect. Worse, you add a second remote for a fork or a mirror, and now you mix up which remote is which — pushing work to the wrong place or pulling stale code. These aren’t just annoyances; they cause wasted time, broken builds, and embarrassing force-pushes. The root cause is that Git’s remotes are just labels for URLs, and upstream tracking is the invisible link that tells Git which remote branch your local branch should talk to by default. Once you understand both, you can control exactly what happens on every push, pull, and fetch.

Core concept / mental model

Think of your Git repo as a workshop. Each remote is a door to a different warehouse — one door labeled origin might lead to your team’s shared server, another labeled upstream leads to the original open-source project, and a third labeled backup leads to a private mirror. Each door has an address (the remote URL), and each local branch has a preferred door — that’s upstream tracking. When you say git push without arguments, Git uses that preferred door. If you haven’t set it, Git has no idea which door you mean, so it fails. In Git terms, a remote is a named reference to a URL, and an upstream is the default remote-tracking branch associated with a local branch. Remote-tracking branches look like origin/main and live in your repo as read-only snapshots of what’s on the remote. Upstream tracking tells Git: "This local main should follow origin/main by default." It’s a one-to-one mapping, but you can change it anytime with git branch --set-upstream-to.

How it works step by step

  1. List existing remotes — Use git remote -v to see every remote name and URL. This is your starting map.

  2. Add a new remote — Use git remote add <name> <url> to create a new door. The name is local; it can be anything (e.g., upstream, backup, fork). The URL is typically HTTPS or SSH.

  3. Fetch from any remote — Use git fetch <remote> to update your remote-tracking branches (e.g., upstream/main) without merging anything.

  4. Set upstream tracking — When creating a branch, use git push -u <remote> <branch> to set upstream on the first push. For an existing branch, use git branch --set-upstream-to=<remote>/<branch> <branch>.

  5. Push and pull with confidence — With upstream set, git push and git pull use that remote/ref by default. To target a different remote, be explicit: git push <remote> <branch>.

  6. Change or remove remotes — Use git remote rename, git remote set-url, and git remote remove to keep your door list tidy.

Read more in the hands-on section below.

Hands-on walkthrough

Scenario: Collaborating with a fork and an upstream

You forked a repository on GitHub, cloned your fork, and want to sync with the original. Here’s how you set up and manage two remotes.

# Start in a fresh clone of your fork
$ git clone https://github.com/yourname/project.git
$ cd project

# Add the original repository as 'upstream'
$ git remote add upstream https://github.com/original/project.git

# Verify both remotes
$ git remote -v
origin  https://github.com/yourname/project.git (fetch)
origin  https://github.com/yourname/project.git (push)
upstream https://github.com/original/project.git (fetch)
upstream https://github.com/original/project.git (push)

# Fetch upstream's branches
$ git fetch upstream

# Create a feature branch based on upstream's latest
$ git checkout -b feature/my-change upstream/main

# Push it to your fork and set upstream tracking
$ git push -u origin feature/my-change

What just happened? git push -u origin feature/my-change created origin/feature/my-change on your fork and set your local branch’s upstream to that. Now git push and git pull on this branch work without arguments.

Updating a fork from upstream

# Get the latest from the original repo
$ git fetch upstream

# Switch to your main branch (assuming it tracks origin/main)
$ git checkout main

# Merge upstream/main into your main
$ git merge upstream/main

# Push the updates to your fork
$ git push

If you prefer rebase over merge, use git rebase upstream/main instead. Both keep your fork current.

Setting upstream for an existing branch

Maybe you created a local branch, pushed it, but never set upstream. Fix it after the fact:

$ git branch --set-upstream-to=origin/feature/x feature/x
Branch 'feature/x' set up to track remote branch 'feature/x' from 'origin'.

Now git push and git pull work. If you want to change which remote a branch tracks, point to a different remote-tracking branch.

Inspecting upstream config

# Show the upstream for the current branch
$ git rev-parse --abbrev-ref --symbolic-full-name @{upstream}
origin/feature/x

# Show all branches with tracking info
$ git branch -vv
  main           abc1234 [origin/main] commit message
* feature/x     def5678 [origin/feature/x] commit message

Pro tip: git remote show origin gives a verbose report of all upstream relationships and which local branches are ahead/behind. Run it when you’re confused about your tracking setup.

Compare options / when to choose what

Approach What it sets Best for Drawback
git push -u on first push Sets upstream immediately New branches Easy to forget if you push later
git branch --set-upstream-to Sets upstream for existing branch Branches already pushed Requires you to remember the remote branch name
git config branch.<branch>.remote/.merge Manual config Automation / scripting Fiddly, error-prone

For most workflows, git push -u is the cleanest. Use --set-upstream-to as a repair tool. If you need precise control (e.g., a branch tracking a different remote), edit the config directly, but know what you’re doing.

Variations: remote names, URL rewriting, and hubs

  • Multiple remotes for mirrors — You might add internal and public remotes to push the same code to two places. Use git remote add for each.
  • Rename remotesgit remote rename origin github keeps your origin name free for another purpose.
  • Fetch from all remotesgit remote update fetches from every remote at once.

Troubleshooting & edge cases

  • "fatal: The current branch ... has no upstream branch" — You didn’t set tracking, or you’re on a new branch. Fix with git push -u origin <branch> or git branch --set-upstream-to=origin/<branch>.
  • "error: remote origin already exists" — You tried to add a remote that already exists. Check with git remote -v; if the URL is wrong, use git remote set-url origin <new-url>.
  • Upstream branch deleted on remotegit branch -vv shows [origin/deleted]. You can still track, but git pull will try to merge from a non-existent ref. Solution: fetch with --prune (git fetch origin --prune) and reset the upstream with git branch --set-upstream-to=origin/main main (or a valid branch).
  • Multiple remotes with the same branch namegit pull pulls from the configured upstream only. If you meant to pull from another remote, be explicit: git pull upstream main. Never rely on guesswork.
  • Accidentally pushed to the wrong remote — If you pushed to upstream instead of origin, you can’t easily undo on the remote. Prevention: always set upstream, and double-check git remote -v before pushing to a shared repo. Use git push --force-with-lease only if you understand the consequences.

Pro tip: Set a default push behavior with git config push.default upstream — then git push will only ever push to the upstream branch, never to a random remote.

What you learned & what's next

You now know how to list, add, and remove remotes, set upstream tracking with push -u or --set-upstream-to, and switch between remotes with explicit arguments. You understand the connection between local branches, remote-tracking branches, and upstream configuration. You’ve seen how to sync a fork with an upstream and how to handle common upstream errors. In the next lesson, "Manage multiple remotes and upstream tracking" expands into advanced workflows like multi-remote fetch strategies and remote-tracking branch cleanup. Practice setting up a fork with two remotes and syncing it daily — that muscle memory will save you hours in real open-source work.

Quick recap

  • A remote is a named URL; upstream is your branch's default remote ref.
  • git remote -v reveals your setup; git add, git rename, git remove manage doors.
  • git push -u sets upstream; git branch --set-upstream-to fixes it later.
  • Explicitly target remotes (git push upstream main) when you need to go off the default path.
  • Common errors like "no upstream branch" have fast, safe fixes.

Practice recap

Set up a practice repo with two remotes: create a bare repo locally for origin, add a second bare repo as backup, push a branch with -u, then change its upstream to the second remote and verify git push works. Experiment with git remote rename and git remote set-url to solidify your understanding.

Common mistakes

  • Adding a remote with a name that already exists — Git throws an error. Always check git remote -v first.
  • Forgetting -u on the first push, leaving the branch with no upstream — fix with git push -u origin <branch>.
  • Setting upstream to a remote-tracking branch that is stale or deleted — run git fetch --prune and reset the upstream.
  • Pushing to the wrong remote because you didn't verify the URL — use git remote -v and explicit git push <remote> <branch> when uncertain.

Variations

  1. Instead of git push -u, you can configure push.default simple and set upstream manually with git branch --set-upstream-to.
  2. For multi-remote workflows, use git remote update to fetch from all remotes at once, or git fetch --all.
  3. Use git remote rename to give remotes semantic names (e.g., upstream vs vendor) for clarity.

Real-world use cases

  • Contributing to open source: fork a repo on GitHub, add the original as upstream, and sync daily with fetch upstream && merge upstream/main.
  • Maintaining an internal mirror and a public release repo — push to both by running git push internal main && git push public main with separate remotes.
  • Working on a monorepo with separate staging and production remotes — set each branch's upstream to the appropriate remote to deploy with a simple git push.

Key takeaways

  • A remote is just a named URL; upstream tracking is the default link between your local branch and a remote-tracking branch.
  • List remotes with git remote -v and add them with git remote add <name> <url> to manage multiple doors.
  • Set upstream on first push with git push -u, or repair with git branch --set-upstream-to=<remote>/<branch>.
  • When you need a different remote, always specify it explicitly: git pull upstream main or git push backup feature.
  • Use git branch -vv to see all upstream relationships; troubleshoot with git remote show origin.
  • Prevent cross-remote accidents with git config push.default upstream and frequent git fetch --prune.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.