This workflow is relevant if you have done fork and clone and now you need to pull subsequent changes from the original repo into your copy.
Sometimes you set this up right away, when you fork and clone, even though you don’t need it yet. Congratulations, you are planning for the future!
It’s also very typical to do this step a few days or months later. Maybe you’re taking an interest in someone else’s work for the second time and you want to make another pull request. Or you just want your copy to reflect their recent work. It is also totally normal to set this up upon first need.
OWNER/REPO refers to the original GitHub repo, owned by
OWNER, who is not you.
YOU/REPO refers to your copy on GitHub, i.e. your fork.
You might hope that GitHub could automatically keep your fork
YOU/REPO synced up with the original
OWNER/REPO. Or that you could do this in the browser interface. Then you could pull those upstream changes into your local repo.
But you can’t.
There are some tantalizing, janky ways to sort of do parts of this. But they have fatal flaws that make them unsustainable. I believe you really do need to add
OWNER/REPO as a second remote on your repo and pull from there.
Get into the repo of interest, i.e. your local copy. For many of you, this means launching it as an RStudio Project. You’ll probably also want to open a terminal (Appendix A) within RStudio for some Git work via Tools > Terminal > New Terminal.
Make sure you are on the
master branch and your “working tree is clean”.
git status should show something like:
BTW I recommend that you never make your own commits to the
master branch of a fork. However, if you have already done so, we are going to address your sorry situation below.
Most of you will see output along these lines (let’s call this BEFORE):
There is only one remote, named
origin, corresponding to your fork on GitHub. This figure depicts a BEFORE scenario:
This is sad, because there is no direct connection between
OWNER/REPO and your local copy of the repo.
The state we want to see is like this (let’s call this AFTER):
Notice the second remote, named
upstream, corresponding to the original repo on GitHub. This figure depicts AFTER, the scenario we want to achieve:
Sidebar: If you used
usethis::create_from_github("OWNER/REPO") for your original “fork and clone”, the
upstream should already be set up. In that case, you can skip to the part where we pull from
Let us add
OWNER/REPO as the
On GitHub, make sure you are signed in and navigate to the original repo,
OWNER/REPO. It is easy to get to from your fork,
YOU/REPO, via “forked from” links near the top.
Use the big green “Clone or download” button to get the URL for
OWNER/REPO on your clipboard. Be intentional about whether you copy the HTTPS or SSH URL.
Use a command like this, but make an intentional choice about using an HTTPS vs SSH URL.
upstream can technically be whatever you want. There is a strong tradition of using
upstream in this context and, even though I have better ideas, I believe it is best to conform. Every book, blog post, and Stack Overflow thread that you read will use
upstream here. Save your psychic energy for other things.
This feels a bit odd, but humor me. Click on “New Branch” in the Git pane.
This will reveal a button to “Add Remote”. Click it. Enter
upstream as the remote name and paste the URL for
OWNER/REPO that you got from GitHub. Click “Add”. Decline the opportunity to add a new branch by clicking “Cancel”.
Let’s inspect the current remotes for your local repo AGAIN. In the shell:
Now you should see something like
Notice the second remote, named
upstream, corresponding to the original repo on GitHub. We have gotten to this:
Now we can pull the changes that we don’t have from
OWNER/REPO into our local copy.
This says: “pull the changes from the remote known as
upstream into the
master branch of my local repo”. We are being explicit about the remote and the branch in this case, because (as our
git remote -v commands reveal),
upstream/master is not the default tracking branch for local
I highly recommend using the
--ff-only flag in this case, so that you also say “if I have made my own commits to
master, please force me to confront this problem NOW”. Here’s what it looks like if a fast-forward merge isn’t possible:
See Um, what if I did touch
master? to get yourself back on the happy path.
This is, frankly, totally optional and many people who are facile with Git do not bother.
If you take my advice to never work in
master of a fork, then the state of the
master branch in your fork
YOU/REPO does not matter. You will never make a pull request from
If, however, your grasp of all these Git concepts is tenuous at best, it can be helpful to try to keep things simple and orderly and synced up.
Feel free to push the newly updated state of local
master to your fork
YOU/REPO and enjoy the satisfaction of being “caught up” with
In the shell:
Or use the green “Push” button in RStudio.
I told you not to!
But OK here we are.
Let’s imagine this is the state of the original repo
and and this is the state of the
master branch in your local copy:
The two histories agree, up to commit or state
C, then they diverge.
If you want to preserve the work in commits
Z, create a new branch right now, with tip at
git checkout -b my-great-innovations (pick your own branch name!). Then checkout
git checkout master.
I now assume you have either preserved the work in
Z (with a branch) or have decided to let it go.
Do a hard reset of the
master branch to
You will have to figure out how to convey
C in Git-speak. Specify it relative to
HEAD or provide the SHA. See future link about resets for more support.
The instructions above for pulling changes from upstream should now work. Your
master branch should reflect the history of
If you chose to create a branch with your work, you will also have that locally:
If you pushed your alternative history (with commits
Z) to your fork
YOU/REPO and you like keeping everything synced up, you will also need to force push
git push --force, but we really really don’t like discussing force pushes in Happy Git. We only do so here, because we are talking about a fork, which is fairly easy to replace if things go sideways.