How to sync your fork with the original repository
TL;DR—here’s what I’ll do.
Whenever I work on open-source projects, I usually maintain my own copy, a fork, of the original codebase. Then, when I propose changes, I open up a Pull Request (PR).
When my PR receives a maintainer’s approval, the commits from my fork will happily live inside the original repository; all is good. However, there are cases when a maintainer merges not via a merge commit, but by squashing or rebasing my changes.1
During a rebase, commits are “rewritten” even with the same diff. Git cannot put two and two together, so it will treat my commits as unique.
Now, when I make a new Pull Request for a different issue, those commits will persist! In Github, this messes the Pull Request interface (i.e., “why do I have commits for fixing Issue 1 when this PR is about Issue 2?”).
To add, Github’s “Fetch upstream” button performs a merge, making further Pull Requests dirty and riddled with stray commits—the worst case is to delete my fork and copy it again.
What I do
What I do is maintain a link to the original repository and track the changes from there. Before making a PR, I make sure to rebase those new changes to my fork.
First, let’s create a link to the original remote. You can call it
upstream
, orig
or whatever you want:
$ git remote add upstream https://github.com/com/original/original.git
You now have two remotes: one for your fork, and one for the original
repository. If you run git remote -v
, you will see the following:
$ git remote -v
origin https://github.com/myusername/myfork.git (fetch)
origin https://github.com/myusername/myfork.git (push)
upstream https://github.com/original/original.git (fetch)
upstream https://github.com/original/original.git (push)
Then, I track the remote branches by fetching them from the upstream:
$ git fetch upstream
Lastly, I go back to my fork’s default branch (usually it’s master
or
main
) then rebase the upstream’s changes to this branch.
$ git checkout main
$ git rebase upstream/main
To fully sync my fork, I push the changes from my local to my remote:
$ git push origin +main
The +
sign means that it’s a force push. It overwrites whatever you have in
your remote and rewrites the history to be similar as the original repo. You
will achieve similar results if you pass an -f
parameter like so:
# Same as above
$ git push -f origin main
And that’s it! Our fork is now fully-synced with the original repository. The commits should match one-to-one, without merge commits nor strays .2 This is helpful to ensure that my fork is up-to-date with the codebase.
Hope you learned something new today!
Footnotes
-
I wrote in-depth about git team workflows. Read more if you’re interested in the differences between merging and rebasing. ↩
-
Another thing that I do is to make a Pull Request using my fork’s feature branch, not through
main
. With this method, I ensure that mymain
branch is always “clean.” ↩