Merge pull request in git causes the upstream branch to go ahead of origin

后端 未结 1 1369
刺人心
刺人心 2021-01-22 08:23

Terminologies:

  1. The local branch is called master
  2. the repository on github is called origin/master
  3. And the root repository from where I have fork
1条回答
  •  醉话见心
    2021-01-22 08:37

    The core of the problem lies inside GitHub. (I'll go into detail in a moment.) The answer to your question is thus both no and yes:

    Is there any way of pushing into the upstream without making an additional commit to the upstream. Just merging the pull request so that the upstream/master and the origin/master branches be even?

    There are four groups of people involved here:

    1. Those that program GitHub itself. They control what appears in one's web browser, i.e., that green button you see on the page that says "Merge this pull request", which has a small dropdown arrow on the edge that, if clicked, let you select one of three types of merge.

      The three types of merge do not include the action you would like. So if they changed this to add a fourth type of merge, or changed one of the existing three, or something along those lines, that could give everyone in group 3 the ability to do what you want.

    2. Those with administrative access to the upstream repository. They can git push straight to the upstream repository. This means they can use command-line Git (not GitHub at all) to perform the action you would like, then run git push to update the repository on GitHub.

    3. Those with write access, but not administrative access, to the upstream repository, through the GitHub-provided interface. They cannot do what you want—at least, not until and unless the Group 1 folks add it as an option.

    4. Those with no write access (presumably including yourself): they cannot do it.

    Now, as to those GitHub clicky buttons—or rather, button singular, with three modes: the three offered modes are labeled this way:

    • Merge.

      You might think this would do the job, but it does not, because it runs the equivalent of git checkout branch && git merge --no-ff -m message hash. The branch is obvious enough, and the message part is Merge pull request #number from repository. The hash part is trickier: it's the hash ID found under refs/pull/number/head. This is the commit ID of the particular commit you submitted, through your clicking of the "compare and pull request" clicky buttons.

      If GitHub were to use the equivalent of git checkout branch && git merge --ff-only hash, with the same branch and hash, you would get just what you wanted. Of course, there would be no commit containing the text Merge pull request .... But that would be fine. (This is the new mode I wish they would add. They could call it Merge without additional commit or Fast-forward, perhaps.)

    • Rebase and merge.

      This adds the pull-request commits—there might be more than one—that are not currently on the branch, to the branch. It runs the equivalent of git checkout branch && git rebase --force-rebase hash (perhaps with --merge or --strategy merge as well). The end result is that the commits from the pull request are copied, not merged, to new commits with new and different hash IDs. The merge request is completed without adding a merge commit, so there is no Merged pull request ... message.

      You might think that, in the case where the commit or commits to be copied are already in the correct position—i.e., just past the tip of the branch before clicking the button—GitHub would not copy the commits, and would instead just use them as is, as would happen if they ran the equivalent of git rebase without --force-rebase or --no-ff. But this is not the case. Whether there is some good (internal to GitHub) reason for this, I do not know.

    • Squash and merge.

      This does the equivalent of git checkout branch && git merge --no-commit --squash hash && git commit -m message. (The --no-commit here is redundant: --squash implies --no-commit. This is an unnecessary peculiarity of the command line Git implementation of git merge --squash, since if you really want --no-commit, you could just specify it.)

      Because this makes a new commit that is not tied in any way to the commits whose effect is brought in, this is clearly totally unsuited to achieve what you want.

    Summary

    Those who have write access to the upstream repository can take your pull request into a Git repository on a computer they control, merge it manually, and push the result back to the upstream repository on GitHub. This is what Tobias K. refers to in a comment. That would achieve what you want.

    If GitHub were to add the merge-mode I wish they would add, the users who currently merge your pull request with the Web-UI merge button would be able to do it in one step, without involving their own computers and Git command-line commands. But without that additional mode, they cannot.

    What they are actually using right now is the Rebase and merge mode. We can tell because of this:

    So now on github it shows "This branch(origin/master) is 1 commit ahead, 1 commit behind upstream/master."

    If they were to use the Merge mode, you would see yourself being one commit behind, but not one commit ahead. This would be more friendly to you, but in some ways, less nice to themselves (and perhaps other users of their repository). When they use Rebase and merge they wind up copying all of your commits, so if you were k commits ahead before, you will suddenly be k ahead and k behind: the k that you are ahead are your commits, with your hash IDs, and the k that you are behind are their copies of your commits, with their hash IDs.

    Recovering from your upstream's Rebase and merge

    Note that you can recover from this by doing, on your own computer, the following:

    git fetch upstream
    git checkout $branch
    git rebase upstream/$branch
    git push --force-with-lease origin $branch
    

    (where $branch represents your branch name). The fetch obtains their copies of your commits. The checkout positions you for the rebase, which the rebase performs.1 The git push sends the new commits to your fork (if they are not already there) and then requests that GitHub change your fork's idea of $branch to point to the last such copied commit, succeeding (as if by --force) even if this drops some commits, but failing (--force-with-lease) if the commits being dropped are not exactly those expected.


    1You can actually do this in one step as git rebase lets you check out a branch before starting the rebase, but I think it is clearer this way. To do it in one step, use git rebase origin/$branch $branch.

    0 讨论(0)
提交回复
热议问题