Kevin's Worklog

Documenting the Daily Stream

Interactive Rebase Commit Squashing

I love Travis-CI for freeing me from having to maintain a Jenkins server, but I do find it a pain to have to make a commit to my GitHub repo in order to test/add a new step in the Travis config file. Their newly announced architecture (using Docker) should make it possible for them to offer a build container for local testing in the future, but for now it’s an iterative commit-then-test process.

The result of this is that I have a bunch of GitHub commits that are really just me testing something. I’d like to be able to condense them all into one commit… hence, I’m learning about squashing Git commits with the interactive rebase feature.

I’ve just started using simple rebasing not too long ago, but I’ve never really squashed commits before so I’m documenting the process for myself (so that I can return to it when I forget it… which I’m sure I will).

To keep things simple, I’ll be working with ‘develop’ and a ‘master’ branches. All my Travis testing has taken place on the ‘develop’ branch and I’d like to condense all this into a single commit in my ‘master’ branch (which is already littered with these sorts of commits, but I’d like to do a better job about having cleaner commits going forward). So, to start, let’s checkout the ‘develop’ branch and interactively rebase it against the ‘master’ branch.

git checkout develop
git rebase -i master

This gives me something like:

pick 7a31f53 Improved Travis script
pick 464171b Explicitly open AWS Carbon port for Travis
pick 9e770be Fixed typo; added workaround for Ubuntu mirror issue
pick f539b9c Removed exit calls in Travis script
pick 5de58de Added travis_wait to Travis config and refactored config
pick b719d96 Added travis_wait to Travis config and refactored config
pick 472de9e Fixed typo in Travis config
pick 62caf9d Reshuffled Travis configuration steps
pick cec0c3b Move Travis tests into external script
pick 897271c Fixed Travis AWS instance cleanup
pick 859af0b Added additional messaging to Travis config
pick 8b52e1e Bumping up number of times test is attempted before it's considered a failure
pick 69bc970 Tweaking Travis test script

# Rebase f69c45e..69bc970 onto f69c45e
#
# 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
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

What I’ll need to do is go through all the commits after the first one and mark them as ‘squash’ or ‘fixup’. In this case, I’ll use ‘fixup’ but if I wanted to preserve the messages (in the case of something less trivial than Travis testing) I would use ‘squash’. So, when I’m done, I should have something like:

pick 7a31f53 Improved Travis script
fixup 464171b Explicitly open AWS Carbon port for Travis
fixup 9e770be Fixed typo; added workaround for Ubuntu mirror issue
fixup f539b9c Removed exit calls in Travis script
fixup 5de58de Added travis_wait to Travis config and refactored config
fixup b719d96 Added travis_wait to Travis config and refactored config
fixup 472de9e Fixed typo in Travis config
fixup 62caf9d Reshuffled Travis configuration steps
fixup cec0c3b Move Travis tests into external script
fixup 897271c Fixed Travis AWS instance cleanup
fixup 859af0b Added additional messaging to Travis config
fixup 8b52e1e Bumping up number of times test is attempted before it's considered a failure
fixup 69bc970 Tweaking Travis test script

# Rebase f69c45e..69bc970 onto f69c45e
#
# 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
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

When I exit my text exitor, I’m told the commit has been made. If I’d selected ‘squash’ instead of ‘fixup’ I would have been presented with a new window in which to type my commit message (and all the previous messages would now be squashed into one big message, ready for me to edit).

I assume, if I’d wanted to provide a better commit message than my first one above, I could have marked the first commit as ‘reword’ instead of ‘pick’ (and kept all the later ones as ‘fixup’). I assume, then, I would then have been presented with an opportunity to improve my commit message (before the commit was finalized).

But, as it is, git log on the ‘develop’ branch now says:

commit 21504618723066fb9bd008c5b5cadcbf7f592028
Author: Kevin S. Clarke <ksclarke@gmail.com>
Date:   Thu Jan 1 00:59:28 2015 -0500

  Improved Travis script

commit f69c45ea737777250562f5b9c6b86bd009d436f8
Author: Kevin S. Clarke <ksclarke@gmail.com>
Date:   Wed Dec 31 21:13:43 2014 -0500

  Added a comment in Packer.io config

And, git log on the ‘master’ branch still says:

commit f69c45ea737777250562f5b9c6b86bd009d436f8
Author: Kevin S. Clarke <ksclarke@gmail.com>
Date:   Wed Dec 31 21:13:43 2014 -0500

  Added a comment in Packer.io config

Now, I need to merge the newly rebased ‘develop’ branch into my ‘master’ branch. To do that, I just type:

checkout master
git merge develop

Then git status tells me I have one new commit that I can now push up to my ‘origin’ repository:

git push

That’s it. I have to say that I found the idea of rebasing on ‘develop’ against ‘master’ a little counter-intuitive at first. But if you think of it as a two step process (e.g., first step: clean up ‘develop’ and, then, second step: merge into ‘master’), it makes more sense.

Caveats for the Future

Ideally, the above would have been done on a feature branch and then merged into ‘develop’, later to be merged into ‘master’, but I started making edits on ‘develop’ before realizing I’d want to squash them.

To clean up this mess, I’ll go ahead and delete the ‘develop’ branch and create a new ‘develop’ branch from my newly merged ‘master’. In the future, I’ll just use (and then delete) a feature branch (since they’re temporary by design).

Just for completeness, the final steps are:

git branch -D develop
git push origin --delete develop
git checkout -b develop
git push origin develop

Hopefully, these steps are just a workaround for my lack of planning (to squash) and not something I’ll something I’ll have to do again. This is a new repository so shouldn’t have anybody who has cloned and is working on the previous ‘develop’ branch. I know… Bad developer. Bad!