Skip to content

Instantly share code, notes, and snippets.

@notmyname notmyname/commit-msg
Created Dec 29, 2013

What would you like to do?
A git commit message hook that will process tags and do stuff with them according to a local config file.
#!/usr/bin/env python
# An commit-msg hook script that parses commit message tags and does stuff.
A tag is referenced in a commit message by starting the line with "Tags:"
(case-insensitive). Tags are comma separated.
For example, given the following commit message:
A happy commit
Tags: Happy happy, joy,, joy
will result in the tags "happy happy" and "joy" being processed.
Config file in the top-level directory of the repo. Each section is a tag
that can be referenced in the commit message. Each config section has two
settings: action and args. For each tag referenced, the "action" script will
be called with two arguments--the commit message file and the value of "args".
For example, given the following .tagformat file:
action = /bin/echo
args = bar
a commit message with the "foo" tag (case-insensitive) will result in the
following call:
/bin/echo .git/COMMIT_EDITMSG bar
If the call to the "action" script fails, the commit will be aborted. Each
tag is processed sequentially in the order given in the commit message. Tags
with no defined .tagformat config section silently pass.
from ConfigParser import SafeConfigParser as ConfigParser, \
Error as ConfigError, NoSectionError, NoOptionError
import subprocess
import sys
tag_action_config_path = '.tagactions'
config = ConfigParser()
except ConfigError:
# store this so we can do case-insensitive matching of tags to config sections
config_section_mapping = {x.lower():x for x in config.sections()}
if not config_section_mapping and not config.defaults():
# no tags configured, so there's nothing to do
found_tags = set()
commit_message_file = sys.argv[1]
except IndexError:
print >>sys.stderr, 'Usage: %s <path/to/commit/message>' % sys.argv[0]
with open(commit_message_file, 'rb') as f:
for line in f:
if line.lower().startswith('tags:'):
tags = line[5:].strip().split(',')
found_tags.update(x.strip().lower() for x in tags if x.strip())
# TODO: handle multi-line tag messages
for tag in found_tags:
section_name = config_section_mapping.get(tag.lower(), tag)
action_script = config.get(section_name, 'action')
script_args = config.get(section_name, 'args')
if action_script:
# call action with the given args
cmd = [action_script, commit_message_file, script_args]
ret =
if ret:
msg = 'Error calling %r for tag %s (%r)' % (cmd, tag, ret)
print >>sys.stderr, msg
except (NoSectionError, NoOptionError):
# no actions configured for this tag, so there's nothing to do
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.