“Anyone moderately familiar with the rigours of composition will not need to be told the story in detail; how he wrote and it seemed good; read and it seemed vile; corrected and tore up; cut out; put in; was in ecstasy; in despair; had his good nights and bad mornings; snatched at ideas and lost them; saw his book plain before him and it vanished…”
– Virginia Woolf
Good code is easy to understand. In a good codebase, changes to code are easy to understand. This requirement is fundamentally at odds with the messy, organic way that code emerges from the mind of the developer. Thankfully, git comes bundled with tools to help developers mold their changes into a concise, logical structure.
This is the first in a series of blog posts on crafting good commits. In future posts, we’ll explore 3 options to basic git commands that can help us make good commits:
git add --patch,
git merge --squash, and
git rebase --interactive.
But first: what is a commit and what makes it good?
Understanding the past:
Software development is a team effort and code itself changes constantly. Most authors write for the general public, whereas code’s authors and audience are usually one and the same. While prose can be considered finished (or perhaps merely abandoned) at the time of publication, software must be supported. Older versions must be patched; breaking changes reverted. These facets of software development have led to an engineering standard: code repositories must have logically coherent history of changes. These changes come in discrete steps, or commits. Commits are comprised of a diff, as well as some metadata, usually in the form of a commit message. Commits form the basic, atomic units of change in a codebase. In git and other distributed version control systems, commits are nodes in a directed acyclic graph.
The rigours of composition:
At odds with the need for a logically coherent history of a piece of software is the way it is created. Crafting good commits is usually an afterthought to the initial work of writing code. Inspiration is fleeting; better to get your ideas down before they’re gone forever. However, a few sketches of code can quickly turn a working tree into a tangled mess of disparate, disconnected changes.
Good commits have some useful properties.
Commits should comprise a single logical change. Good commits are cohesive and self-contained. They encapsulate a single, logical change to the codebase. Commits to master or stable branches should have the added property that they result in a passing build.
Good commits can be reverted. If a new feature has a bug or introduces a regression, it should be possible to revert a commit or series of commits to rollback to a previous good state.
Good commits are portable. It should be possible to port a commit or series of commits from one branch to another. For example, if a feature needs to be backported to a previous release, it should be clear which commits correspond to the feature, and it should be possible to apply these commit(s) to the release branch (say using
Now that we understand a bit more about commit is, what makes a commit good, and useful, we can start exploring some tools that can help ease the tension between turning ideas into code, and making good commits. Stay tuned.