Skip to content

Instantly share code, notes, and snippets.

@notmyname
Created December 29, 2013 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save notmyname/8174779 to your computer and use it in GitHub Desktop.
Save notmyname/8174779 to your computer and use it in GitHub Desktop.
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.
#
#
"""
====
Tags
====
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
[EOF]
will result in the tags "happy happy" and "joy" being processed.
==========
.tagformat
==========
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:
[foo]
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'
try:
config = ConfigParser()
config.read(tag_action_config_path)
except ConfigError:
sys.exit(0)
# 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
sys.exit(0)
found_tags = set()
try:
commit_message_file = sys.argv[1]
except IndexError:
print >>sys.stderr, 'Usage: %s <path/to/commit/message>' % sys.argv[0]
sys.exit(1)
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
break
for tag in found_tags:
try:
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 = subprocess.call(cmd)
if ret:
msg = 'Error calling %r for tag %s (%r)' % (cmd, tag, ret)
print >>sys.stderr, msg
sys.exit(ret)
except (NoSectionError, NoOptionError):
# no actions configured for this tag, so there's nothing to do
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment