githistoryrebasing

Rebasing to remove parallel history and create a single linear history


I created a feature branch from master branch. After that there is a commit [F1] from feature branch.

        [F1]            -- Feature Branch
       /
[M1]-[M2]               -- Master Branch

After that, feature branch is merged in master branch, and there are two more commits [M3] and [M4] in master branch.

        ----[F1]                   -- Feature Branch
       /        \
[M1]-[M2]-[M3]-[F1]-[M4]-[M5]       -- Master Branch

But now I would like to rebase to to create a single linear history.

[M1]-[M2]-[M3]-[F1]-[M4]-[M5]       -- Master Branch

Is that posible an how would I do it?


Solution

  • Your diagram is impossible:

            ----[F1]                   -- Feature Branch
           /        \
    [M1]-[M2]-[M3]-[F1]-[M4]-[M5]       -- Master Branch
    

    In the diagram, the same commit, F1, appears in two different places. That cannot be. I will assume that the second F1 is actually a true merge commit, and that what you have is this:

             -- F1 --
           /          \
    M1 -- M2 -- M3 -- [merge] -- M4 -- M5 (master)
    

    And now you wish you had done a "squash merge" or rebase of the feature branch onto the end of master rather than making a true merge.

    The simplest way to do this is just to cherry pick the relevant commits one by one. Start a temporary branch at M3, and cherry pick F1, then M4, then M5. Now your temporary branch looks the way you wish master would look, so checkout master and reset it hard to your temporary branch and throw the temporary branch away.

    git switch --det M3
    git switch -c temp
    git cherry-pick F1
    git cherry-pick M4
    git cherry-pick M5
    git switch master
    git reset --hard temp
    git branch -D temp
    

    That will form this, which seems to be what you want:

             -- F1 --
           /          \
    M1 -- M2 -- M3 -- [merge] -- M4 -- M5 (was master, now nothing)
                  \
                   F1' -- M4' -- M5' (master, formerly temp)
    

    The original M5, M4, merge, and F1 now have no branch name pointing at them, and so they will eventually be garbage-collected and the diagram will clean itself up:

    M1 -- M2 -- M3
                  \
                   F1' -- M4' -- M5' (master)
    

    I suppose it's possible to do something similar with an interactive rebase, but this simple step-by-step manual approach seems a lot more straightforward to me. In any case, rebase really is an extended series of cherry-picks, so the approaches are the same, without loss of generality.

    Note that the usual caveats are in order here. You will be making copies of commits, so the new F1, M4, and M5 will have different SHA identifiers than the old ones. You are rewriting history, so you must not do any of this if master is shared with other collaborators, and if you have already pushed master, you will have to use force to push it after this change.