0

the workflow we are required to follow is this:

  1. fork a project on github into your own private githib account.
  2. clone forked project to local machine.
  3. create feature branch (of master) on local machine.
  4. commit branch to forked repo.
  5. do a pull request on the main repo to the new branch on the forked repo.

This is great, but a few days later we need to create a new feature branch. In the mean time some features have been commited to master on the main repo.

If we now create a new branch on our repo, push it to our forked copy, we are working with old out of date code.

So how do we pull the latest master from the original repo we dont have permission on, into our forked repo, then down to our local repo?

Or is it best to always create a new fork for each feature branch, and try to work quickly (as the code will soon be out of date as we cant pull in changes pushed by others).

This has some discussion on the subject:

Best practice for tracking upstream in fork on github

But is missing what I need, which is how to do this with actual git commands.

E.g. what we have now is this:

  1. fork on github to local repo using github web ui
  2. git clone https://github.com/myname/aproject
  3. git remote add origin https://github.com/myname/aproject # not sure why we have to do this.
  4. git checkout -b mybranch
  5. do some work.
  6. git commit -m ...
  7. git push origin mybranch # puts the new branch on our personal github forked copy
  8. make a pull request via github ui.

What we dont know is what commands we would need to get the latest master updates from the original github repo, through to our forked copy of that repo on in our private github account, then finally down to our local cloned copy of the forked repo.

E.g. how would we tell the github hoasted forked repo to update itself from the repo it was forked from?

Or, if this is not possible, a way to pull the latest master from the original github hosted repo directly to the local cloned copy on the developers PC (bypassing the forked repo in the devs. private github account)

I also saw this post:

How do I update a GitHub forked repository?

The accepted answer is this:

git remote add upstream https://github.com/whoever/whatever.git
git fetch upstream
git checkout master
git rebase upstream/master
  1. How does the fork get the new master updates? This updates the master of your local repo only?
  2. This implies that your local cloned repo has 2 parents - the original repo and your forked repo. How can this work? How can you fetch from two different repos into one repo? Presumably, the forked repo will never know about the updates pulled from the original repo? When you next push a second feature branch back to your forked repo, it will be based on the wrong code, so the forked repo will be differnt to your local repo, and tests which work locally would not work if you re-checked out everything from your forked repo to a fresh dir?
John Little
  • 10,707
  • 19
  • 86
  • 158
  • 2
    Possible duplicate of [Best practice for tracking upstream in fork on github](https://stackoverflow.com/questions/11459072/best-practice-for-tracking-upstream-in-fork-on-github) – Daniel A. White Jan 18 '18 at 18:32

2 Answers2

2

As the other answer describes, there is nothing wrong in having 2 remotes. Actually, to submit feature branches, you do not have to perform any re-syncing or your fork. You need to follow so-called triangular workflow (search there for "Improved support for triangular workflows").

tl:dr:

<fork in Web>
git clone <URL>
cd clone
git add upstream <upstream URL>
<later>
git fetch upstream
git checkout -b <feature> upstream/master
<develop, commit>
git push origin <feature>
<make PR in Web>

As you can see, you only update here the <feature> branch of your fork, which is up-to-date with upstream - just the one you need to submit a PR. Basically, the whole purpose for the fork to exist is to have that one branch, then there is no reason to maintain other branches in the fork.

The question Best practice for tracking upstream in fork on github goes beyond that, it needs to "tracking of changes unique to the fork". When such goal arises then there is a task of maintaining those changes up-to-date, but not before.

max630
  • 8,762
  • 3
  • 30
  • 55
  • I dont quite follow the second paragraph. If I need to do this: 1) create feature branch A & commit to fork 2) do pull request 3) pull from upstream to get any master branch changes 4) create feature branch B and commit to fork etc. (i.e. create mulitple feature branches) can I use your triangular thing? What I dont get is that the forks master will be out of date, so my FB B will be against old code so be wrong? – John Little Jan 19 '18 at 12:56
  • Sorry, fixed the checkout. You create the feature directly from the upstream master, you don't need to have anything local there before you start developing – max630 Jan 19 '18 at 13:01
  • Basically, you don't need to have the master branch, or any other branches than you feature branches, in your fork at all. – max630 Jan 19 '18 at 13:05
