TechEarl

How to Sync a Fork with the Upstream Repo

Your fork falls behind the original project the moment someone else merges a change. Add an upstream remote, fetch it, then merge or rebase to catch up before you open a PR.

Ishan Karunaratne⏱️ 8 min readUpdated
Share thisCopied
How to sync a fork with the upstream repository in Git by adding an upstream remote and fetching

To sync a fork with the original project, add the original as a remote called upstream, fetch it, then bring its latest commits into your branch. The short version:

bash
git remote add upstream https://github.com/original-owner/project.git
git fetch upstream
git checkout main
git merge upstream/main
git push origin main

That fetches the upstream history and merges it into your local main, then pushes the result up to your fork on GitHub. Do this before you start new work, and again right before you open a pull request, so your changes sit on top of the current code.

Why a fork goes stale

When you fork a repository on GitHub, you get your own copy frozen at that instant. The original project keeps moving: other people merge pull requests, the maintainer pushes fixes, files change. Your fork knows nothing about any of that. It is not magically connected to the source.

Your local clone has one remote, origin, and origin points at your fork, not the original. So git pull only ever pulls from your own copy. To pull in the upstream project's changes, you need a second remote that points at the original repository. That second remote is conventionally named upstream. If you are still fuzzy on the fork-versus-clone distinction, my fork vs clone explainer covers what each one actually does.

Check what remotes you already have

Run this to see where your clone is pointing:

bash
git remote -v

A fresh clone of your fork shows only origin:

text
origin  https://github.com/your-username/project.git (fetch)
origin  https://github.com/your-username/project.git (push)

Two lines, both origin, both your fork. That is the problem. There is no link back to the source project.

Add the upstream remote

Point a new remote at the original repository. Use the original owner's URL, not yours:

bash
git remote add upstream https://github.com/original-owner/project.git

Now git remote -v shows four lines, two remotes:

text
origin    https://github.com/your-username/project.git (fetch)
origin    https://github.com/your-username/project.git (push)
upstream  https://github.com/original-owner/project.git (fetch)
upstream  https://github.com/original-owner/project.git (push)

You only ever fetch from upstream; you never push to it (you do not have write access to the original, and that is the whole point of working through pull requests).

If Git complains that remote upstream already exists, you added it once before, possibly with the wrong URL. Either reuse it or update the URL with git remote set-url upstream <url>. The same error on origin has its own fix here. You can use either the HTTPS URL shown above or an SSH one (git@github.com:original-owner/project.git); if you are not sure which to pick, see SSH vs HTTPS remotes.

Fetch the upstream history

Adding the remote does not download anything. Fetch it:

bash
git fetch upstream

This pulls down all the new commits and branches from the original project and stores them under remote-tracking branches like upstream/main. Crucially, it does not touch your working files or your current branch. Fetch is read-only against your work; it just updates Git's picture of what upstream looks like. If the difference between fetch and pull is not clear, I wrote a whole piece on git pull vs git fetch.

Merge or rebase upstream into your branch

Now you have upstream's commits sitting in upstream/main, but your local main has not moved. You have two ways to catch it up. Start by checking out your main:

bash
git checkout main

Option A: merge

bash
git merge upstream/main

If your local main has no commits of its own (the common case, since you should be doing your actual work on a feature branch), this is a fast-forward: your main simply jumps to upstream's latest commit, no merge commit created. If you have committed directly to main, Git creates a merge commit to join the two histories. If this merge instead errors out with git refusing to merge unrelated histories, your fork was almost certainly re-initialized at some point and no longer shares a common ancestor with upstream; that guide covers the fix.

Option B: rebase

bash
git rebase upstream/main

Rebase replays any commits you have on main on top of upstream's latest, giving you a straight line of history with no merge commit. For keeping a fork in sync this is the cleaner option, especially if you accidentally committed to main and want those commits to land neatly on top of the current code.

Either way, push the updated branch back to your fork:

bash
git push origin main

Which one should you use

MergeRebase
HistoryKeeps a merge commit when branches divergedLinear, no merge commit
SafetySafer; never rewrites existing commitsRewrites your local commits
Already-pushed commitsFineAvoid if those commits are already public
Best forA shared main others have pulledKeeping a personal fork tidy before a PR

For a deeper treatment of the trade-off, see git merge vs git rebase. The golden rule: do not rebase commits you have already pushed and that someone else might have based work on. On your own private fork, rebasing main against upstream/main is generally safe, and if a rebase ever goes sideways and drops a commit, you can usually recover them with git reflog before they are gone for good.

The right way: sync, then branch, then PR

Keeping main clean makes the whole workflow easier. The pattern I follow:

bash
git checkout main
git fetch upstream
git merge upstream/main
git push origin main

git checkout -b fix-typo-in-readme
# do the work, commit it
git push -u origin fix-typo-in-readme

Sync main first, then create a feature branch off the freshly-synced main, then do your work there. Your main stays a pristine mirror of upstream, and every feature branch starts from current code. The -u flag on that final push is what sets the upstream tracking branch, so plain git push and git pull work on this branch from then on; leave it off and the first push complains that the branch has no upstream configured. When you are ready, you open the pull request from your feature branch. If branches are new to you, my branching walkthrough covers git checkout -b and what a branch actually is, and the pull request guide covers the GitHub side.

Sync again right before you open the PR

A pull request is a merge request: you are asking the maintainer to merge your branch into their main. If upstream has moved on since you branched, your PR may conflict or look out of date. So sync once more just before you submit, and rebase your feature branch onto the latest upstream:

bash
git fetch upstream
git checkout fix-typo-in-readme
git rebase upstream/main
git push --force-with-lease origin fix-typo-in-readme

--force-with-lease is the safe form of a force push: it refuses to overwrite the remote branch if someone else pushed to it since your last fetch, so you cannot silently clobber another contributor's work. Use it instead of a bare --force after any rebase. If the rebase surfaces conflicts, my guide on resolving merge conflicts walks through fixing them. While you are already rebasing here, it is a good moment to squash your feature branch's commits before the PR so the maintainer reviews one clean change rather than a trail of work-in-progress commits.

A note on uncommitted changes

If you have edits in your working directory when you go to merge or rebase, Git may refuse with a message about local changes being overwritten. Commit them, or stash them first:

bash
git stash
git merge upstream/main
git stash pop

The stash tucks your changes away, lets the sync happen cleanly, then git stash pop puts them back on top. If you hit the overwrite error specifically, there is a dedicated fix here.

Where this fits

Syncing a fork is a small but constant chore in any open-source contribution loop, and it is one of the first habits worth building. If you are early on with Git generally, start at my Git for Beginners hub, which links out to the staging area, branching, remotes, and the rest of the fundamentals you will lean on here.

Sources

Authoritative references this article was fact-checked against.

Tagssync fork with upstreamGitVersion Controlgit remotepull requestupstream

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Match a URL with Regex

Match a URL with regex. Covers http/https schemes, protocol-relative URLs, ports, paths, query strings, fragments, runnable JavaScript / Python / PHP, engine notes, and the URL parser alternative.