For everything you need to know about version control, check out Version control – Everything you need to know, on Programming Duck.
Knowledge prerequisites: This article assumes that you can already use the basics of git and know the basics of version control. If you don’t, then I recommend that you start at the beginner section of the article Version control – Everything you need to know.
Commit messages are messages that are added to commits. They consist of:
- A mandatory subject line
- An optional body
- An optional footer
Having good commit messages helps users get the information they need, as well as to find it quickly.
Commit messages in a nutshell
If you just want the quick version of how to write good commit messages, I recommend using the conventional commits convention.
I find that it results in really good commit messages that provide a lot of useful information.
You can use their recommendations exactly, or adapt them slightly to work for you.
Sidenote: Ticket ID, issues and task managers
Overall, I believe that having the ticket or issue ID in the commit message is really important. A significant amount of information can usually be found in tickets or issues. As a result, it’s usually much easier for someone to find the ticket ID and open the ticket for more information, than to continue reading the commit message.
Also, if you’re reading a ticket and you want to find the relevant commits, you can search for them if you’ve included the ticket ID somewhere in the commit message.
However, if you don’t find task managers useful, then feel free to ignore tickets altogether.
Commit subject line
The subject line should provide a very quick summary of what happened in that commit. Most often, the subject line is read when someone is scanning over multiple commits.
It’s good to list the most important information in the subject line in a concise way.
Here is a common format in places where I’ve worked:
TICKET-ID: type(optional-scope): Description
1234567: feat(view): Import and use service worker
Or, without the optional scope:
1234567: feat: Import and use service worker
This is basically the conventional commits format with the ticket ID added at the start.
Here are some commonly cited guidelines for the commit message subject line.
Use imperative tense
This means that you should use "add" or "fix" instead of "added" or "fixed".
"add HTML for about page" instead of "added HTML for about page".
This is because:
- Imperative tense results in shorter commits. For example "fix" is shorter than "fixed" or "fixes".
- The source code of git uses imperative tense. In other words, this can be the considered the original convention for modern version control.
- Git commands like
git revertautomatically generate commit messages written in the imperative tense. If you don’t manually edit those commit messages and you don’t use imperative sense, you’ll have inconsistencies across your commit messages.
Another reason I’ve seen mentioned is: You can think of commits as commands or patches to apply. For example, when rebasing, a commit message in the imperative tense describes a command that you’re about to apply, such as "fix X". Since the commit is about to be applied, but isn’t applied yet, this feels like the most appropriate tense. Other commands are similar.
However, realistically, it doesn’t matter. It’s a minor detail. If you and your team prefer a different tense, that’s completely fine.
Keep it short
Shorter is better. All things being equal, a concise description is better than a long description, especially since a reader is likely to be scanning over many commit messages.
Also, some version control providers truncate subject lines to 72 characters. Originally this was due to legacy reasons, such as terminals being "smaller" in the past than they are today. Many providers have since changed to show as many characters as your monitor allows. However, some still truncate to the subject line to 72 characters.
So, it’s good to keep the subject line short when possible.
However, in-line with being pragmatic, I don’t believe that you always need to constrain it to 72 characters. If you really need more than 72 characters on rare occasions, then use them. Consider what would be worse: Leaving out key information or having a slightly longer subject line?
Optional ticket ID
In most places where I’ve worked, the ticket ID is the first thing in the subject line.
This helps ensure that it’s always included because it’s the first thing we write. If we had to write it in the commit message body, we might forget it sometimes.
Regardless, my personal preference is to include it in the footer of the commit message.
That’s because I don’t personally find the ticket ID useful when scanning over multiple commit subject lines. It’s only useful when I want detailed information about a specific commit or a set of related commits. At that point, I can view the details of a single commit to find the ticket ID. Also, subject lines without the ticket ID are shorter.
Alternatively, you may find the ticket ID useful in the subject line. It clearly shows which commits are related and are part of the same ticket.
In the end, it’s up to you to decide which you prefer. All options are valid.
Finally, consider using a git hook to insert the ticket ID for you, or to validate that it’s there. This ensures that you won’t forget it and it automates some work. One way of doing this is to include the ticket ID in the branch name. Then, a script can read the branch name, parse the ticket ID and add it to your commit message.
This seems to be a fairly new trend. Some projects have started including emojis in the subject line. The emoji depends on the type of commit.
🐛 fix(x): Fix bug X
This could be a pretty good idea. A visual aid like that can instantly show you what type of commit you’re looking at. When looking over the commit history of a project, this could prove very useful.
One small downside is that this won’t be helpful until you learn what each emoji means in your codebase. However, for many long-term projects, this shouldn’t be an issue.
If this seems useful to you, consider giving it a try.
This refers to the types described in the conventional commits specification.
The types describe the purpose of the code changes made in that commit. For example:
- fix – The commit fixes a bug or is part of multiple commits that fix a bug.
- feat – The commit works towards a new feature.
- feat! – The commit works towards a new feature and also introduces a breaking change.
This information is very useful when scanning over the commit history of a project. Overall, the quicker you can get an idea of what each commit does, the better.
The conventional commits specification requires those types. It also allows the use of custom types. As of the time of writing, conventional commits links to the Angular convention for additional commit types which you may want to consider.
Scope is another useful piece of information to quickly specify where (in which area of the codebase) changes are being made.
You can think of scopes as distinct areas of the codebase or distinct functionality in the codebase where you often make standalone changes.
Both conventional commits and the Angular convention recommend using some high-level nouns, such as:
Unless your project is also a framework like Angular, you’ll probably use different scopes. Feel free to use whatever works for your project.
Personally, I haven’t put too much thought into this (yet) for my personal projects. I just use some scopes that I feel are useful. For example:
- dependencies (npm packages)
- Any other "top-level" folders or areas of the project where I frequently make standalone changes.
I imagine that as a project grows, more obvious scopes will start to appear.
Capitalized vs lowercased
The conventional commits specification doesn’t enforce a particular casing for the description. It only states that you should be consistent with your casing.
Therefore, you can start the description with a capital letter or a lowercased letter. It’s all up to your preference.
For example, both of these are valid:
- build(config): disable rule x for ESLint
- build(config): Disable rule x for ESLint
My personal preference is to start the description with a capital letter. I just find it easier to read. The capital letter gives a clearer indication of where the description starts.
However, you may have the opposite opinion and that’s fine. You can use whichever you prefer.
No full-stop at the end of the description
Don’t end the subject line with a full-stop.
It’s an unnecessary additional character. Since subject lines should be short, it’s better to exclude it.
Also, full stops shouldn’t be used for things like subject lines and headings, except in particular cases. For more information on this, please see Full Stops in Titles, Headings and Captions.
The content of the description will always vary. It depends on what happened in the commit.
In essence, the description should answer this question: If someone was looking at the commit subject line in the future, 6 months from now and they wanted to understand what you did as quickly as possible, what should the subject line say?
If you can answer that question well, then you’ll probably be able to write a good description. You may also need to re-word it so that it’s sufficiently short.
Please don’t worry if coming up with good commit descriptions seems difficult. It’s actually a very difficult thing to do. Even people that have had a lot of practice with it still struggle. Just try your best, you’ll definitely improve over time.
Commit message body
The purpose of the commit message body is to help a developer, examining the commit in the future, understand as much as possible about why the particular changes were made.
The commit message body should include information such as:
- Why the changes needed to be made.
- How the changes were made (if it’s not obvious from the commit diff).
- Why the particular implementation was chosen (if it’s not immediately obvious).
- Any limitations of the current code.
In addition, consider these points (adapted from the section "information in commit messages" from the OpenStack Git Commit guidelines):
- The reviewer may not understand what the original problem was.
- The code may not be as self-evident / self-documenting as you believe.
For even more points to consider, I recommend reading looking at "information in commit messages" from the OpenStack Git Commit guidelines.
As already mentioned in Version control – How much do you need to know and use?, it’s probably better to be pretty good with your commits rather than absolutely perfect.
As a result, personally, I rarely write commit message bodies. Most of the time, I believe that the changes I made and the reasons for them are obvious from the ticket. If a developer wants more details, I expect them to open the ticket and find them there. Therefore, I don’t believe that the time required to write comprehensive commit message bodies is worth it.
Otherwise, if I feel that something may be confusing, I’ll probably write a short commit message body. Usually it’s an explanation of why the changes had to happen or why this implementation was used. I may also include links to external resources for more information.
But of course, in your case, you can write commit messages that are as comprehensive as you want. You know what’s best for your own project.
Formatting guidelines for the commit message body
It’s recommended that you separate the commit body and the commit subject with a blank line in-between them. Some reasons for this are:
- It logically separates the commit subject from the body. Without the blank line, you couldn’t be as certain that the second line was intended to be part of the subject or part of the body.
- It’s easier to read.
- I’ve heard of many conventions stating that there should be a blank line in-between, but I’ve never heard of the contrary. In other words, you can consider this to be a convention.
- I’ve heard that some tools rely on having a blank line between the subject line and body, one example being Vim. (Although I’m not sure if this issue still exists.)
Additionally, the commit message body should be wrapped at 72 characters. This means that if a word makes the line longer than 72 characters, you should move it to the next line instead. The reasons for this are:
- Similar reasons as the commit subject line being limited to 72 characters.
- It’s easy to do, so you might as well follow the convention.
- Both IDEs and version control providers may not optimise paragraphs of text for easy reading. For example, your IDE (or other people’s IDEs) may not "soft wrap" long lines of text. This means that they may be very difficult to read unless you explicitly create new lines.
However, if you feel that this point is no longer necessary, then please feel free to ignore it. (Also, please let me know your reasons for it in the comments.)
Commit message footer
The commit message footer is there for metadata, such as references to ticket IDs, or anything else your organisation requires.
This would be my preferred place to include the ticket ID.
Most conventions recommend having key value pairs, with one pair on each line, such as:
TicketID: 12345 MyKey: MyValue
In terms of other guidelines, similar to the commit body, it’s recommended to separate the footer and the body or subject line with a blank line.
As mentioned earlier, if possible, I recommend using a git hook to insert the metadata for you and / or verify that it’s there. That way you can’t forget to include it.
For additional information and examples, see "including external references" from the OpenStack Git Commit guidelines.
That’s it for this article.
If you have any feedback, or even counter-arguments, please let me know in the comments.
Next, if you want to know more about version control, please see the article Version control – Everything you need to know.