返回介绍

6.2 灵活使用钩子

发布于 2024-01-21 17:11:03 字数 9108 浏览 0 评论 0 收藏 0

Mercurial 有钩子功能。所谓钩子功能,就是指 Mercurial 在执行特定处理时还能够额外执行其他处理的功能,而且这个额外处理是任意的。比如借助这个功能,我们可以在 push 时发送邮件通知,或者在提交之前自动检测提交是否有疏漏等。

本节将通过几个实际的例子,教各位灵活运用 Mercurial 的钩子功能。

6.2.1 钩子功能的设置方法

钩子功能的设置可通过在 hgrc 的 [hooks] 节下指定 shell 命令或 Python 函数来进行。想让哪个版本库执行钩子功能就在哪个版本库的 hgrc 中进行指定。比如在 6.2.2 节,我们想利用钩子功能在提交时检查编码风格,那么就在本地版本库的 hgrc 中进行指定;又比如在 6.2.5 节,我们想通过钩子功能禁止中央版本库出现多头现象,那么就要在中央版本库的 hgrc 中进行指定。

[hooks]
commit = echo commit;
update = python:myhook.sample

如果在一个钩子事件中指定了多个钩子脚本,那么将执行最后一个。

[hooks]
update = python:myhook.sample1
update = python:myhook.sample2 # 这个钩子事件会被执行

指定为空时不会执行任何操作。我们可以利用空指定覆盖已有操作,从而避免该操作被执行。

[hooks]
update = python:myhook.sample1
update =  # update 钩子事件不会被执行

在钩子事件名后面加上后缀,就能让同一个钩子事件执行多个钩子脚本了。

[hooks]
update.sample1 = python:myhook.sample1
update.sample2 = python:myhook.sample2

6.2.2 尝试钩子脚本

如今网络上有许多已公开的钩子脚本,这里我们以“提交时检验 PEP8 编码风格”的脚本为例进行导入。该脚本包含在名为 hghooks1 的钩子脚本集之中。

1 https://pypi.python.org/pypi/hghooks/

这个 hghooks 可通过 pip 安装。

$ sudo pip install hghooks

安装完成后,只要在 hooks 中添加以下设置即可开始使用。

[hooks]
pretxncommit.pep8 = python:hghooks.code.pep8hook

6.2.3 钩子事件

钩子事件种类繁多,下面我们对应命令的执行类型,来了解一下其中的一部分。给版本库设置钩子功能时,要看对应命令是否会给版本库带来变更,这两种情况下所用的钩子事件是不一样的。

不会给当前操作版本库带来变更的命令有 update、从当前版本库 push、被其他版本库 pull 等。

会给当前操作版本库带来变更的命令有 commit、tag、从当前版本库 pull、被其他版本库 push 等。

接下来,我们对应着命令来了解一些已定义的钩子事件。其中,changegroup 表示 changeset 群被拿到版本库时的事件。另外,对 changeset 群中各个 changeset 执行的是 incoming。

◉ update 时

当前操作版本库的变更集不会被变更

① preupdate

② update

◉ 提交时

当前操作版本库的变更集会被变更

① precommit

② pretxncommit

③ commit

◉ 从当前版本库 push/ 被其他版本库 pull 时

向其他版本库发送变更集群的操作。当前操作版本库的变更集不会被变更

① preoutgoing

② outgoing

◉ 向当前版本库 pull/ 被其他版本库 push 时

接受其他版本库的变更集群的操作。当前操作版本库的变更集会被变更

① prechangegroup

② pretxnchangegroup

③ changegroup

④ incoming

我们将对版本库 A 执行各操作后的动作汇总成了下表。

执行的命令

版本库 A 发生的事件

版本库 B 发生的事件

hg update

preupdate, update

hg commit

precommit, pretxncommit, commit

hg push B

preoutgoing, outgoing

prechangegroup, pretxnchangegroup, incoming

hg pull B

