Differences between revisions 29 and 31 (spanning 2 versions)
Revision 29 as of 2009-05-19 19:30:58
Size: 6344
Editor: localhost
Comment: converted to 1.6 markup
Revision 31 as of 2011-01-17 16:23:31
Size: 6691
Comment:
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
Line 7: Line 6:
Other related pages: HookExamples
Line 9: Line 10:
== Hook return values ==
Hooks can be implemented as either external programs, or internal python calls. The meaning of the return value in both cases is based on the convention for external executables; in other words, a value of 0 means "success". For hooks implemented in python this can be a bit misleading, since it means you return "False" to indicate success and "True" (or throw an exception) to indicate failure.
Line 10: Line 14:
Line 17: Line 20:
Line 20: Line 22:
{{{
#!/bin/sh
{{{#!/bin/sh
Line 23: Line 24:
SUBJECT=$(hg log -r $HG_NODE --template '{desc|firstline}')
hg log -vpr $HG_NODE | mail -s "commit: $SUBJECT" commit-list@example.com
}}}
SUBJECT=$(hg log -r $HG_NODE --template '{desc|firstline}') hg log -vpr $HG_NODE | mail -s "commit: $SUBJECT" commit-list@example.com }}} And something like this can be used for closing bugs in roundup:
Line 27: Line 26:
And something like this can be used for closing bugs in roundup:
{{{
#!/bin/sh
{{{#!/bin/sh
Line 31: Line 28:
ISSUE=`hg log -vr $HG_NODE | grep -o "\<fix:[[:alnum:]]*" | head -n 1 | sed s/fix://`
[ -z $ISSUE ] && exit
SUBJECT=`hg log -r $HG_NODE | grep "^summary:" | cut -b 14-`
hg log -vr $HG_NODE | mail -s "[$ISSUE] [status=testing] commit: $SUBJECT" roundup@example.com
}}}

If you want multiple hooks, just add a suffix to the action, an empty command can be used to unset a site-wide hook:
ISSUE=`hg log -vr $HG_NODE | grep -o "\<fix:[[:alnum:]]*" | head -n 1 | sed s/fix://` [ -z $ISSUE ] && exit SUBJECT=`hg log -r $HG_NODE | grep "^summary:" | cut -b 14-` hg log -vr $HG_NODE | mail -s "[$ISSUE] [status=testing] commit: $SUBJECT" roundup@example.com }}} If you want multiple hooks, just add a suffix to the action, an empty command can be used to unset a site-wide hook:
Line 46: Line 37:
Line 48: Line 38:
Line 54: Line 43:
Line 63: Line 51:
Line 66: Line 53:
{{{
#!/bin/sh
{{{#!/bin/sh
Line 69: Line 55:
dest="commit-list@example.com"
repo="http://hg.example.com/myrepo"
subject="New changesets in $repo"
dest=" commit-list@example.com " repo="http://hg.example.com/myrepo" subject="New changesets in $repo"
Line 73: Line 57:
hg log -r $HG_NODE: | mail -s "$subject" $dest
}}}
hg log -r $HG_NODE: | mail -s "$subject" $dest }}} For a nicer display of the changes, use the GraphlogExtension like this:
Line 76: Line 59:
For a nicer display of the changes, use the GraphlogExtension like this: {{{#!/bin/sh
Line 78: Line 61:
{{{
#!/bin/sh
dest=" commit-list@example.com " repo="http://hg.example.com/myrepo" subject="New changesets in $repo"
Line 81: Line 63:
dest="commit-list@example.com"
repo="http://hg.example.com/myrepo"
subject="New changesets in $repo"

hg glog -r $HG_NODE: | mail -s "$subject" $dest
}}}

An alternative is to use the NotifyExtension, which sends email notifications to a list of subscribed addresses.
hg glog -r $HG_NODE: | mail -s "$subject" $dest }}} An alternative is to use the NotifyExtension, which sends email notifications to a list of subscribed addresses.
Line 91: Line 66:
Line 96: Line 70:
{{{
#!/bin/bash
{{{#!/bin/bash
Line 99: Line 72:
RECIP=""
MODULE=""
RECIP="" MODULE=""
Line 103: Line 75:
    case $1 in
      --recip)
        shift
       
RECIP="$RECIP $1"
       
shift
       
;;
     --module)
        shift
       
MODULE=$1
       
shift
       
;;
    esac

. case $1 in
  . --recip)
   . shift RECIP="$RECIP $1" shift ;;
  --module)
   . shift MODULE=$1 shift ;;
 esac
Line 117: Line 85:
hg update -C
SUBJECT=$(hg log -r $HG_NODE --template '{desc|firstline}')
hg update -C SUBJECT=$(hg log -r $HG_NODE --template '{desc|firstline}')
Line 120: Line 87:
PARENT=$(hg log --debug -r $HG_NODE | sed -n '/^parent:/{s/^.*: *.*://;p}' | head -1 )
FILES=$(hg log -v -r $HG_NODE | sed -n '/^files:/{s/^.*: *//;p}')
PARENT=$(hg log --debug -r $HG_NODE | sed -n '/^parent:/{s/^.*: *.*://;p}' | head -1 ) FILES=$(hg log -v -r $HG_NODE | sed -n '/^files:/{s/^.*: *//;p}')
Line 124: Line 90:
    exit 0
. exit 0
Line 127: Line 95:
(hg log -vr $HG_NODE | egrep -v '^(changeset|parent|date):' | sed 's/^description:$//' | cat -s ; hg diff -r $PARENT -r $HG_NODE $FILES) | mail -s "$MODULE: $SUBJECT" $RECIP
}}}

