user1382278 user1382278 - 2 months ago 16
Git Question

Update shared forked repository using git

Consider repository A a github repository that is managed via Gerrit.
I cloned repository A and I created a new branch starting from the master branch of repository A. I pushed this new branch on a new gitlab repository B. I am admin on repository B and I shared it with other developers. Developers cannot push on this branch, but I can merge theirs pull requests.
I merged some pull requests on the master branch of repository B. So, repository B has the initial commits of repository A and new commits of the pull request: B commits on top of A commits (b a).

Then, I want to update the repository B with new commits of repository A. Call these commits a+.

I see two options:


  1. If I merge A on B, the result is: a+ b a.

  2. If I rebase B on A+, the results is: b a+ a.



Option 1: the developments commits are mixed with external ones. Difficult to debug and to highlight differences.

Option 2: I have to push force the changes on the the remote B. The consequences, if I am not mistaken, may be:
1. if developers pull B and they have commits on local master of B, they will lose their local changes;
2. developers may face big troubles during rebase their local branches after the forced update of branch B.

How should I proceed to avoid any problems?

Answer

If I understand it correctly, you have a single (local) repository which looks somewhat like this:

            A/master
               ↓
* -- * -- * -- a
                \
                 * -- * -- b
                           ↑
                       B/mybranch

Now, A is updated, so it looks like this:

                          A/master
                              ↓
* -- * -- * -- a -- * -- * -- a+
                \
                 * -- * -- b
                           ↑
                       B/mybranch

Note here that you have two diverged branches that are not in a straight line. So saying that you would get a+ b a when you merge A onto B is not correct. You do get a merge, but that keeps the history as it is:

                          A/master
                              ↓
* -- * -- * -- a -- * -- * -- a+
                \               \
                 * -- * -- b --- M
                                 ↑
                            B/mybranch

As you can see, you still have the information where the commits came from: The ones between a and a+ came from one branch, and the other ones between a and b came from another. And M combines those tro branches. Usually, this is exactly the proper way to solve this.

The benefit of this over rebasing—as you noticed yourself—is that the commits stay the way they are. All users who have interacted with either repository can simply pull those changes in without any problems. If you rebased any of those commits, they would have to manually fix that (chances are that they don’t even realize it, so they will just pull and actually create an even messier history with duplicate commits).

So a merge is definitely better to work with here. Yes, the history will not look as perfect as a straight line, but it properly conveys what has happened: There is a diverged development line which is independent and that has then been updated with the changes from the upstream repository A. This information can be useful and as such it makes sense to keep them in. And especially if you have users interacting with both those remotes, they will definitely prefer to keep their repository compatible with both remotes.

If you don’t want the history to look like this, and don’t care about compatibility with A, you could also squash all the changes from A onto your B:

                          A/master
                              ↓
* -- * -- * -- a -- * -- * -- a+
                \
                 * -- * -- b -- [A]
                                 ↑
                            B/mybranch

Here, [A] is a squashed commit that contains all the changes between a and a+ in a single commit. You could get this result by rebasing A onto B while squashing all commits. In this case, you should clearly state where those changes come from in the commit message though.