gitsquashgit-squashgit-interactive-rebase

git interactive rebase squash into next commit


In Git I can use an interactive rebase to re-write history, this is great because in my feature branch I made a ton of commits with partially working code as I explored different refactors and ways of getting it done.

I'd like to squash a lot of the commits together before rebasing or merging the branch onto master.

Some made up commits in order from first (top) to bottom (last)

1. Initial commit on feature branch "Automatic coffee maker UI"
2. Add hot chocolate as product
3. Add tea as product. Products are now generic
4. Create in memory data store for adapter tests
5. Cry because I can't get entity framework to create a composite key. Integration tests broken.
6. Implemented composite key!!
7. All tests green and feature done!

Lets say I want to keep commits 3, 4 and 7.

Using rebase I want to "squash" commits

Ideally in the interactive rebase I would do

1. squash
2. squash
3. pick (contains the work of 1 & 2)
4. pick 
5. squash
6. squash
7. pick (contains the work of 5 & 6)

But that is backwards because squash merges a commit with its previous commit. I can't figure out how to make it squash forward.

Am I being difficult, and should I accept that won't work (I'd rather get it working), or is there a way to accomplish this?

I'm invoking this command with

git checkout My-feature-branch
git rebase master -i

Then I'm editing the list of commits that come up, and trying to finish it by saving the file and editing the editor, which typically works for me.


Solution

  • You either need to also reorder the commits so the to-be-kept commit comes before the to-be-squashed commits if this is feasible. (For an alternative, see the update at the end of the answer)

    If this is not feasible, because you then would get conflicts you don't want to resolve, just make it

    1. pick
    2. squash
    3. squash
    4. pick 
    5. pick
    6. squash
    7. squash
    

    When the squashes are done, you can edit the commit message to contain the message you like the final commits to have. Easy as pie. :-)

    You might even be able to do

    1. pick
    2. fixup
    3. squash
    4. pick 
    5. pick
    6. fixup
    7. squash
    

    Then I think there should only once the commit message editor being fired up, as with fixup the previous commit message is simply taken without launching the editor.

    On squash when the commit message editor fires, you also get both commit messages, the one from the to-be-squashed-into and the to-be-squashed commit, so you then can simply delete the commit message you don't want to keep.


    Update:

    To use the author date and commit message of 4. and 7. and thus be more what OP originally wanted, you can use this setting:

    1. edit
    2. fixup
    3. fixup
    4. pick 
    5. edit
    6. fixup
    7. fixup
    

    Then on the first break you use:

    git commit --amend -C master~4 # or any other way to reference commit number 3
    

    Then you continue the rebase and on the second break you use:

    git commit --amend -C master # or any other way to reference commit number 7
    

    Then you continue the rebase and are done.

    or to automate it:

    1. pick
       exec git commit --amend -C master~4 # or any other way to reference commit number 3
    2. fixup
    3. fixup
    4. pick 
    5. pick
       exec git commit --amend -C master # or any other way to reference commit number 7
    6. fixup
    7. fixup