Creating Pre-Commit-Hooks in Git and Mercurial: Prefix Commit Messages for Feature/Story Branches

December 16th, 2012 by

Managing my projects’ source code I am using Git also as Mercurial. Therefore I often encounter the situation where I am creating a special branch to implement a specific user story or feature request.

Now when working on such a story branch I often enter the issue-key or a short title as a prefix for each commit message. Doing this by manually is a waste of time and error-prone and luckily for us, each of both DVCS offers us an easy API to add custom hooks to the different life-cycle events.


 

Commit Messages for Story Branches

Now let’s assume that we’re using an issue tracker like Jira or another similar tool to document the story so that there is an identifier like TEST-123 for the story/feature task. When we start working on the task, we’re creating a new feature branch with the name of this identifier.

This allows us to create a custom hook that fetches the branch name and prefixes each commit message with this name.

Git Example

Creating a hook in git is quite easy .. when you’re creating a new repository, git creates a set of sample hooks for you to be found in .git/hooks. To use one of the hooks simply rename the hook that matches your need and remove the sample suffix:

.git/hooks/
├── applypatch-msg.sample
├── commit-msg.sample
├── post-update.sample
├── pre-applypatch.sample
├── pre-commit.sample
├── prepare-commit-msg.sample
├── pre-rebase.sample
└── update.sample

Now let’s create a new project, initialized the repository and add a simple shell script as commit hook..

$ mkdir myproject
$ cd myproject/
$ git init
Initialized empty Git repository in /tmp/myproject/.git/
$ date >> test.txt
$ git add .
$ git commit -m "Initial release."
[master (root-commit) 461eb05] Initial release.
1 file changed, 1 insertion(+)
create mode 100644 test.txt
$ git checkout -b "Issue-123"
Switched to a new branch 'Issue-123'
$ cp .git/hooks/prepare-commit-msg.sample .git/hooks/prepare-commit-msg

Now open the .git/hooks/prepare-commit-msg in a text editor and replace the existing content with this one:

#!/bin/sh
 
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
if [ -n "$BRANCH_NAME" ] && [ "$BRANCH_NAME" != "master" ]; then
echo "[$BRANCH_NAME] $(cat $1)" > $1
fi

Now that we’ve got the hook, we should add some changes to the branch to demonstrate how our submit message is extended with the feature branch’s name:

$ date >> test.txt
$ git commit -a -m "Important feature added."
[Issue-123 18a50b5] [Issue-123] Important feature added.
1 file changed, 1 insertion(+)
$ git log
commit 18a50b53b15c12d561f9a43fface399ae748d811
Author: Micha Kops
Date:   Fri Dec 7 21:59:05 2012 +0100
 
[Issue-123] Important feature added.

La voilà

Mercurial/Hg Example

Now let’s do something similar for mercurial .. again we’re creating a new project and  initialize the repository…

$ mkdir myproject && cd myproject
$ hg init
$ date >> test.txt
$ hg ci -A -m "Initial release."
adding test.txt
test.txt
committed changeset 0:4fc464a47f2e
 
$ hg branch "Issue-123"
marked working directory as branch Issue-123
(branches are permanent and global, did you want a bookmark?)

Now create/edit your .hg/hgrc and add the following two lines to add a pre-commit hook:

[hooks]
precommit = python:.hg/rename-story-description.py:prefix_commit_message

Now we’re creating our hook in python  – please add the following file and open it in your editor: .hg/rename-story-description.py

If you’d like to do something special then a closer look at the Mercurial Python API might be interesting.

import re,os,sys,mercurial
 
def prefix_commit_message(repo, **kwargs):
 commitctx = repo.commitctx
 
 def rewrite_ctx(ctx, error):
   branch_name = ctx.branch()
   old_text = ctx._text
   ctx._text = "["+branch_name+"] "+old_text
 
   return commitctx(ctx, error)
 
 repo.commitctx = rewrite_ctx

Now we’re adding some changes to this branch…

$ date >> test.txt
$ hg ci -m "Important feature added."
calling hook precommit:
test.txt
committed changeset 1:2dd14dc85e1b
$ hg log
changeset:   1:2dd14dc85e1b
branch:      Issue-123
tag:         tip
user:        Micha Kops
date:        Fri Dec 07 22:28:57 2012 +0100
files:       test.txt
description:
[Issue-123] Important feature added.

That’s what we wanted…

If you want to have a look which hooks do exist in your repository you may do the following

$ hg showconfig hooks
hooks.precommit=python:.hg/rename-story-description.py:prefix_commit_message

A Note on Mercurial Branches

Depending on your project and the way you’re handling your branches you might change the hook and work with bookmarks instead. With Mercurial, branches are handled differently to Git and therefore it might be better  sometimes not to use a branch rather than a bookmark.

If you’re interested in some more details here I ‘d highly recommend reading Steve Losh’ excellent article A Guide to Branching in Mercurial.

Resources

Tags: , , , , , , , , , , ,

One Response to “Creating Pre-Commit-Hooks in Git and Mercurial: Prefix Commit Messages for Feature/Story Branches”

  1. Ian Wood Says:

    Thanks for this guide – very helpful.

    However, the identaion of the rewrite_ctx method isn’t quite correct. I needed to indent to get it to work.

    Our way of hg branching is to use clones, which are in a different dir which a common “root” dir, so our impl is.

    import re,os,sys,mercurial,repo

    def prefix_commit_message(repo, **kwargs):
    commitctx = repo.commitctx

    def rewrite_ctx(ctx, error):
    branch_name = repo.root.split(“/”)[5]
    old_text = ctx._text
    ctx._text = “["+branch_name+"] “+old_text

    return commitctx(ctx, error)

    repo.commitctx = rewrite_ctx

    Thanks again,

    Ian

Search
Categories