Whether you’re working in a large team or solo, effective collaboration is predicated upon keeping a clean version control history. (Yes, dealing with your own past follies also counts as collaboration.) Here we’ll consider a clean history to be one in which commits are Conventional, Context-Providing, Complete, and Focused. Each of these four rules (or guidelines) is described in detail below.

It is important to note that these rules only apply for commits that are shared and/or eventually merged into a stable branch (usually master). But this is not the only situation in which commits can be used — quick and dirty commits are very helpful when considered as drafts. These rules are not meant to dissuade one from committing early and committing often. During the early divergent phase of problem solving, when many approaches are considered, following these rules would only inhibit the creative process.

The rules

Conventional

Every commit message should follow the conventions of its project.

Photo by Jessica Ruscello on Unsplash

There are a few very basic conventions that almost every project follows, but the specifics vary greatly from project to project. What’s important, is to follow whatever conventions have been agreed upon. Are all commit titles (the first line of the commit message) in the project in imperative mood? Every new title should also be in the imperative mood. Are commit titles prefixed by the general nature of the change (e.g. documentation/bug/feature/refactoring)? Every new commit should also have the prefix.

Context-Providing

Every commit message should explain the commit’s reason for being.

Photo by Markus Spiske from Pexels

The message should provide (within reason) all possible context for the commit. Here are some questions that the message should answer:

Why is this change necessary?

What assumptions is it based on?

Were any alternative solutions considered?

Why was this solution chosen from among the alternatives?

Are there any non-obvious implications that this change has?

References to other relevant information may be included. Some examples:

A ref to another commit. E.g. a bug fix may refer to the commit that introduced the bug for providing additional context. Usually the abbreviated SHA (e.g. c71598f) of the commit is sufficient, but more comprehensive styles, such as the one used for git itself, can be considered.

Link to library/language or any other documentation. Often it also makes sense to actually quote the most relevant bits within the message as well, to avoid losing the context when the link stops working.

Link or a reference to the relevant ticket/issue for which this commit was created.

Complete

Every commit should be complete.

Photo by Markus Spiske from Pexels

This means that every commit should leave the system in a state where: 1) the code compiles, 2) its tests pass (note that requiring end-to-end tests to pass isn’t always reasonable, but lower level tests should always pass), and 3) the linter does not complain. This means, for example, that a bug fix and the related test changes should be in the same commit.

Focused

Every commit should only do a single thing.

Photo by Paul Skorupskas on Unsplash

What this means precisely is often arguable, but what it means in general is usually well understood. For example, refactoring should be separate from an addition of a feature. Formatting changes should be separate from bug fixes. A commit should only fix a single bug at a time, not two or three.

Note that keeping commits Complete forces one towards bigger (less in terms of lines and more in terms of logical changes) commits, whereas keeping them Focused pushes towards smaller commits. This is so because essentially they’re different perspectives or gauges for reaching the same ultimate goal — having a commit that does just enough and not a bit more.