Checking git commit message format before commit’ing
UPDATE: You can’t actually use the python script directly as your commit hook due to the need for a TTY. Instead I’ve updated these instructions with a way to call it from a simple shell script which provides a TTY. Sorry for the misinformation!
Seems like I’ve been on a ‘style guide’ tear lately. Once I got git yelling at me when I tried to check in non-pep8 compliant code a coworker of mine pointed out that git commit messages have a ‘style guide’ of sorts. Of course now I needed to make sure that my git commit messages were good as well.
Fortunately the style guide for git commit messages is pretty simple, so I wrote my own really basic checker. If you’re interested, keep reading.
Basically the rules of git commit messages are these:
- The first line should act like a subject and be no longer than 50 characters
- That should be followed by a blank line
- Any other lines should be less than 72 characters wide
I told you they were simple. Here’s the python script I wrote to make sure I was following those 3 rules:
#!/usr/bin/python
import sys
import os
from subprocess import call
editor = os.environ['EDITOR']
message_file = sys.argv[1]
# Used to figure out when we've reached the part in the commit message
# where the errors go.
error_header = '# GIT COMMIT MESSAGE FORMAT ERRORS:'
def check_format_rules(lineno, line):
"""
Given a line number and a line, compare them against a set of
rules. If it it fails a given rule, return an error message. If
it passes all rules then return false.
"""
# Since enumerate starts at 0
real_lineno = lineno + 1
if lineno == 0:
if len(line) > 50:
return "E%d: First line should be less than 50 characters " \
"in length." % (real_lineno,)
if lineno == 1:
if line:
return "E%d: Second line should be empty." % (real_lineno,)
if not line.startswith('#'):
if len(line) > 72:
return "E%d: No line should be over 72 characters long." % (
real_lineno,)
return False
while True:
# Temporary storage for the commit message so we can recreate it
# and then append errors if there are any.
commit_msg = list()
errors = list()
with open(message_file) as commit_fd:
for lineno, line in enumerate(commit_fd):
stripped_line = line.strip()
# Break out of the loop if we've hit the error header
if stripped_line == error_header:
break
commit_msg.append(line)
e = check_format_rules(lineno, stripped_line)
if e:
errors.append(e)
if errors:
with open(message_file, 'w') as commit_fd:
for line in commit_msg:
commit_fd.write(line)
commit_fd.write('%s\n' % (error_header,))
for error in errors:
commit_fd.write('# %s\n' % (error,))
re_edit = raw_input('Invalid git commit message format. Would '
'you like to re-edit it? (If you answer no, your '
'commit will fail) [Y/n]')
if re_edit in ('N', 'n', 'NO', 'no', 'No', 'nO'):
sys.exit(1)
call('%s %s' % (editor, message_file), shell=True)
continue
# No errors (otherwise it would have either continued or exited) so lets
# break out of the while loop and exit cleanly
break
I went ahead and dropped that code into .git/hooks/check-git-commit.py. Then I put the following shell script into /.git/hooks/commit-msg:
#!/bin/sh
exec < /dev/tty
.git/hooks/check-git-commit.py $1
Basically if there is anything wrong with your commit message format that will stop your commit from happening (because it exits with a non-zero status) and ask if you want to re-edit the message. If so it gives you the standard git commit message in your $EDITOR and provides a list of errors at the bottom of the git comments.
BTW, you can get the latest version of my git hooks in my github repo.