Search code examples
gitgit-mergegit-rebaserevision-history

How can I move a commit across a merge commit?


Suppose I have a repository like this:

I --- C --- M    master
  \       /
   `- A -´       topic

where M is a merge commit incorporating topic into master.

Later I find a bug in C, so I make a commit fixing it on the master branch, on top of M:

I --- C --- M --- C1    master
  \       /
   `- A -´              topic

but ideally I would like the history to look like this:

I --- C --- C1 --- M    master
  \              /
   `- A --------´       topic

How can I rewrite the history so that C1 appears before the merge M?

I could remove M, apply the patch made by C1, and merge topic into master again, resolving all the conflicts again, but I'd like to avoid the effort, and I'd prefer to preserve the original commit information (author, date, etc.) if possible, which rules out doing git commit again. I hope it's possible with git rebase, but I failed, with either one or both of -p and -i.


Solution

  • The best way I've found is:

    git checkout -b tmp master^^
    # Now tmp is on top of C
    
    git cherry-pick master
    # Now tmp is on top of C1', a copy of C1
    
    git rebase -p tmp master
    # Now master is on top of a merge commit from C1' and A
    
    git branch -d tmp
    

    I don't know why git rebase cannot do this all at once without git cherry-pick, but at least I know this works.