prechangegroup, pretxnchangegroup, incoming

preoutgoing, outgoing

可以看到,对于部分会给当前操作版本库变更集带来变更的命令,其执行时发生的事件名带有 pretxn 前缀。这是因为这些变更是事务性的,钩子事件必须发生在变更处理已结束且事务尚未确定的时间点。

6.2.4 钩子功能的执行时机

接下来详细了解一下钩子功能。

我们看到,版本库发生变更时,有些钩子事件带有 pre 前缀,有些却没有。有 pre 前缀的钩子事件位于执行命令指定的处理之前,没有前缀的则位于执行命令指定的处理之后。pre 可以通过 exit 1 在命令指定的处理被执行之前直接中止命令。

比如我们把 /usr/bin/false 加入 preupdate。

[hooks]
preupdate = echo preupdate;hg parents;/usr/bin/false;
update = echo update;hg parents;

如下所示,如果工作目录的 parent 处于 tip(最新提交)的前一个状态,那么 hg update tip 命令在执行时将被 preupdate 打断。结果就是 update 并没有执行,工作目录的 parent 仍保持原样。

$ hg log -l2
changeset:   12:45648f583d32
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 14:03:11 2011 +0900
changeset:   11:2b1a3a2e6e29
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 14:02:55 2011 +0900
$ hg parents
changeset:   11:2b1a3a2e6e29
user:    onjudoh <monjudoh@gmail.com>
date:    ri Dec 02 14:02:55 2011 +0900
$ hg update tip
preupdate
changeset:   11:2b1a3a2e6e29
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 14:02:55 2011 +0900
abort: preupdate hook exited with status 1
$ hg parents
changeset:   11:2b1a3a2e6e29
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 14:02:55 2011 +0900

版本库发生变更时,钩子事件有 3 种:有 pre 前缀的、有 pretxn 前缀的、没有前缀的。pre 位于命令指定的处理被执行之前,pretxn 位于处理实际执行之后且事务完成之前,无后缀的位于事务完成之后。

比如,我们这里设置 precommit、pretxncommit、commit 这 3 个钩子事件来执行 hg tip 。然后执行 hg commit 会发现,从 pretxncommit 起 tip 就被更改了。

[hooks]
precommit = echo precommit;hg tip;
pretxncommit = echo pretxncommit $HG_NODE;hg tip;
commit = echo commit $HG_NODE;hg tip;
$ hg ci -A -m "commit successful"
precommit
changeset:   28:8a68c4364175
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 15:26:47 2011 +0900
pretxncommit d3000a3cea812e687f2f6bac7aaa2716be003cab
changeset:   29:d3000a3cea81
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 15:26:58 2011 +0900
summary:   commit successful
commit d3000a3cea812e687f2f6bac7aaa2716be003cab
changeset:   29:d3000a3cea81
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 15:26:58 2011 +0900
summary:   commit successful

不过,由于 pretxn 位于事务完成之前,所以在 pretxn 中用 exit 1 可以实现回滚。现在我们把 /usr/bin/false 加入 pretxncommit 中试试看。

[hooks]
precommit = echo precommit;hg tip;
pretxncommit = echo pretxncommit $HG_NODE;hg tip;/usr/bin/false;
commit = echo commit $HG_NODE;hg tip;
$ hg parents
changeset:   29:d3000a3cea81
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 15:26:58 2011 +0900
summary:   commit successful
$ hg ci -m "commit failed"
precommit
changeset:   29:d3000a3cea81
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 15:26:58 2011 +0900
summary:   commit successful
pretxncommit c42df85f2e7632b6e7ec124011b11ad8a9a3d61f
changeset:   30:c42df85f2e76
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 15:36:52 2011 +0900
summary:   commit failed
transaction abort!
rollback completed
abort: pretxncommit hook exited with status 1
$ hg parents
changeset:   29:d3000a3cea81
tag:     tip
user:    monjudoh <monjudoh@gmail.com>
date:    Fri Dec 02 15:26:58 2011 +0900
summary:   commit successful

