Search code examples
gitgithubgit-workflow

Is it possible to tell Github that my branch was merged into upstream master?


I used my local branch feature to create a PR for a github repo (I don't have write access to it). Later I decided I want to separate its last commit into a standalone PR, so I moved feature one commit back:

git checkout feature
git branch feature2
git reset --hard @~
git push -f

The first PR is merged upstream, so now I want to create the second PR:

git checkout master
git pull upstream master
git push origin
git checkout feature2
git rebase master

Unfortunately, it turns out that git lacks the information that feature was merged into master. Therfore, it doesn't realize that the nearest common base of feature2 and master is very close: it's just feature. Instead, rebase goes back all the way to common base of feature and master as if they were never merged. As a result, git rebase master becomes unnecessarily messy.

Why did Github lose the information that feature was merged into master through an upstream PR? Is there any way to provide Github with that information?

In the end, I had to resort to:

git checkout master
git checkout -b feature2_new
git cherry-pick feature2

Luckily I only needed to take care of a single commit. And even with a single commit, I think that a merge with the true base (if git knew about it) would be better than the cherry-pick because git would be able to use its knowledge of history to resolve more conflicts automatically.

Note that if I were to merge feature into master locally instead of doing a github PR, no information would have been lost. Of course, then my master would not be in sync with the upstream repo, so it would be pointless.


Solution

  • Github now supports 3 techniques to merge pull requests:

    • Merge: creates a merge commit (no fast forward) + fetches all the original commits from the PR branch
    • Squash and merge: creates a single commit
    • Rebase and merge: creates as many commits as the PR branch, but they are rebased onto master

    Only the regular merge preserves the knowledge that my local commits were part of the PR merged into master. If it was used, I wouldn't have encountered the problem I described in the question.

    The other two techniques lose that knowledge - and there's nothing I can do to create it retroactively (without modifying the upstream master). That's the price to pay for a simpler history.

    Intuitively, in order for git to know that an upstream master commit U is related to my local commit L, there needs to be an arrow pointing from U to L.

    Conceptually, there are two ways to achieve this.

    First, U can have two parents: one connecting it to L, the other connecting it to all the previous commits on the upstream master. This is precisely what Github merge technique does.

    Second, U can have L as its sole parent, if L already points to all the previous commits on the upstream master. Github could have supported this by allowing fast-forward with its merge technique, but it chose not to.

    If a Github PR is merged with either squash and merge or rebase and merge, all commits created on the upstream master have only one parent; there are no arrows between them and my local commits.

    Edit:

    Also I now believe that the loss of history I was asking about was no big deal in the first place. IIUC, the conflicts I would encounter with git cherry-pick are actually the same as the ones with git rebase if master was connected to feature2 through a regular merge commit. And if I had more than 1 commit split into a standalone PR, cherry-pick would handle that easily too.