Question

As a followup for Mercurial: enforce "hg pull -u" before "hg commit" I have started to use a hook

[hooks]
pretxnchangegroup.forbid_2heads = /usr/local/bin/forbid_2head.sh

where forbid_2head.sh looks like this

#!/bin/bash 
BRANCH=`hg branch`
COUNT=`hg heads --template '{branch}|{rev}\n' | grep ^${BRANCH} | wc -l` 
if [ "$COUNT" -ne "1" ] ; then 
   echo "=========================================================="
   echo "Trying to push more than one head, which is not allowed"
   echo "You seem to try to add changes to an old changeset!"
   echo "==========================================================" 
   exit 1 
fi 
exit 0

It is derivative of the script found at http://tutorials.davidherron.com/2008/10/forbidding-multiple-heads-in-shared.html where I do allow multiple named branches.

The problem I have now is that

  • it stops hg push -f which is what I wanted
  • it also stops hg pull in case there are incoming changeset and I have commits outgoing. This is indeed bad

Can I in any way reuse the same script but change the hook setup and stop "hg push -f"? Or can I in forbid_2head.sh know whether this is a push or pull command running?

Was it helpful?

Solution

First, the script isn't completely correct: it just counts the number of heads in the branch currently checked out on the server (the one hg branch) reports. You could improve it by using

hg heads tip

to get the heads of the branch of tip. But someone might push changesets on more than one branch at a time, so what you really want is

hg heads --template '{branch}\n' $HG_NODE:tip

to find branch heads for the branches touched by $HG_NODE:tip (the changesets pushed in the not-yet-committed transaction). You can then compare that with

hg log --template '{branch}\n' -r $HG_NODE:tip | sort -u

which are the branches touched by the changegroup.

If you don't want to allow existing multiple heads, then you can simplify the above to just

$(hg heads --template 'x' | wc -c) -eq $(hg branches | wc -l)

which just tests that the number of branch heads is equal to the number of branches — i.e., that there is exactly one branch head per named branch.

With that out of the way, let me mention $HG_SOURCE. That environment variable is set by Mercurial when it runs the hook: it has the value push if the changegroup is being pushed into the repository using direct filesystem access, and the value serve if the changegroup is coming in over SSH or HTTP. See the Mercurial book.

So, to conclude, I believe this is a good "forbid multiple heads" script:

#!/bin/sh
HEADS=$(hg heads --template 'x' | wc -c)
BRANCHES=$(hg branches | wc -l)
test $HG_SOURCE = 'serve' -a $HEADS -ne $BRANCHES
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top