Create Mercurial Hook

Jun 14, 2011 at 8:49 AM

I would be interested to know if your API can help in writing hooks in mercurial (just like the person who asked the question on SO http://stackoverflow.com/questions/6298478).

What I'm wondering is how do I get for example the files that were modified by a commit. If you write your hook in Python it looks like you have access to several parameters named $HG_xxx (eg $HG_NODE).

Coordinator
Jun 14, 2011 at 9:04 AM

Hi, yes, eventually Mercurial.Net will make it easy for you to create hooks, but right now I have just started developing the support framework for this.

Basically I will create one class for each type of hook, so you would do something like:

var hook = new MercurialCommitHook();
hook.Repository.Log()...

This gives you easy access to the various environment variables provided by Mercurial and the repository in which the hook is executing.

However, right now there is only the start of that development available in the repository, and the final API has not been stabilized yet, so I wouldn't use the code in the repository in any production code at the moment.

The repository with the hook code is here: https://lassevk.kilnhg.com/Repo/MercurialNet/dev/hooks

The code is slated for inclusion in Mercurial.Net 1.1 which has a tentative release date of July 2nd.

There are no binaries produced from the pre-1.1 code, but you can easily grab the code and compile it yourself.

If you have thoughts, feedback, suggestions, whatever, regarding the code in the repository, please don't hesitate to let me know.

Jun 15, 2011 at 12:33 AM

Thanks,

Actually it's already quite simple to do. Just like the Python hook you can execute the log command to retrieve the change sets.

                Client.SetClientPath(@"C:\Program Files\TortoiseHg");

                Repository repo = new Repository(@"C:\RepoTest");

                IEnumerable<Changeset> allCommitLogs = repo.Log(null);

                Changeset lastCommit = allCommitLogs.ElementAt<Changeset>(0);

                foreach (ChangesetPathAction action in lastCommit.PathActions)

                    Console.WriteLine(action.Path + action.Action);

But it will be even easier if you provide a class like MercurialCommitHook for that (no need to know where Hg nor the repository is).

Jun 15, 2011 at 11:39 AM
Edited Jun 16, 2011 at 6:38 AM

You'd better use the "Tip" method. If your directory contains a lot of revisions (> 2000) you will recieve a TimeOut if you get all logs :

 

Repository repo = new Repository(@"C:\RepoTest");

// Get the last Changeset
Changeset lastCommit = repo.Tip();

// Enumerate all modified (or added or removed...) files with their corresponding action
foreach (ChangesetPathAction action in lastCommit.PathActions)
allCommittedFiles.Add(action.Path);

 

Coordinator
Jun 20, 2011 at 6:18 PM
Edited Jun 20, 2011 at 10:09 PM

Hi.

I have now pushed the final (I hope) changes needed to implement hook support in Mercurial.Net.

Basically, what you have to do is:

  1. Create a project for a console app, and reference Mercurial.Net
  2. Construct an object of the appropriate Mercurial*Hook class, found in the Mercurial.Hooks namespace
  3. Read out the values of the various properties on that object
  4. If the hook is a controlling hook (all the pre hooks), you have two methods, TerminateHookAndProceed, which exits the hook console app and allows Mercurial to continue, or TerminateHookAndCancelCommand, which will also exit the console hook app, but it will prevent Mercurial from continuing. There's some overloads for the latter one to help with dumping error messages to the console, etc.

Hopefully this should allow you to create all the hooks you want. Please let me know if there's anything you're missing in the hook implementations.

I followed this page when implementing the hooks: http://www.selenic.com/mercurial/hgrc.5.html#hooks

And supplemented with some additional properties from http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html

Note that these changes are only present in the Mercurial source code repository at CodePlex (https://hg01.codeplex.com/mercurialnet), and not in the downloadable binaries file or the nuget package. They will be in those files come version 1.1 which has a tentative release date of July 2nd.

Here's a basic hook you could install server-side to ensure that all changesets that are attempted pushed to your repository will have a case reference in the commit message in the format of "case 12345", or the push will be aborted, you will have to install the hook as a "pretxnchangegroup" hook:

static void Main(string[] args)
{
    var hook = new MercurialPreTransactionChangegroupHook();

    var changesets =
        hook.Repository.Log(new LogCommand()
            .WithRevision(RevSpec.From(hook.FirstRevision)))
            .ToArray();

    var re = new Regex(@"\bcase(\s|:)\d+", RegexOptions.IgnoreCase);
    var proceed = true;
    foreach (var changeset in changesets)
        if (!re.Match(changeset.CommitMessage).Success)
        {
            Console.Error.WriteLine("changeset " + changeset.Hash + " does not contains a case reference");
            proceed = false;
        }
    if (!proceed)
        hook.TerminateHookAndCancelCommand();
}
Coordinator
Jul 8, 2011 at 6:32 PM

These classes for writing hooks was released a few days ago in the 1.1 version of Mercurial.Net.