Git provides a very valuable feature that many developers seem to either overlook or blatantly not care about; it enables you to add distinct sections to a commit message. Specifically, it allows you to add a short summary for the commit and a long and detailed body. A great commit message:
Capitalized, short (50 chars or less) summary More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Write your commit message in the imperative: "Fix bug" and not "Fixed bug" or "Fixes bug." This convention matches up with commit messages generated by commands like git merge and git revert. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here - Use a hanging indent
Key points of a well formed commit message:
- Must have a summary line
- Summary line must be 50 characters or less
- Should have a well thought out and meaningful description
- No line in the description should be over 72 characters long
- First line is summary
- Second line is empty
- Third line starts the in-depth description
Be sure to write your summary in the present tense imperative. For example, instead of
Adds new copy to homepage use
Add new copy to homepage or instead of
Fixes bug #234234 use
Fix bug #234234. This folows the convention that git itself uses. Have you ever noticed when you merge a branch the commit message is
Merge [branch] and not
Merges [branch] or
A lot of this info comes from a great blog post by Tim Pope in 2008. I didn’t make the rules I just follow them™. Various git commands pull the summary automatically and will just truncate your commit message if it is too long. This makes scanning the commit history very cumbersome and annoying. Commit summaries allow you to quickly convey the full intent of the commit in a single and succinct line. Think of it like the subject line of an email.
Yesterday I was needing a break from normal work and decided to work on some git hooks that would make me follow these rules.
Git provides several hooks into git events for you to do some pre-defined action prior to or immediately after a commit.
I want to validate the commit message after the user creates the message but prior to it being committed to the repository. The specific hook I need for this is the
commit-msg hook. If you remove the
.sample from these files git will run them at their appropriate times. They are shell scripts and I could have written my message checker in a shell script like bash but I’m really not good at awk and sed and all that crazy shell scripting voodoo. So I just use the commit-msg shell script to call my own python script.
What I want this script to do:
- Verify I have a summary line on my commit
- Verify the summary line is not over 50 characters
- Verify no line is over 72 characters
- If there are any errors, reject my commit and ask me to reformat
- If I choose to reformat my commit, bring me back into the commit editor and show me what exactly was wrong with my commit in comments on the commit message
After a few beers and some tinkering I ended up with this messy bit of python code:
Now if I try to commit a badly formed commit message like this:
It will kick me back to the shell prompt me with this message:
Invalid git commit message format. Press y to edit and n to cancel the commit. [y/n]
Once you press
y to edit your commit it will kick you back to your commit and allow you to fix the bad formatting and even tell you which lines were incorrect and why!
This tells us that we have errors on line 1, 2 and 7 of our commit. I’ve accomplished everything I set out to and now I’ll be forced to follow good practices on my commit messages.
I actually wrote this initially in Ruby, if you know me you know I am definitely not a python programmer, but had some problems I couldn’t figure out with forking subprocesses for the editor after a second commit rejection. I needed it to stay in the error loop and check the message again but if you rejected your commit and then tried to use the exact same message with the errors again, it would succeed and commit to the repository. Someday soon I’ll dig into that more but for now this python script will certainly do!
Do you follow any guidelines on commit message format?