It is distressingly common for me to see commits in a git repository which look something like:
commit 6a69053d2419b311cc38c9fdbbbac75b5a62c4fa Author: dev-dude <email@example.com> Date: Sun Mar 29 14:05:46 2015 +0200 fix missing commas
Such a commit will often be half a complete thought (if even that much), have no tests, and a message which explains exactly nothing about why the change was made, or what its intended consequence are. We can do a lot better.
A good commit is much like a good sentence: a well-formed, grammatically correct, complete expression of a single thought. In the case of a commit, this means that it should:
- make exactly one cohesive change to the code
- include tests to verify the change
- have a title which clearly—but briefly—indicates what was done
- have a message which describes why it was done
The most important virtue of a well-formed commit is good cohesion, or: doing only one thing. This could be a refactoring in preparation for some new feature to be added, it could be adding that feature, it could be fixing a bug. The actual size of the commit doesn’t matter so terribly much (though smaller is generally better), but that it clearly represents a single change does.
As a short-cut for deciding whether your change is cohesive or not, consider how easily you could come up with the title for the commit. Can you, in 70 characters or less, state clearly what your change does. If not, it’s probably not very cohesive. If you find yourself tempted to make a list as your commit title, you definitely don’t have good cohesion.
In such cases, stop and ask yourself: “what is the smallest thing I could possibly do to this codebase which would be a clear improvement?”. In a single pull request, it’s not at all uncommon for me to have more than one of these kinds of commits:
- clear up some stylistic problems with old code
- refactor various parts of the code I’m about to touch
- add a new class which will be part of a new feature I’m about to add
- tie a bunch of prior commits together in a way which makes the new feature visible to the user
- update documentation
Of course, depending upon the exact nature of the work, I may not need all of those, but it will be very common for me to have at least a few of them.
In my opinion, tests deserve as much care as any other code, and it’s quite possible to have too many or too few. That’s a topic for another time. For now, I just want to point out that any new commit should include any appropriate tests related to that new code. Of course, it’s possible that it’s appropriate not to add new tests (e.g., for a refactoring), but if a commit doesn’t have tests, it should only ever be because you considered it, and decided there weren’t any new tests which were needed.
Git actually does have a proper format for the commit message; it’s not just arbitrary. Remember that git is a command-line tool, and is often used in environments where space can be limited. To that end, there’s a definite format.
In this format, the first line is considered a title, and it should be a present-tense, imperative sentence stating what changed with this commit (e.g., “Add the FizzyWizzBanger class”). This should be less than 70 characters because git will only show that many in the various “one-line” versions of its commit logs. Ideally, it will be sufficiently differentiated from other commit messages so that you can readily figure out which one of several commits it is, even out of context.
The question of “what” the commit changes should really be summarized by the title, and made obvious from the code. In the commit message, you should focus on why you made this change. Perhaps it fixes a certain bug. If so, go ahead and explain—in a sentence or two—why this change fixes that particular bug. It doesn’t hurt to mention the bug number here either.
Don’t be afraid to use simple formatting here either. A good many Git-related tools support markdown formatting, so feel free to use whatever you like. Just be sure to leave an empty line after the title since that’s what git will be expecting for its longer-form messages, as will other git-related tools.
Building up your codebase using a series of well-formed commits has a number of major benefits.
- other developers will be able to understand what changed and why much more easily with small, focused commits with clear messages
- it creates a commit log which reads well, whether using the full log format or just the “one-line” version
- it is much easier to test small changes
- not allowing any commits without proper tests means not playing catch-up with a massive and under-tested codebase later on
- tracking down problems later is much easier if you can isolate the problem to a single commit, and that commit isn’t massive
- merging branches is much easier (and git is much better at avoiding conflicts altogether) if you work in smaller increments
So, before you start typing… stop to think about what small, specific improvement you’re going to make to the codebase next, and make it as perfect and tidy as you can before moving along to the next one.