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:
git remote add upstream https://github.com/original-owner/project.git
git fetch upstream
git checkout main
git merge upstream/main
git push origin mainThat 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:
git remote -vA fresh clone of your fork shows only origin:
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:
git remote add upstream https://github.com/original-owner/project.gitNow git remote -v shows four lines, two remotes:
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:
git fetch upstreamThis 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:
git checkout mainOption A: merge
git merge upstream/mainIf 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
git rebase upstream/mainRebase 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:
git push origin mainWhich one should you use
| Merge | Rebase | |
|---|---|---|
| History | Keeps a merge commit when branches diverged | Linear, no merge commit |
| Safety | Safer; never rewrites existing commits | Rewrites your local commits |
| Already-pushed commits | Fine | Avoid if those commits are already public |
| Best for | A shared main others have pulled | Keeping 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:
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-readmeSync 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:
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:
git stash
git merge upstream/main
git stash popThe 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.
- git-remote: Official Git documentationgit-scm.com
- git-fetch: Official Git documentationgit-scm.com
- Syncing a fork: GitHub Docsdocs.github.com





