Git: Tracking a Remote Branch for Changes
When you fork a project, you need a way to pull in changes from the original repository, usually called upstream. Here is how to wire up an upstream remote, actually sync your fork, and set up branch tracking so plain git pull and git push just work.
Sometimes you need to track a remote branch for changes, typically the original project you forked from, conventionally named upstream. Say you are on a team where you forked a project and have your own copy. Before you open a pull request, you want to pull in any changes from the originating repository so your work sits on top of the latest code. Here is how to set that up and keep it in sync.
Two different things called "upstream"
The word upstream gets used for two related but distinct things, and mixing them up is where people get confused.
- The upstream remote. A named remote (conventionally
upstream) pointing at the original repository you forked, so you can fetch the project's new commits. Your own fork is usually theoriginremote. - A branch's upstream tracking ref. The link between one local branch and the specific remote branch it pulls from and pushes to. Your local
main"tracks"origin/main. This relationship is what powers a baregit pullandgit push, and what produces theYour branch is up to date with 'origin/main'line ingit status.
This post covers both. First the fork scenario, then branch tracking at the end.
Add the upstream remote
Start by checking what remotes you already have. Right after forking and cloning, you will usually see only origin, which is your fork.
git remote -v
Add the original repository as a second remote named upstream.
git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPO.git
Swap
ORIGINAL_OWNERandORIGINAL_REPOfor the username and repository name from the original project's URL, the one you forked.
Confirm it is configured. You should now see both origin (your fork) and upstream (the original), each with a fetch and a push entry.
git remote -v
Actually sync: fetch, merge, push
Adding the remote is only half the job. The whole point is to pull the upstream project's new commits into your fork. That is three steps: fetch, merge, push.
git fetch upstream # download upstream's commits and refs (merges nothing yet)
git checkout main # be on your local default branch
git merge upstream/main # bring upstream's main into your local main
git push # update YOUR fork (origin) on GitHub
A couple of things worth calling out. git fetch upstream downloads everything but changes none of your branches; it just makes upstream/main available locally. The final git push matters: syncing only updates your local clone, so without it your fork on GitHub is still behind.
Merge or rebase?
I used git merge above, which is the safe default. The alternative is to rebase your branch on top of upstream instead.
git rebase upstream/main
Merging is non-destructive, but it adds a merge commit each time you sync. Rebasing replays your commits on top of upstream for a cleaner, linear history, at the cost of rewriting commit hashes. The Golden Rule of Rebasing applies: never rebase a branch that other people have already pulled. For keeping a personal fork's main in step with upstream, either works. If you keep your main as a clean mirror of upstream and do all your real work on feature branches, these syncs fast-forward and the whole question goes away.
The quick way: gh CLI and the Sync fork button
If your fork lives on GitHub, you often do not need the manual commands at all. The GitHub CLI can sync the fork for you.
gh repo sync # sync your local clone from its parent
gh repo sync OWNER/YOUR_FORK # sync the fork on GitHub from upstream
GitHub's web UI has the same thing: on your fork's page, the "Sync fork" dropdown shows how far behind you are and updates your branch with one click. These are convenient, but knowing the fetch and merge underneath is what saves you when a sync hits a conflict.
Bonus: tracking a remote branch for real
The fork case is one kind of "tracking." The other is setting a branch's upstream tracking ref, so plain git pull and git push know where to go without you naming a remote and branch every time.
When you first push a new branch, -u (short for --set-upstream) creates the branch on the remote and wires up tracking in one go.
git push -u origin my-feature
For a branch that already exists, you can set or change its upstream after the fact.
git branch --set-upstream-to=origin/main
To see what each branch is tracking, ask for the verbose listing. It shows the tracked branch and how far ahead or behind you are.
git branch -vv
That tracking link is exactly what git status reads when it tells you Your branch is up to date with 'origin/main', and it is what lets a bare git pull know which remote branch to fetch and merge.
Keep your main clean
One habit makes all of this painless: never commit directly to the branch you sync. If your local main only ever moves by syncing from upstream, every sync fast-forwards cleanly. The moment you commit your own work onto main, easy fast-forwards turn into merge conflicts. Do your work on feature branches off a clean main, and keep main as a mirror of the source of truth.
That is the whole loop: add the upstream remote once, then fetch and merge (or let gh do it) whenever you need the latest, and push to update your fork. When you are coordinating this across a team, it pairs nicely with a shared branching strategy, which I cover in Git Flow vs GitHub Flow.