precommit mercurial hook to stop commits to the wrong branch

Faheem Mitha faheem at faheem.info
Sun Oct 13 18:37:52 UTC 2013


On Sat, 12 Oct 2013, Faheem Mitha wrote:

> On Fri, 11 Oct 2013, Faheem Mitha wrote:

>> On Thu, 10 Oct 2013, Faheem Mitha wrote:

>>> I see there is also http://mercurial.selenic.com/wiki/PythonHglib

>> I posted the following approach to SO:

>> http://stackoverflow.com/a/19305079/350713

>> The post appears below. However, I still have a problem.

>> I use mq, and when it comes to mq commits, I don't care what commits
>> are made to what branch, because mq itself doesn't really care about
>> branches (an mq patch takes the name of whatever branch it is
>> qpushed to), and so one can merrily pop and push mq patches between
>> branches.

>> So, is there some good way to tell the hook that if the commit in
>> progress is an mq commit, don't do anything?

>> I looked at mq.py for a bit, but didn't see any obvious way to do
>> this. As you can see, I am using hglib for my hook, so a solution
>> that integrates with hglib would be nice, but if I have to drop down
>> to a low level API, then I guess I can live with that.

>>                                                           Regards, Faheem

>> ########################################################################

>> Using @Ry4an's solution as a starting point, I came up with the
>> following script using the new hglib API.

>> #!/usr/bin/python

>> # Abort commit to the debian branch if it is not contained in a debian
>> # subdirectory

>> # Similary abort commit to non-debian branches if it is contained in a
>> # debian subdirectory

>> import hglib, os, sys
>> client = hglib.open("/home/faheem/hooktest")
>> ctx = client['tip']
>> files = ctx.files()
>> branch = ctx.branch()

>> for f in files:
>>    d = os.path.dirname(f)
>>    if branch == "debian" and d != "debian":
>>        sys.exit("cannot commit %s (file not in 'debian' directory) to
>> 'debian' branch"%f)
>>    if branch != "debian" and d == "debian":
>>        sys.exit("cannot commit %s (file in 'debian' directory) to non
>> 'debian' branch"%f)

> Here is an in-process hook which allows MQ commits. Thanks to Brendan and
> Bob Hood for helpful information. However, I *still* have a problem. I can
> convert an MQ commit into a regular commit with qfinish, and the hook
> doesn't stop me. Does anyone have any ideas about that?

> ##########################################################################

> def abort_commit_to_wrong_branch(ui, repo, **kwargs):
>    # If repo has '_committingpatch' attribute, then it is an mq
>    # commit in progress, so return 'False'
>    import os
>    ctx = repo[kwargs['node']]
>    files = ctx.files()
>    branch = ctx.branch()
>    if hasattr(repo, "_committingpatch"):
>        for f in files:
>            d = os.path.dirname(f)
>            if branch == "debian" and d != "debian":
>                ui.warn("Warning: committing %s (file not in 'debian'
> directory) to 'debian' branch. Allowed since this ia an MQ commit.\n"%f)
>            if branch != "debian" and d == "debian":
>                ui.warn("Warning: committing %s (file in 'debian' directory)
> to non 'debian' branch. Allowed since this ia an MQ commit.\n"%f)
>        return False
>    for f in files:
>        d = os.path.dirname(f)
>        if branch == "debian" and d != "debian":
>            ui.warn("Error: cannot commit %s (file not in 'debian' directory)
> to 'debian' branch\n"%f)
>            return True
>        if branch != "debian" and d == "debian":
>            ui.warn("Error: cannot commit %s (file in 'debian' directory) to
> non 'debian' branch\n"%f)
>            return True

One approach to the qfinish problem is to create a pre-qfinish hook as
follows. Thanks to Brendan Cully for the suggestion and various hints.

These two hooks were used in .hgrc as follows:

[hooks]
pretxncommit.foo = python:mercurial_hooks.abort_commit_to_wrong_branch
pre-qfinish.bar = python:mercurial_hooks.qfinish_abort_commit_to_wrong_branch

Comments/corrections appreciated.

                                                               Regards, Faheem

def qfinish_abort_commit_to_wrong_branch(ui, repo, **kwargs):
     """
     Don't allow qfinish on 'debian' branch including files not
     contained in the 'debian/' directory. Also don't allow qfinish on
     non-'debian' branches including files contained in the 'debian/'
     directory. Don't restrict MQ commits.
     """
     from mercurial import scmutil
     import os
     if not repo.mq.applied:
         ui.status(('no patches applied\n'))
         return True
     opts = kwargs['opts']
     # case corresponding to `-a`. no revisions specified.
     if opts.get('applied'):
         revrange = ('qbase::qtip',)
     # case where revision(s) specified
     revrange = kwargs['pats']
     revs = scmutil.revrange(repo, revrange)
     # loop over revisions
     for rev in revs:
         ctx = repo[rev]
         files = ctx.files()
         branch = ctx.branch()
         for f in files:
             d = os.path.dirname(f)
             if branch == "debian" and d != "debian":
                 ui.warn("Error: cannot commit %s (file not in 'debian' directory) to 'debian' branch\n"%f)
                 return True
             if branch != "debian" and d == "debian":
                 ui.warn("Error: cannot commit %s (file in 'debian' directory) to non 'debian' branch\n"%f)
                 return True



More information about the Mercurial mailing list