章节 ▾ 第二版

8.3 自定义 Git - Git 钩子

Git 钩子

和其它版本控制系统一样,Git 也具有在特定事件发生时执行自定义脚本的能力。 有两种类型的钩子:客户端钩子和服务端钩子。 客户端钩子由诸如提交和合并这样的操作所触发,而服务端钩子作用于诸如接收被推送的提交这样的联网操作。 你可以利用这些钩子做很多事情。

安装一个钩子

钩子都被存储在 Git 目录下的 hooks 子目录中。 大部分项目,这个目录位于 .git/hooks 。 当你用 git init 初始化一个新仓库时,Git 会自动在这个目录下面创建一些示例钩子。 这些示例文件本身是可以运行的脚本,它们以文档的方式告诉你如何正确使用钩子。 所有的示例都是 shell 脚本,其中混杂了一些 Perl 脚本,不过,任何其他脚本语言如 Ruby 或 Python 都可以使用。 这些示例钩子的文件名都以 .sample 结尾,如果你想启用它们,就得先移除这个后缀。

要启用一个钩子脚本,只需将其放到 .git 目录下的 hooks 子目录中,并且保证文件名正确(去掉 .sample 后缀)以及可执行。 从此以后,它就会被 Git 调用。 接下来将会详细介绍一些重要的钩子文件。

客户端钩子

客户端钩子由诸如提交和合并这样的操作所触发。 这一节会分为提交工作流钩子、邮件工作流钩子和其他客户端钩子来讲述。

注意

需要注意的是,当你克隆一个仓库时,客户端的钩子是不会被复制的。如果你的目的是通过这些脚本来强制执行策略,你可能需要在服务器端进行操作;请参见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 钩子在变基任何内容之前运行,并且可以通过返回非零值来停止该过程。你可以使用此钩子来禁止变基已推送的任何提交。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 的正在推送的引用列表;如果它返回非零值,则不会接受任何引用。你可以使用此钩子来执行诸如确保没有更新的引用是非快进的操作,或者对它们正在修改的推送的所有引用和文件执行访问控制。

update

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

post-receive

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

提示

如果你正在编写其他人需要阅读的脚本/钩子,请首选命令行标志的长版本;六个月后你会感谢我们的。

scroll-to-top