- 引言
- 本书涉及的内容
- 第 1 部分 Python 开发入门
- 第 1 章 Python 入门
- 第 2 章 开发 Web 应用
- 第 3 章 Python 项目的结构与包的创建
- 第 4 章 面向团队开发的工具
- 第 5 章 项目管理与审查
- 第 6 章 用 Mercurial 管理源码
- 第 7 章 完备文档的基础
- 第 8 章 模块分割设计与单元测试
- 第 9 章 Python 封装及其运用
- 第 10 章 用 Jenkins 持续集成
- 第 11 章 环境搭建与部署的自动化
- 第 12 章 应用的性能改善
- 第 13 章 让测试为我们服务
- 第 14 章 轻松使用 Django
- 第 15 章 方便好用的 Python 模块
- 附录 A VirtualBox 的设置
- 附录 B OS(Ubuntu)的设置
6.2 灵活使用钩子
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论