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.

  • Attempted to deploy to firebase on github branch
  • How can I push changes to a github repository as a different user?
  • How do I commit password only to heroku and not to github?
  • setting up two git accounts on one machine based on parent folder
  • Forking using TFS Git
  • Keeping Pro and Lite Version on Different Branches
  • Is it possible to add a version number using git / github
  • Git merge is not possible because I have unmerged files
  • 4 Solutions collect form web for “Is it possible to tell Github that my branch was merged into upstream master?”

    To answer the questions as asked,

    Is it possible to tell Github that my branch was merged into upstream master? […] Why did Github lose the information that feature was merged into master through an upstream PR?

    Yes, it’s certainly possible to record the merge. That’s usual.

    Someone chose to tell git (and github) not to record this one, so the effects appeared on the upstream master branch without a trace of where they came from.

    What you’re looking at is the results of someone choosing to divorce the mainline history from the history you offered. Why they chose to do that, you’ll have to find out from them. It’s a common and widely-used option, for reasons any number of people will be very happy to opine about. Linearizing history looks nice but involves tradeoffs, there’s downsides either way and different people and different situations will tilt the balance in their own ways.

    Regardless, after fetching the result you’ve now got an unrecorded merge, which is fine, clean and dandy so long as you never try to merge subsequent work still based on the unrecorded parent.

    What to do about it?

    The option you chose, which could also and more flexibly (it handles multiple-commit feature1..feature2 histories) be full-spelled as

    git rebase --onto master feature1 feature2
    

    is generally cleanest: upstream abandoned your feature1 history, so you abandon it, rebasing your feature2 on the content they have now.

    If for some reason you really don’t want to abandon the feature1 history in your own repository — maybe you’ve got more than just feature2 based off the old feature1 tip and the rebasing would start to get tedious — you can also add a local record of the merge.

    echo $(git rev-parse origin/master origin/master~ feature1) >>.git/info/grafts
    

    This tells the local git that the current origin/master commit has as parents both its recorded first-parent commit and also the local feature1 commit. Since upstream has abandoned the feature1 commit and you haven’t, all of git’s machinery now works properly both here and upstream.

    Different projects have different rules for what pull-request histories should be based on, some want everything based on some latest tip, others want everything based off a maintenance-base tag, others want bugfixes based on the commit that introduced the bug (I think this should be far more common). Some don’t care much because everybody’s so conversant with the code base that rebase-as-desired is still simplest.

    But the important part here is that rebase-before-pushing is your last opportunity to be sure that what you’re pushing is exactly right. It’s an excellent habit to get into, and grafts work beautifully in that workflow.

    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.

    The underlying root cause of your woes is that when the pull request for feature (the feature branch with one commit rolled back) completes, it results with a merge commit going into master. Here is a diagram showing what master and feature2 look like after the feature pull request into master has completed:

    master:   ... A -- B -- C -- M
                             \
    feature:                  D
                               \
    feature2:                   E
    

    Here, we can see that feature branched off from master at commit C, and feature2 is simply a continuation of feature with one extra commit. Merge commit M sits on the top of master and it represents all the extra work done in feature. Note that this is a merge commit, and hence has nothing to do with the history of feature2.

    Next, you ran the following rebase of feature2 on master:

    git checkout feature2
    git rebase master
    

    After this rebase, feature2 will look like this:

    feature2: ... A -- B -- C -- M -- D' -- E'
    

    Note carefully that the merge commit remains a part of the history. Even though functionally speaking it might seem unnecessary because commit D contains everything needed to make that merge commit, this commit still appears.

    If you are wondering what you can do to avoid this, one option would be to have kept the history of master linear. The flaw was the pull request which ended with the merge commit. If, instead you had played the commits from feature directly on top of master then you would not have had this problem. Consider the following commands:

    git checkout feature
    git rebase master
    

    Then, do a fast forward merge of master with feature:

    git checkout master
    git merge feature
    

    This would have left master and feature2 looking like this:

    master:   ... A -- B -- C -- D
    feature2: ... A -- B -- C -- D -- E
    

    Now, if you were to merge feature2 into master, Git would simply play the E commit, rather than going back to the original point whence master and feature diverged.

    Yes normally. But git answers this question by looking at the history, not the changes. If you use Github’s squash (i.e. nuke the history), then you forfeit the ability to leverage this history; namely git’s ability to detect whether part of that history already exists in the upstream.

    Building on the diagram created by Tim Biegeleisen:

    master:   ... A -- B -- C -- M
                             \  /
    feature:                  D
                               \
    feature2:                   E
    

    When you go to rebase feature2 onto master you should actually see this history:

    master:   ... A -- B -- C -- M
                             \  /  \
    feature:                  D     \
                                     \
    feature2:                         E'
    

    Because rebase will never recreate a commit that already exists in the destination. It will know that D is already in master’s history.

    When you rebased feature2 on master, you saw commits that logically were already present in master. This can only happen in the following scenario.

    Prior to the merge, you rewrite some of the commits in feature:

                     D'   feature
                   / 
    ... A -- B -- C 
                   \  
                    D 
                     \
                      E  feature2
    

    perform the merge:

                     D'   feature
                   /   \
    ... A -- B -- C --- M   master
                   \  
                    D 
                     \
                      E  feature2
    

    then tried rebasing feature2 on top of master:

                     D'   feature
                   /   \
    ... A -- B -- C --- M   master
                         \  
                          D'' 
                           \
                            E'  feature2
    
    Git Baby is a git and github fan, let's start git clone.