In Git, How can I rebase + squash a branch that has multiple merge commits in it's history without cherry-picking onto a totally new branch

Viewed 51

The Problem

I want to be able to take a branch originally branched off master with multiple merges from the master so it appears as a single commit on the master. We do this in order to take a developer's branch and produce a clean history in the master (once it has been tested).

I know how to this by creating a new branch off the tip of the master (i.e. the "master" pointer ) and cherry-picking onto it (resolving conflicts as necessary, but it should be possible to do with the rebase command only (shouldn't it?). The assumption here is that the master itself has not been rebased.

As an example I want to take the following history, below with branch master and branch doodle:

Original History

  *  commit K, branch doodle, merge commit (conflicts)
 /|
* |  commit J, branch master, 
| |
| *  commit I, branch doodle
* |  commit H, branch master
| |
| *  commit G, branch doodle,merge commit (conflicts)
|/|
| *  commit F, branch doodle
* |  commit E, branch master
| |
| *  commit D, branch doodle,merge commit (no conflicts)
|/|
* |  commit C, branch master
| *  commit B, Branch doodle 
|/
*    commit A, Branch master

And turn it into the following history on the master where the entire chain of commits on doodle are squashed and rebased onto the HEAD of master as shown below. Since the head of branch doodle has commit K and all conflicts between master and doodle have been resolved, doing the below ought to be as easy as a single command. Also it does not mess with the history of the master that has been published.

Desired History

*    commit L, branch master,(squash commit B,D,F,G,I,K)
|
*    commit J, branch master
|
*    commit H, branch master
|
*    commit E, branch master
|
*    commit C, branch master
|
*    commit A, branch master

An alternate would be to generate the history shown below and than squash commits B', F', G' I', J', K' before merging back to master. This approach below, however ,seems to add an extra step that rebase ought to be able to handle (albiet with conflict resolution).

Alternate History

| *  commit K', branch doodle (possible conflict resolution)
| |
| *  commit I', branch doodle (possible conflict resolution)
| |
| *  commit G', branch doodle (possible conflict resolution)
| |
| *  commit F', branch doodle (possible conflict resolution)
| | 
| *  commit B', branch doodle (possible conflict resolution)
|/
*    commit K, branch master
|
*    commit H, branch master
| 
*    commit E, branch master
|
*    commit C, branch master
| 
*    commit A, Branch master

What I have tried

Currently I am creating a new branch off the master tip and cherry-picking B, F, and I onto it. This works but is is more complicated than I believe is necessary as I have to re-resolve merge conflicts. Now, I am aware of the --rebase-merges flag for rebase. However, for some reason, I cannot get --rebase-merges to produce the "desire history" or even the alternate history. Chances are I am selecting the commits with rebase -i HEAD~N --rebase-merges incorrectly.

Question

How can one easily easily go from original history to desired history?

Also, if the master in original history had a few extra commits after the last merge into doodle, will this approach allow for conflict resolution before pulling into master?

1 Answers

This is easily done with reset --soft

git checkout doodle
git reset --soft master
git commit -m "doodle, as a single commit"

No hassle. If there are more commits into master that are not part of doodle, then merge before doing this.

Related