章节 ▾ 第二版

8.3 自定义 Git - Git 钩子

Git 钩子

和许多其他版本控制系统一样,Git 也有一种方式,可以在特定的重要动作发生时触发自定义脚本。这些钩子分为两组:客户端钩子和服务器端钩子。客户端钩子由诸如提交和合并这样的操作所触发,而服务器端钩子则作用于诸如接收推送的提交这类网络操作。你可以出于各种目的使用这些钩子。

安装钩子

所有钩子都存储在 Git 目录的 hooks 子目录中。在大多数项目中,即 .git/hooks。当你使用 git init 初始化新仓库时,Git 会在 hooks 目录中填充一系列示例脚本。这些脚本本身就很有用,同时也记录了每个脚本所需的输入值。所有的示例都是以 shell 脚本编写的,其中夹杂了一些 Perl,但任何命名正确的可执行脚本都可以正常工作——你可以用 Ruby、Python 或任何你熟悉的语言编写它们。如果你想使用捆绑的钩子脚本,则必须重命名它们;它们的文件名都以 .sample 结尾。

要启用一个钩子脚本,只需在 .git 目录的 hooks 子目录中放置一个命名正确(且不带任何扩展名)且可执行的文件。从那时起,它就会被调用。我们将在下面介绍大多数主要的钩子文件名。

客户端钩子

客户端钩子非常多。本节将它们分为提交工作流钩子、邮件工作流脚本以及其他钩子。

注意

值得注意的是,当你克隆一个仓库时,客户端钩子是不会被复制的。如果你使用这些脚本的目的是为了强制执行某些策略,那么你可能应该在服务器端进行操作;请参阅 一个 Git 强制策略示例 中的示例。

提交工作流钩子

前四个钩子与提交过程有关。

pre-commit 钩子首先运行,甚至在你输入提交信息之前。它用于检查即将提交的快照,看看是否遗漏了什么、确保测试通过,或者检查代码中任何需要检查的内容。如果该钩子以非零状态退出,则会中止提交,不过你可以通过 git commit --no-verify 来跳过它。你可以执行诸如检查代码风格(运行 lint 或类似工具)、检查尾随空格(默认钩子正是这样做的),或者检查新方法是否有适当的文档说明等操作。

prepare-commit-msg 钩子在提交信息编辑器启动之前,但在默认信息创建之后运行。它允许你在提交作者看到默认信息之前对其进行编辑。此钩子接收几个参数:保存当前提交信息的文件路径、提交类型,如果是修补提交(amend),则是提交的 SHA-1。通常对于普通提交来说,此钩子用处不大;但它非常适合默认信息是自动生成的场景,例如模板化的提交信息、合并提交、压缩提交和修补提交。你可以将其与提交模板结合使用,以编程方式插入信息。

commit-msg 钩子接收一个参数,即开发者编写的包含提交信息的临时文件路径。如果该脚本以非零状态退出,Git 将中止提交过程,因此你可以利用它在允许提交之前验证项目状态或提交信息。在本章的最后一部分,我们将演示如何使用此钩子检查提交信息是否符合要求的格式。

在整个提交过程完成后,post-commit 钩子运行。它不接收任何参数,但你可以通过运行 git log -1 HEAD 轻松获取上一次提交的信息。通常,此脚本用于通知或类似用途。

邮件工作流钩子

你可以为基于电子邮件的工作流设置三个客户端钩子。它们都是由 git am 命令调用的,因此如果你的工作流中没有使用该命令,可以安全地跳过本节。如果你是通过 git format-patch 准备并接收补丁,那么其中一些钩子可能会对你有帮助。

运行的第一个钩子是 applypatch-msg。它接收一个参数:包含拟议提交信息的临时文件名。如果此脚本以非零状态退出,Git 将中止补丁应用。你可以使用它来确保提交信息格式正确,或者通过脚本原地编辑来标准化信息。

