Advent Day 2: Fixup Commits and autosquash

December 2, 2018

This is day 2 of my Git Tips and Tricks Advent Calendar. If you want to see the whole list of tips as they're published, see the index.

When working on a development team, it's important to try to create a pull request that's easy for your peers to review. Some people like to look at the aggregate changes - the pull request itself. But some people like to look at each individual commit as a small piece of work to get a better understanding of the code.

That's how we work on the libgit2 project: one contributor in particular likes to review each commit to understand the motivation for each individual change. So I try to craft a series of commits that stand on their own and are each reviewable. To help me get there, I use a rebase-focused workflow: when I have a series of commits that make up a pull request, git rebase will help me reorder them - or even modify them or squash them together - into something that I think best communicates my intent.

There are two useful git commands in particular that help me.

First, when I'm working on a pull request and realize that one of my previous commits could have been improved, I can make a "fixup commit".

Let's say in some previous commit abcd111, I added some variable to a new file:

int a = 42;

And after I've made a few other changes, and committed them, I realize that seems pretty magical. Why 42? I don't like magic in my code, so I want to add a comment to explain it, or maybe even make it a constant. So I can edit that code:

/* This is the answer to life, the universe and everything. */
#define MAGIC_NUMBER 42

int a = MAGIC_NUMBER;

There, that's better. Now I can git add that change to my staging area like normal, but when I'm ready to commit it, I can mark it as a "fixup commit", specifying the original commit that I'm "fixing up":

git commit --fixup abcd111

When I do, git will give it a special commit message. But why is this useful? Well, when I'm ready to push my changes up to the server, then I can use rebase --autosquash to tidy up my commits. The --autosquash flag will look for these fixup commits and set up my rebase steps to actually squash them right in with the commits they're fixing.

It will look something like this:

pick abcd111 Add some code that starts at 42
fixup 41a8ffe fixup: Add some code that starts at 42
pick 3bd40de Update documentation to reflect changes
pick 9a74a45 Add another test for the magic number validation

Even if I made that fixup commit later in my series of commits, rebase has reordered it and made it a fixup on top of that last commit. If I just close my editor and let rebase do its thing, then I'll end up with only three commits: the fixed up first commit, the documentation commit, and then the test commit.

Now I can push my changes up to a branch on the server and open a pull request for review, and my collaborators will see a nice, tidy, easy to understand history.