This script filters out some of the other information that is really not needed for patch review: the changeset hash exists in the diff; the parents are available by using {{{hg log}}} on the changeset hash; the word "description" isn't really needed in this context, and so is filtered out as noise; the date is in the email header, etc. Some of the brevity introduced in this script was driven by one of its authors being in the habit of reading changemail on his Treo, but it seems to generally help focus the review process on the things that really changed. When we pointed this script at the node with the accidental inappropriate reversion, it went from being hidden in the noise to being painfully obvious.
(hg log -vr $HG_NODE | egrep -v '^(changeset|parent|date):' | sed 's/^description:$//' | cat -s ; hg diff -r $PARENT -r $HG_NODE $FILES) | mail -s "$MODULE: $SUBJECT" $RECIP }}} This script filters out some of the other information that is really not needed for patch review: the changeset hash exists in the diff; the parents are available by using {{{hg log}}} on the changeset hash; the word "description" isn't really needed in this context, and so is filtered out as noise; the date is in the email header, etc. Some of the brevity introduced in this script was driven by one of its authors being in the habit of reading changemail on his Treo, but it seems to generally help focus the review process on the things that really changed. When we pointed this script at the node with the accidental inappropriate reversion, it went from being hidden in the noise to being painfully obvious.
Line 133: Line 98:
<<Anchor(tmpdirhook)>>
By default Mercurial use /tmp (or one of the directory define by environment variable TMPDIR, TEMP, TMP) to uncompress the bundle received from a remote host. This may be problematic on some server with a small /tmp directory or with small quotas on that partition. To circumvent that, you can define a hook to set TMPDIR to another location before mercurial set up its serving thread.
<<Anchor(tmpdirhook)>> By default Mercurial use /tmp (or one of the directory define by environment variable TMPDIR, TEMP, TMP) to uncompress the bundle received from a remote host. This may be problematic on some server with a small /tmp directory or with small quotas on that partition. To circumvent that, you can define a hook to set TMPDIR to another location before mercurial set up its serving thread.
Line 137: Line 101:
Line 141: Line 106:
Somewhere in your {{{$PYTHONPATH}}}, put the following {{{hgenviron.py}}} file :
Line 142: Line 108:

Somewhere in your {{{$PYTHONPATH}}}, put the following {{{hgenviron.py}}} file :
Line 150: Line 114:
 

Hooks

