gitgit-mergegit-rebasegit-rewrite-history

How to restore linear git history after nonlinear merge?


Few commits ago I accidentally did a nonlinear merge in my master branch. I have a habit of always trying to keep a linear history, so now I would like to restore the linearity.

Here's the output of git log --oneline --graph --date-order:

* 88a4b7e (HEAD -> master, origin/master, origin/HEAD) 11
* 5aae63c 10
*   5506f33 Merge branch 'other'
|\
| * b9c56c9 9
* | 3c72a2a 8
| * 8d2c1ea 7
| * 35f124b 6
* | 7ca5bc1 5
* | b9e9776 4
| * fd83f02 3
|/
* 4fa8b2e 2
* cbdcf50 1

Same graph in Sourcetree:

git log in sourcetree

And here is a mspaint visualization of how I would like to get my master to look like — it should essentially be like I would've rebased before the merge:
(The hashes would change ofc)

wanted end result

I know that this might not be the best practice and I am familiar with the consequences of rewriting history (no one else is working on this branch though), but would still want to be able to do this. How can this be accomplished?


Solution

  • Perhaps the simplest way this can be done is to "abuse" the default behavior of git rebase. That is, without explicitly passing --rebase-merges to git rebase, it will actually remove all merge commits from the history. This allows us to get the desired result extremely easily:

    Before:

    ~/merge-question (master) $ git log --oneline --graph --date-order
    * 88a4b7e (HEAD -> master, origin/master, origin/HEAD) 11
    * 5aae63c 10
    *   5506f33 Merge branch 'other'
    |\
    | * b9c56c9 9
    * | 3c72a2a 8
    | * 8d2c1ea 7
    | * 35f124b 6
    * | 7ca5bc1 5
    * | b9e9776 4
    | * fd83f02 3
    |/
    * 4fa8b2e 2
    * cbdcf50 1
    

    Running the command:

    ~/merge-question (master) $ git rebase 3c72a2a
    First, rewinding head to replay your work on top of it...
    Applying: 3
    Applying: 6
    Applying: 7
    Applying: 9
    Applying: 10
    Applying: 11
    

    After:

    ~/merge-question (master) $ git log --oneline --graph --date-order
    * d72160d (HEAD -> master) 11
    * 90a4718 10
    * 3c773db 9
    * ba00ecf 7
    * 9e48199 6
    * 24376c7 3
    * 3c72a2a 8
    * 7ca5bc1 5
    * b9e9776 4
    * 4fa8b2e 2
    * cbdcf50 1
    

    After this, just a simple git push --force-with-lease origin master and the remote's history is back to linear.