Showing posts with label mercuial. Show all posts
Showing posts with label mercuial. Show all posts

Thursday, 15 April 2010

NOCOMMIT for Mercurial

One problem with DVCSes (DVCSii?) is that because they encourage committing often, I sometimes accidentally end up committing temporary debugging code I didn't mean to.

So here's a simple little Mercurial pre-commit hook that blocks any commit which adds a line containing the string !NOCOMMIT.

#!/usr/bin/env python

#
# Prevents any commits which contain the string `!NOCOMMIT'.
#
# Based on code at:
# http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html
#

import re

def scan_nocommit(difflines):

    linenum = 0
    header = False

    for line in difflines:
        if header:
            # capture name of file
            m = re.match(r'(?:---|\+\+\+) ([^\t]+)', line)
            if m and m.group(1) != '/dev/null':
                filename = m.group(1).split('/', 1)[-1]
            if line.startswith('+++ '):
                header = False
            continue

        if line.startswith('diff '):
            header = True
            continue

        # hunk header - save the line number
        m = re.match(r'@@ -\d+,\d+ \+(\d+),', line)
        if m:
            linenum = int(m.group(1))
            continue

        # hunk body - check for !NOCOMMIT
        m = re.match(r'\+.*!NOCOMMIT.*', line)
        if m:
            yield filename, linenum, line[1:].rstrip()

        if line and line[0] in ' +':
            linenum += 1


def main():
    import os, sys

    msg_shown = False

    added = 0
    for filename, linenum, line in scan_nocommit(os.popen('hg export tip')):
        if not msg_shown:
            print >> sys.stderr, 'refusing commit; nocommit flags present:'
            msg_shown = True
        print >> sys.stderr, ('%s:%d: %s' % (filename, linenum, line))
        added += 1

    if added:
        # Save commit message so we don't need to retype it
        os.system('hg tip --template "{desc}" > .hg/commit.save')
        print >> sys.stderr, 'commit message saved to .hg/commit.save'

        return 1


if __name__ == '__main__' and not __file__.endswith('idle.pyw'):
    import sys
    sys.exit(main())

To use this:

  • Save the above as .hg/block_nocommit.py in your repository.
  • If you're on *nix, set execute permissions on the script.
  • Add pretxncommit.nocommit = .hg/block_nocommit.py in the [hooks] section of your .hg/hgrc file.
Now all I need to do is figure out how to write a 'block commits that contain bugs' script...