gitrebasesquash

Why does 'Git rebase' squash commits into a previous commit, instead of the following commit?


When I squash commits in a branch (using git rebase -i), I'm always annoyed that the squashed commit is combined with the older commit instead of the newer commit.

I don't understand why it was designed this way. When I commit a work in progress (WIP), it represents code that doesn't compile or isn't finished. When I finally commit the "it finally works!" commit and squash before merging, it makes far more sense for these WIP commits to be combined into the "it finally works!" commit, instead of being combined with the previous commit. Squashing a WIP essentially 'breaks' the previous commit with code that I know doesn't compile.

To get around this, my workflow is to squash the commits from "it works!" all the way back to one before the first WIP commit. But isn't this silly? What are other people doing, that makes squashing a WIP to a previous commit make sense?


Solution

  • With current Git versions (included from version 2.32.0 onwards; introduced in commit 9e3cebd97cbd47909e683e617d5ffa2781f0adaa), you can specify fixup -C in the todo list of an interactive rebase to select the commit message to be used when squashing/fixing up, e.g.

    pick 456789 Finished topic XYZ
    pick abcdef WIP your incomplete commit
    fixup 012345 WIP resolving compiler errors
    fixup 6789ab WIP adding unit tests
    fixup cdef01 WIP refactoring
    fixup -C 234567 Implemented feature ABC
    pick 89abcd Another feature
    

    Will end up with 2 commits: "Finished topic XYZ" and "Implemented feature ABC", without having to adapt any of the commit messages manually (so removing all those "WIP" lines is avoided).

    If you still want to edit the final commit message, use -c instead of -C:

    pick 456789 Finished topic XYZ
    pick abcdef WIP your incomplete commit
    fixup cdef01 WIP refactoring
    fixup -c 234567 Implemented feature ABC
    pick 89abcd Another feature
    

    Any other additional steps can be performed with an exec line:

    pick 456789 Finished topic XYZ
    pick abcdef WIP your incomplete commit
    fixup cdef01 WIP refactoring
    fixup -C 234567 Implemented feature ABC
    exec git commit --amend --reset-author -CHEAD
    pick 89abcd Another feature
    

    Before Git 2.32.0 you could work around this limitation with the following todo list:

    pick 456789 Finished topic XYZ
    pick abcdef WIP your incomplete commit
    fixup cdef01 WIP refactoring
    fixup 234567 Implemented feature ABC
    exec git commit --amend -C 234567 # commit id from line above
    pick 89abcd Another feature