Squash Commits with Git
I'm not a git expert but I know enough git to get by, and surely know enough git to appreciate its ease of use over svn. A while ago I published some basic git commands to that go slightly beyond basic cloning and commits, and should handle most git interactions. Today's mini git lesson involves squashing multiple commits into one, allowing for easier pull request review and merge management.
Start by making changes to the feature branch you're doing work on. Let's assume that these changes span a few commits and I want to consolidate them into one commit. The first step involves making sure the master branch is up to date with the destination repo's master branch:
# switch to master branch git checkout master # ensure our master is up to date git pull remoteRepoName master
With the master branch up to date, we'll use git rebase to consolidate:
git rebase -i master
That command will show a list of each commit, as such:
pick fb554f5 This is commit 1 pick 2bd1903 This is commit 2 pick d987ebf This is commit 3 # Rebase 9cbc329..d987ebf onto 9cbc329 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. #
Edit the summary shown to you by the rebase command, leaving the commit you want to be the main commit as "pick" and changing all subsequent "pick" commands as "squash":
pick fb554f5 This is commit 1 squash 2bd1903 This is commit 2 squash d987ebf This is commit 3 # Rebase 9cbc329..d987ebf onto 9cbc329 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. #
Write/quit past the editor twice (the second screen would allow you to change the commit message, though I like to keep it the same). At this point, your commits are squashed into one. Run the following command to force a push of the new, consolidated commit:
# Force a push git push -f
This forced push updates the source repository and our commits have become one. If you had already submitted a pull request at GitHub, the pull request would now show only one commit! With one consolidated commit, code review becomes much, much easier!
Please note that it’s not a very good practice to squash commits that have already been pushed to public repository (like github) – if anyone had already forked or cloned the repo before they were squashed they may end up with broken commit tree, unable to merge any new changed (without some more tricky cleaning commands)
So it’s much better to remember to squash commits before they are pushed anywhere.