章节 ▾ 第二版

8.3 自定义 Git - Git Hooks

Git Hooks

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

安装钩子

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

要启用一个钩子脚本,请在你 .git 目录的 hooks 子目录下放置一个命名恰当(无扩展名)且可执行的文件。从那时起,它将被调用。我们将在此介绍大多数主要的钩子文件名。

客户端钩子

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

注意

需要注意的是,克隆仓库时 **不会** 复制客户端钩子。如果你打算用这些脚本强制执行策略,你可能需要在服务器端进行;请参阅 Git 强制策略示例 中的示例。

提交工作流钩子

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

pre-commit 钩子最先运行,在你输入提交消息之前。它用于检查即将提交的快照,看你是否遗漏了什么,确保测试运行,或者检查你需要检查代码的任何内容。如果此钩子退出时返回非零值,则会中止提交,但你可以使用 git commit --no-verify 来绕过它。你可以执行诸如检查代码风格(运行 lint 或等效工具)、检查末尾空格(默认钩子正是执行此操作)或检查新方法的适当文档等操作。

prepare-commit-msg 钩子在提交消息编辑器启动之前但默认消息创建之后运行。它允许你在提交作者看到默认消息之前对其进行编辑。此钩子接受几个参数:包含当前提交消息的文件路径、提交类型以及是否为修订提交(如果是,则提供提交 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 钩子在你 rebase 任何内容之前运行,并且可以通过退出时返回非零值来停止该过程。你可以使用此钩子来禁止 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。它接收来自 stdin 的推送的引用列表;如果它退出时返回非零值,则所有引用都不会被接受。你可以使用此钩子来执行诸如确保更新的引用都不是非快进(non-fast-forward)的,或者对所有引用及其在推送中修改的文件进行访问控制等操作。

update

update 脚本与 pre-receive 脚本非常相似,但它的运行次数是推送者尝试更新的每个分支一次。如果推送者尝试推送到多个分支,pre-receive 只运行一次,而 update 则针对他们正在推送的每个分支运行一次。此脚本不从 stdin 读取,而是接受三个参数:引用的名称(分支)、推送前该引用指向的 SHA-1,以及用户尝试推送的 SHA-1。如果 update 脚本退出时返回非零值,则仅拒绝该引用;其他引用仍然可以被更新。

post-receive

post-receive 钩子在整个过程完成后运行,可用于更新其他服务或通知用户。它接收与 pre-receive 钩子相同的 stdin 数据。例如,发送电子邮件给一个列表,通知一个持续集成服务器,或更新一个工单跟踪系统——你甚至可以解析提交消息来查看是否需要打开、修改或关闭任何工单。此脚本不能停止推送过程,但客户端直到它完成才会断开连接,所以如果你尝试执行任何可能需要很长时间的操作,请谨慎。

提示

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