How to enforce a commit policy

Here's an interesting application of hooks to make sure that changes conform to your project's policies (mercurial 0.8.1 and after required).

I'll take the Linux kernel as an example; Linus requires patch descriptions to have at least one "Signed-off-by:" line, so that people can tell who wrote and reviewed each patch. Obviously, you could change this to require that a bug ID be referenced or something. You get the idea :-)

There are two easy ways to enforce this kind of policy in Mercurial. The first is to forbid people from even committing changesets that don't follow the policy. You can do this using the "pretxncommit" hook:

        pretxncommit.signoff = hg log --template '{desc}\n' -r $HG_NODE
        | \
            grep -qi '^signed-off-by:'

Here's a step-by-step breakdown of what's happening above.

This hook could print an informative error message, but I've kept it simple to illustrate the point.

As an alternative, we can allow people to create whatever changesets they wanted, by leaving this hook out, and instead install a hook on a shared server that will enforce this rule. This model works well with the Mercurial Queues (mq) extension, where you might want to edit and re-edit a patch locally many times before finally pushing it for others to see.

On a shared repository, we use the "pretxnchangegroup" hook. This gets fired whenever someone pushes, pulls, or unbundles changesets from somewhere else into the repository. It doesn't get run on a commit.

        pretxnchangegroup.signoff = hg log --template '{desc}\n' \
            -r $HG_NODE: | grep -qi 'signed-off-by:'

There are a few minor differences between "pretxnchangegroup" and "pretxncommit".

Here's something to bear in mind: since "hg log" can see those changes from inside the hook, other hg commands outside the hook can see those changes, too. If you're pushing to a repo that uses a hook like this, you should make sure that nobody can pull from it, or else they'll be able to pull a change that the hook may not allow to be committed.

How do you forbid pulls? Simple.

        preoutgoing.never = test $HG_SOURCE = push -o $HG_SOURCE =

This hook allows push or bundle from the repository, but not pull.

So if people can't pull from that repo because they might get changes that the pretxnchangegroup hook will forbid, what should they do? Use a hook to push approved changes to another repo that it's OK to pull from.

        changegroup.push = hg push ../ok-to-pull

The "changegroup" hook gets fired after the transaction adding a set of changesets has been committed, so it's the perfect place to push from. Its exit status can't cause anything to abort, either.


EnsureCommitPolicy (last edited 2010-06-17 04:44:28 by MikeDuigou)