通过 git am 应用补丁时运行的下一个钩子是 pre-applypatch。令人困惑的是,它是在补丁应用之后但在提交之前运行的,因此你可以在进行提交之前使用它来检查快照。你可以使用此脚本运行测试或检查工作区。如果缺少某些内容或测试未通过,以非零状态退出将中止 git am 脚本,而不会提交该补丁。

git am 操作期间运行的最后一个钩子是 post-applypatch,它在提交完成后运行。你可以使用它来通知团队或补丁作者你已经应用了该补丁。你无法通过此脚本停止补丁过程。

其他客户端钩子

pre-rebase 钩子在变基之前运行,并可以通过以非零状态退出而中止该过程。你可以使用此钩子禁止变基已经推送的提交。Git 安装的示例 pre-rebase 钩子就是这样做的,尽管它做出了一些可能与你的工作流不符的假设。

post-rewrite 钩子由替换提交的命令运行,例如 git commit --amendgit rebase(但 git filter-branch 除外)。它的唯一参数是触发重写的命令名称,并从 stdin 接收重写列表。此钩子具有与 post-checkoutpost-merge 钩子许多相同的用途。

在成功运行 git checkout 后,post-checkout 钩子运行;你可以使用它为项目环境正确设置工作目录。这可能包括移动不想受版本控制的大型二进制文件、自动生成文档等类似操作。

post-merge 钩子在成功执行 merge 命令后运行。你可以使用它来恢复 Git 无法跟踪的工作区数据,例如权限数据。该钩子同样可以验证在工作区发生变化时,可能需要复制进来的 Git 控制之外的文件是否存在。

pre-push 钩子在 git push 期间运行,在远程引用更新之后但在任何对象传输之前。它接收远程仓库的名称和位置作为参数,并通过 stdin 接收待更新的引用列表。你可以使用它在推送发生前验证一系列引用更新(非零退出代码将中止推送)。

Git 有时会作为正常操作的一部分执行垃圾回收,通过调用 git gc --autopre-auto-gc 钩子在垃圾回收进行前调用,可用于通知你正在进行垃圾回收,或者如果当前不是合适的时间,也可以中止该过程。

服务器端钩子

除了客户端钩子外,作为系统管理员,你还可以使用几个重要的服务器端钩子来为项目强制执行几乎任何类型的策略。这些脚本在推送到服务器之前和之后运行。pre 钩子可以随时以非零状态退出以拒绝推送,并向客户端打印错误消息;你可以设置任意复杂的推送策略。

pre-receive

处理来自客户端的推送时运行的第一个脚本是 pre-receive。它从标准输入接收正在推送的引用列表;如果它以非零状态退出,则所有引用都不会被接受。你可以使用此钩子执行诸如确保更新的引用都是快进(fast-forward)更新,或者对推送所修改的所有引用和文件进行访问控制等操作。

update

update 脚本与 pre-receive 脚本非常相似,区别在于它为推送者尝试更新的每个分支运行一次。如果推送者尝试推送到多个分支,pre-receive 只运行一次,而 update 会为每个要推送的分支各运行一次。该脚本不是从标准输入读取,而是接收三个参数:引用名称(分支)、推送前该引用指向的 SHA-1,以及用户尝试推送的 SHA-1。如果 update 脚本以非零状态退出,则只有该引用被拒绝;其他引用仍然可以更新。

post-receive

post-receive 钩子在整个过程完成后运行,可用于更新其他服务或通知用户。它接收与 pre-receive 钩子相同的数据(标准输入)。例如发送邮件列表、通知持续集成服务器或更新工单跟踪系统——你甚至可以解析提交信息以查看是否有工单需要打开、修改或关闭。此脚本无法停止推送过程,但客户端在脚本完成之前不会断开连接,因此如果你尝试执行可能耗时很长的操作,请务必小心。

提示

如果你编写的脚本/钩子需要供他人阅读,请优先使用长格式的命令行标志;六个月后你会感谢我们的。