Git commit messages are how we communicate to our future selves. They help you understand why a certain line of code was added to the code base. That's why knowing how to write a good Git commit message is important.
We've all been there; "Git is confusing", "Why can't I just push to the main branch?", "No one will ever read this message". These thoughts are normal and most people that have learned Git can probably relate. You might not appreciate good commit messages until you work with a code base that's been around for years. Or when you return to an old project of yours.
In this article, I'll show you how to write Git commit messages that you and your colleagues will love.
Why Git Commit Messages Are Important
Have you opened up an old project and checked the commit messages with git log
? If not, I recommend you do that now. If they're not filled with "Fix styling" or "Add endpoint" then great job! If you're like the rest of us, read on 😄
Or perhaps you joined a company with a code base that's been around for a few years. And you're trying to understand why some piece of code was added. Will you break anything if you change it? In this case, a good commit message is invaluable.
Simply put, by writing good Git commit messages you are future-proofing yourself and your colleagues. And as developers, we are reading a lot more code than writing it.
Reading vs. Writing Code
Uncle Bob (Robert C. Martin - author of popular programming books such as Clean Code) says
“Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write.” (ref.)
At times, to understand the code you will also need to read the commit message. I hope that you now see that the effort of writing a good commit message is worth it!
How To Write Good Git Commit Messages
Now that you're convinced that commit messages are important, let's look at how you can write them in a way that your colleagues will love!
A commit message consists of two parts: the subject line and the body. Here's an example from the bitcoin repo.
commit eb0b56b19017ab5c16c745e6da39c53126924ed6
Author: Pieter Wuille <pieter.wuille@gmail.com>
Date: Fri Aug 1 22:57:55 2014 +0200
Simplify serialize.h's exception handling
Remove the 'state' and 'exceptmask' from serialize.h's stream
implementations, as well as related methods.
...
The first line is the subject and what follows after the new-line is the body.
Focus On The Why
This is the most important rule. And it's also the most common mistake. Commit messages should explain why the code was added, not what was added. To see the what I can just read the code. But to understand the why I can only guess.
Even if I'm the author of the code, I will eventually forget the why.
Use Imperative Mood In The Subject Line
Imperative mood means "used to demand or require that an action be performed". This is the mood Git uses by default, and so should you.
For example, the git revert
command will create a so-called "revert commit" for you. This commit will have a subject line with an imperative mood - "Revert 'Add field to schema'".
You can think about it that you are telling the system/app/etc. how to behave. This might be awkward at first but you'll get used to it.
Note: You can write in whatever mood you prefer for commit bodies.
Restrict Subject Line Length to 50 Characters
Have you ever seen a commit message on GitHub that ends with ...
? That's because GitHub truncates the subject line if it goes above 72 characters. The rest of the subject line moves to the commit body.
50 characters is the recommendation by GitHub. The limit exists to make the commit more readable. And you're forced to explain your code changes in a concise and comprehensible way.
If you're having problems explaining your code changes with 50 characters you might be committing too many changes at once. If this is the case, you should split the commit into multiple commits.
Conclusion
These are the three most important rules to follow when writing Git commit messages. If you follow these rules your colleagues will quickly understand what the commit is about.
To summarise:
- Focus On The Why
- Use Imperative Mood In Subject Line
- Restrict Subject Line Length to 50 Characters
To learn more git commit best practices, check out this excellent blog post.
Top comments (29)
Yep, definitely nice tips.
I also recommend the Conventional Commits Pattern.
And keeping the commits atomic (but, maybe I am baised as I gave talk on that topic 😁)
I love Conventional Commits. They also works great with semantic-release.
Love that link, thanks for sharing it! Yes, agree. Atomic commits are the best :)
It would be nice to see good example of a commit message, since Bitcoin commit that you've added is not good example. It shows what was done, not why which is later explaned that it's not a good thing.
Good point! I might edit this post later and add it :) Thanks for the advice.
Yes please, if you advise this, included examples would be so helpful.
Some commit convention also requires to add the step to reproduce, previous result and new result in case of commit fixing a bug.
On my side, I would prefer keeping behavioral information in the issue tracker, and keep in the commit only the technical explanations, like why this modifications of code fix the issue #426 for instance.
What is your opinion about this ?
Hi! Good question!
Personally I think such information could go in the Pull Request and not in the commit. Commits should be atomic and describe, like you said, the technical changes.
Issue trackers are good for tracking the issues. By referencing a ticket in the issue tracker you are creating a dependency. Later, you might decide to change issue tracker system, and all your ticket references in Git are useless. So you should not put all information in the issue tracker.
It's a fine balance and I agree with what you said :)
Your point on changing issue tracker system and losing all the info you'd referred to from your commits is a good argument in favour of making sure that the salient info is in commit messages not pull requests, though.
Imagine in a few years you decide GitHub is no longer right for you. There may be a good migration path to some other provider you choose, or there may not. As you already have your Git repo checked out, you can just push it to some other Git host, or self-host, and you've not lost any of those years of commit messages, even if the information on GitHub pull requests wasn't able to come along.
This is a good point. Again, it's the question of balance. Should you put all the context everywhere? Probably not. For various reasons. Should you put enough context in a commit for it to make sense? Yeah, probably.
Changing systems is always a risk that we have to live with. And hope that if/when we migrate we can migrate all data :D
Regarding changing issue tracker, I went through (bugzilla ?)->Elementool->Redmine->Jira->Another Jira.
I think Bugzilla database has been lost as I never saw it but I've heard talking about Bugzilla in coffee long long time ago.
If I remember well, Elementool database has been ported to Redmine with some "one shot script". Elementool Ticket number was written in a Redmine custom field, so we can make the link between commit message and ticket. In commit message, Elementool issues was with the form "#1234". So we use the convention "RM1234" for new Redmine ticket. It was easy to know where the ticket came from.
For first Jira migration, Redmine was an on premise server. So for our team we kept it alive in read only access, create new ticket on Jira and that's it. It was a mess for a few weeks, time for WIP ticket to be closed, but not that an issue.
Some other teams have duplicated their Redmine database to the Jira instance, which was the worst idea ever as their brand-new Jira instance has been flooded with thousands of useless legacy tickets and years of custom fields entropy...
Migration from Jira1 to Jira2 was quite smooth, as it was Jira propose an import system.
Looking back, having a database migration once every few year is not really an issue compare to every day constrains on commit message. Steps to reproduces HAVE to be in ticket anyway, because it is the interface between customer, hotline and developers, so better keep it in a single place than duplicating the information both in ticket and in commit message.
Nice article about commit messages. The basic rules you describe are important. I did the same until I found out about Conventional Commits Pattern. I would suggest that you try it at least once.
It has several benefits in terms of readability, maintainability, or if you want to revert to a specific point without investigating much.
Furthermore, it works with SemVer (semantic version). This means, you can generate changelog files automatically!
Some months ago, I wrote an article on Medium about that pattern: betterprogramming.pub/write-better...
Check it out! Until today, the feedback I got was really positive. I have also adopt the pattern at work and everyone loves it.
So I probably SHOULDN'T add 7 commits in a row with variations of "Work, damn you!" then, right? ;-)
🤷♀️🤣😇 "Works for me"
"Works on my machine" !!
Whatever floats your boat :D
Examples would have been nice, I have no idea what you mean by "imperative mood".
I see now that examples would be useful.
For ref., imperative mood is when you tell someone to do something.
Imperative mood
Add field to schema
Present Subjunctive (I think .. )
Added field to schema
Present Subjunctive mood is the most common verb mood I see when not using Imperative mood. Git uses imperative mood so it makes sense to follow the same principle.
Great Tips 😄
Thanks 👍
Thanks, Krishna! :)
Very cool!
Bit of a sudden ending to the story; I thought you might continue to talk about the (much neglected) body, and why you shouldn't accept the default message on merge commits when your source and destination branch have the same name (when there's a reasonable chance a rebase would be more appropriate).
I also was hoping in more juice about the body. It's a whole topic itself.
Commit titles are imho easy to write; and they don't really solve the "lack of context" problem. That's what commit body is for: the details that one will spend hours/days to guess/investigate by themselves.
Stuff like:
There are just some of the aspects that I find myself dealing with into commits' body.
When I commit with a message on the command line, I think I'm only able to add a subject line. Is there a way to add a commit body through command line?
You can use the
-e
or--edit
option which will open your configured editor.You can also use the
-m
option multiple times and the text will be concatenated as separate paragraphs.Another method is to pipe the comment to a file and use either the
-F <file>
or--file=<file>
option.Running
git help commit
should show you all the possible options.HTH
I guess you do something like
Instead you can do
And git will open up your favorite editor (it defaults to
$EDITOR
I think) and you can put in a subject and body there. You separate them by a newline.In a monorepo context, it turned out to be a great idea to prefix the headline with the projects affected by the commit. For example:
[Admin UI] Added option to mass edit users.
I considered including this in the post. I agree, especially for monorepos it makes sense. Just gotta find that good balance so the prefixes don't take up too many characters!
Probably codifying the project name or the topic to 3-5 letters should work.
Same as many prefix with
bug
,fix
,feat
,ui
, etc.There are also several repos providing a model for emojii commits (a decent one, for example)
I really liked the ideas a lot (as long as a legend is provided somewhere in the repo) and started to use them happily (you can even filter commits by emoji/category)....
... until our sysadmin complained when the (draconian) issue-tracking-system crashed because of poor unicode support and told me to stop.... 😒