1

That git remote add upstream … sequence of commands is the correct way to get a local copy of the original repo’s master branch.

This implies that your local cloned repo has 2 parents – the original repo and your forked repo. How can this work? How can you fetch from two different repos into one repo?

Git call them “remotes” rather than “parents”. Yes, your repository will have two remotes (links to remote repositories) after this operation.

When you git clone your local repo from your fork, your local repo automatically gets one remote called origin, and automatically creates a local branch master based on the remote branch origin/master. There should be no need to manually git remote add origin because the origin remote is created by default, but it wouldn’t hurt. Then the git remote add upstream … command adds a second remote called upstream.

There is no ambiguity when you have multiple remotes, because GitHub namespaces all branches from each remote with name/ at the beginning. The master branch on your forked origin remote is referred to as origin/master, and the master branch on the original upstream remote is referred to as upstream/master. If you refer to a branch with no namespace, such as master, this refers to a branch in your local repo.

You can see the list of all branches stored in your Git repo with git branch --list -a. This includes local-only branches such as master as wells as local backups of remote branches like origin/master and upstream/master. Your repo's local backups of the remote branches will be updated when you run git fetch --all (or git fetch --all --prune, which deletes local backups of branches that were deleted on that remote).

Presumably, the forked repo will never know about the updates pulled from the original repo?

If you want to update one remote based on another remote, you always have to do it through a branch in your local repo. You can’t directly push or pull between remotes.

First, to make sure you have the latest commits from all remotes, run git fetch --all, as mentioned previously.

Now that the original repo’s master is accessible at upstream/master, you can merge it into your local master branch. Do this with git checkout master followed by one of git merge upstream/master or git rebase upstream/master. After this, you can git push origin master to push your local master branch’s commits into origin/master.

If you wanted to create a new branch on your forked origin repo that matches the upstream’s master branch, you can git branch upstream-master upstream/master to create a local upstream-master branch, then git push origin upstream-master to push a new branch upstream-master to your origin remote. You could also rename the branch during the push by using a :, like in git push origin upstream-master:old-master.

Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
  • This is awesome. Note, we never modify master on the local repo, this might make things simpler. R.e. the last paragraph, why would we want to make a new master branch on the forked origin repo? Would the "fetch --all, checkout master, merge upstream/master, push origin master" not make all 3 repos master branches the same? – John Little Jan 19 '18 at 14:00
  • After looking at the options, it might be simpler if we just create a new fork for every feature branch. That way, there is less scope for screwing things up. It means we dont have to do any 3 way syncronisation. The down side is needing to locally clone the new forked repo for each feature, and rebuild the project in it (dependencies etc). I think this is what most of our devs are doing currently – John Little Jan 19 '18 at 14:03
  • @JohnLittle You would only want to make a new master branch on the forked origin repo if history has diverged and you want a copy of both to be conveniently in the same repo. It seems that doesn’t describe your situation, so you can ignore it. And yes, those steps ending with `push origin master` will make all of their master branches the same if you haven’t added commits to your local or forked master branches that aren’t in the upstream master branch. – Rory O'Kane Jan 19 '18 at 15:58
  • @JohnLittle As for creating a new fork for every feature branch, I do not recommend this. Not only would you have to download and rebuild the project again in each local clone, you would also need to set up your editor again. And GitHub does not make this forking model easy, because the GitHub “fork” button can only create one fork per repository per user. Instead, use `git branch` to create branches, `git checkout` to switch between them, and `git merge` and `git push` to control what is on each branch. – Rory O'Kane Jan 19 '18 at 16:09
  • @JohnLittle If you’re having trouble learning how to work with Git, I recommend using a [GUI client for Git](https://git-scm.com/downloads/guis). Since they make it easy to view the state of each local and remote branch, they can help you develop an intuition as to how Git works. I especially recommend [Tower](https://www.git-tower.com/) (though it’s not free), because it lets you drag and drop a branch onto another branch or a remote to perform the applicable `merge` or `push` command. – Rory O'Kane Jan 19 '18 at 16:24