Creating Pre-Commit-Hooks in Git and Mercurial: Prefix Commit Messages for Feature/Story BranchesDecember 16th, 2012 by micha kops
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.
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.
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.
- Git Tutorial: Customizing Git Hooks
- Git Hooks Man Page
- Sarah Goff-Dupont: Story Branching and Continuous Integration: a swords-to-plowshares tale
- Bryan O’Sullivan: Mercurial – The Definitive Guide. Handling repository events with hooks
- Mercurial Documentation: Feature separation through named branches
- Mercurial Documentation: Don’t treat branch names as disposable
- The Mercurial Python API
- Steve Losh: A Guide to Branching in Mercurial