See .hgrc (or http://www.selenic.com/mercurial/hgrc.5.html#hooks) for a complete list of hooks and http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html

Other related pages: HookExamples

1. Hook return values

Hooks can be implemented as either external programs, or internal python calls. The meaning of the return value in both cases is based on the convention for external executables; in other words, a value of 0 means "success". For hooks implemented in python this can be a bit misleading, since it means you return "False" to indicate success and "True" (or throw an exception) to indicate failure.

2. Commit Hook

You can use the hook feature to automatically send mail when a commit occurs. Add the following to .hg/hgrc:

[hooks]
commit = commithook

And put this in a file called commithook in your path:

{{{#!/bin/sh

SUBJECT=$(hg log -r $HG_NODE --template '{desc|firstline}') hg log -vpr $HG_NODE | mail -s "commit: $SUBJECT" commit-list@example.com }}} And something like this can be used for closing bugs in roundup:

{{{#!/bin/sh

ISSUE=hg log -vr $HG_NODE | grep -o "\<fix:[[:alnum:]]*" | head -n 1 | sed s/fix:// [ -z $ISSUE ] && exit SUBJECT=hg log -r $HG_NODE | grep "^summary:" | cut -b 14- hg log -vr $HG_NODE | mail -s "[$ISSUE] [status=testing] commit: $SUBJECT" roundup@example.com }}} If you want multiple hooks, just add a suffix to the action, an empty command can be used to unset a site-wide hook:

[hooks]
# do not use the site-wide hook
commit =
commit.email = /my/email/hook
commit.autobuild = /my/build/hook

3. Tips for centralized repositories

(!) If you are using some central repo with the shared ssh method and want to get notified on pushes to there, don't use the commit hook (won't get triggered), but the incoming hook.

(!) If you push a large number of changesets, the script above will flood users with one email per commit. Instead, consider using the changegroup hook, which is activated once for each push/pull/unbundle instead of once for each changeset.

4. The changegroup hook

The changegroup hook is activated once for each push/pull/unbundle, unlike the commit hook, which is run once for each changeset.

To send a mail containing all changesets of a push/pull/unbundle, put this in .hg/hgrc:

[hooks]
changegroup = /path/to/changegrouphook

Then put something like this in /path/to/changegrouphook:

{{{#!/bin/sh

dest=" commit-list@example.com " repo="http://hg.example.com/myrepo" subject="New changesets in $repo"

hg log -r $HG_NODE: | mail -s "$subject" $dest }}} For a nicer display of the changes, use the GraphlogExtension like this:

{{{#!/bin/sh

dest=" commit-list@example.com " repo="http://hg.example.com/myrepo" subject="New changesets in $repo"

hg glog -r $HG_NODE: | mail -s "$subject" $dest }}} An alternative is to use the NotifyExtension, which sends email notifications to a list of subscribed addresses.

5. Commit mail for patch review

One common reason for commit emails is to review changes. When multiple committers push to a central, canonical repository and have changes mailed only when they push to the repository rather than whenever they do commits to their local clones, you can get the same data coming through multiple times, making it hard to review the changes. This happens because of merge commits; hg pull; hg update do work hg commit; hg pull; hg update -m; hg commit will give two commit messages; one for the work and one for the merge. Except when there are conflicts, the text of the commit message for the merge is all diff that is really part of the commit, but which doesn't represent an actual change to the central, canonical repository. So while the commit message is absolutely technically correct, the duplicate information that it provides tends to degrade the reviewing process, and in practice, we (rPath) saw it obscure accidental reversion from a bad merge.

It is possible to remove almost all of the duplicate information with a bit of scripting. The following script does the trick:

{{{#!/bin/bash

RECIP="" MODULE=""

while [ $# -gt 0 ] ; do

  • case $1 in
    • --recip)
      • shift RECIP="$RECIP $1" shift ;;
      --module)
      • shift MODULE=$1 shift ;;
    esac

done

hg update -C SUBJECT=$(hg log -r $HG_NODE --template '{desc|firstline}')

PARENT=$(hg log --debug -r $HG_NODE | sed -n '/parent:/{s/.*: *.*://;p}' | head -1 ) FILES=$(hg log -v -r $HG_NODE | sed -n '/files:/{s/.*: *//;p}')

if [ -z "$FILES" ]; then

  • exit 0

fi

(hg log -vr $HG_NODE | egrep -v '(changeset|parent|date):' | sed 's/description:$//' | cat -s ; hg diff -r $PARENT -r $HG_NODE $FILES) | mail -s "$MODULE: $SUBJECT" $RECIP }}} This script filters out some of the other information that is really not needed for patch review: the changeset hash exists in the diff; the parents are available by using hg log on the changeset hash; the word "description" isn't really needed in this context, and so is filtered out as noise; the date is in the email header, etc. Some of the brevity introduced in this script was driven by one of its authors being in the habit of reading changemail on his Treo, but it seems to generally help focus the review process on the things that really changed. When we pointed this script at the node with the accidental inappropriate reversion, it went from being hidden in the noise to being painfully obvious.

6. Change temporary directory used on remote when pushing

By default Mercurial use /tmp (or one of the directory define by environment variable TMPDIR, TEMP, TMP) to uncompress the bundle received from a remote host. This may be problematic on some server with a small /tmp directory or with small quotas on that partition. To circumvent that, you can define a hook to set TMPDIR to another location before mercurial set up its serving thread.

On remote global hgrc (/etc/mercurial/hgrc or MERCURIAL.INI, set the following hook:

[hooks]
pre-serve.tmpdir = python:hgenviron.settmpdir

Somewhere in your $PYTHONPATH, put the following hgenviron.py file :

import os
#see http://docs.python.org/lib/module-tempfile.html
def settmpdir(ui, repo, hooktype, node=None, source=None, **kwargs):
        os.environ["TMPDIR"] = "/home/tmp"

Now mercurial on remote will use /home/tmp as the temporary directory when receiving changesets, for every user (but only for mercurial).

Hook (last edited 2021-08-23 00:42:32 by PaulBissex)