Advent Day 2: Fixup Commits and autosquash
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.