Search code examples
gitgit-commitgit-rebase

Git rebase a branch on another after parent branch is merged to master


If I have branch_1 forked out of master with let's say 2 commits.

Next, I made branch_2 forked out of branch_1.
On branch_2 I have made changes which are not in the area branch_1. I of course have changes from branch_1 included here but I have not touched those changes.

Now let's say branch_1 merges into master.

Next, on branch_2 I do git pull upstream master --rebase to rebase branch_2 on top of master so that commits from branch_1 disappear from commit history of branch_2. I should not get merge conflicts here. Right? But I do in the same lines of code which were changed on branch_1. This is strange! Is this expected?


Solution

  • Your assumption about what the rebase would do is valid, if git knows the relationship between branch_1 and branch_2. Your problem is that you threw away that relationship by rebasing or squashing branch_1.

    With a true merge, your commit graph would look like this (where "M" is the merge commit when you merged branch_1 into master):

                master
                  |
                  v
    ... <- A <--- M
            \    /
             <- B <- C
                ^    ^
                |    |
           branch_1 branch_2
    

    Then, when you rebased branch_2 onto master, git would find that commit B is already reachable from master (as a parent of commit M), so wouldn't include it in the list of commits to replay.

    However, when you squashed branch_1, you created this (where "BS" is the result of squashing branch_1):

                master
                  |
                  v
    ... <- A <--- BS
            \    
             <- B <- C
                ^    ^
                |    |
           branch_1 branch_2
    

    Now when you ask to rebase branch_2 on master, it finds that commit B is not reachable from master (commit BS), so includes it in the list of commits to replay. If commit B was actually multiple commits, it will apply them one by one, and since the changes have already happened in BS, you'll get conflicts when it does so.

    The solution (other than using true merges from now on 😛) is to be more explicit about what you want to rebase, by either:

    • Using an interactive rebase (git rebase -i upstream/master) and manually deleting the commits from branch_1 from the todo list
    • Using the three-argument form of git rebase, "rebase old_base old_tip --onto new_base": git rebase branch_1 branch_2 --onto upstream/master