可以看到,pretxncommit 时反映出的提交已经被撤销。灵活使用 pretxncommit、pretxnchangegroup 能帮助我们验证提交的结果或 push 的结果是否有问题,并且在有问题时实现回滚。

6.2.5 编写钩子脚本

◉ 用shell 脚本实现钩子脚本

我们以禁止中央版本库出现多头现象的钩子脚本为例,来了解一下用 shell 脚本实现的钩子脚本。首先如下所示,将脚本放到任意位置(“.hg”之下就不错)。

#!/bin/bash
# force-one-head
# add the following to <repository>/.hg/hgrc :
# [hooks]
# pretxnchangegroup.forceonehead = /path/to/force-one-head
if [ $(hg heads --template "{branch}\n"|sort|uniq|wc -l) != $(hg heads --template "{branch}\n"|sort|wc -l) ]; then
  echo "There are multiple heads."
  echo "Please 'hg pull' and get your repository up to date first."
  echo "Also, don't 'hg push --force' because that won't work either."
  exit 1
fi

接下来,在 hgrc 中将其指定为 pretxnchangegroup 的钩子脚本。具体方法如下。

[hooks]
pretxnchangegroup.forceonehead = .hg/force-one-head

这个脚本被指定给了 pretxnchangegroup,所以它的执行时机位于 push 处理执行之后且事务完成之前。因此我们可以在 push 完毕的状态下执行各种 Mercurial 命令。这里我们利用 heads 命令检查是否出现多头现象,如果出现则通过 exit 1 回滚。这就是带 pretxn 前缀的钩子事件的用法之一。

在 Mercurial 上禁止中央版本库出现多头现象

http://labs.timedia.co.jp/2011/09/reject-multiple-head.html

◉ 用 Python 脚本实现钩子脚本

这里我们讲一个用 Python 脚本实现钩子脚本的例子,即在每次提交时都对第 2 章中完成的留言板发送一次 diff(LIST 6.10)。

LIST 6.10 myhook.py

# coding: utf-8
import logging
import urllib
# 接收方URL
POST_URL = 'http://127.0.0.1:8000/post'
# 发送信息的格式
MESSAGE_FORMAT = """%(description)s
%(diff)s"""
def http_post(url, data):
  """ 用POST 方法向url 发送data
  """
  fp = urllib.urlopen(url, urllib.urlencode(data))
  return fp.read()
def postdiff(ui, repo, hooktype, node=None, source=None, **kwargs):
  """ 将差别用HTTP 进行POST 的钩子函数
  """
  # 获取提交的上下文对象
  context = repo['tip']
  # 从上下文中获取差别列表(由于是迭代器对象,所以要列表化)
  diff_list = list(context.diff())
  # 将差别结合成文本
  text_diff = ''.join(diff_list)
  # 获取用户
  user = context.user()
  # 获取概要
  description = context.description()
  # 生成要发送的信息
  message = MESSAGE_FORMAT % {'description': description, 'diff': text_diff}
  # 生成发送数据的字典
  data = {
    'name': user,
    'comment': message,
  }
  # 发送
  http_post(POST_URL, data)

将 myhook.py 放到 PYTHONPATH 的影响范围之下,然后在 hgrc 中作如下描述。

[hooks]
commit = python:myhook.postdiff

提交后,会显示如图 6.1 所示的结果。

图 6.1 向留言板发送的提交信息

用 Python 写出的钩子脚本能直接使用 context 对象。当输出结果需要用版本库内数据进行条件判断等加工时,shell 脚本不但要处理输出格式,还必须对字符串进行操作。相对地, Python 脚本只需要从管理各修订版元数据的 context 对象中获取适当信息即可。

另外从管理方面上讲,Python 脚本可以利用 Python 的包管理,用 pip 就可以安装,十分方便。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文