git hooks的作用

547 阅读17分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第20天,点击查看活动详情

1、什么是git hooks

git hook是git提供的,在发生特定事件时,允许用户自定义一些操作的方式。

2、git hooks的作用

  • 当我们在多人协作开发时,每个人的编码习惯不同,每个人对commit信息的习惯格式也不同,当你看到有人的commit信息是 git commit -m"bug fixed"时,你根本不知道这个人提交了什么,这时候就可以使用git hooks来规范团队成员的git commit 的提交格式,诸如git commit -m"feat(fixed):修复bug(loginForm)"时,可以一眼得知修改的内容和大致范围,团队协作效率显著提高。这种情况就可以使用commit-msg强制限制所有人的提交信息,当commit信息不符合规范时,会提交失败
  • 由于每个人的编码习惯不同,有的喜欢前面空两格,有的喜欢换行等,会导致代码合并之后比较混乱,可读性较差,git hooks可以结合eslint,pritter等进行提交前的格式检查,格式检查不通过则不允许提交

3、怎么使用git hook

1、git hooks存在哪里

git hooks其实就存在仓库根目录中的 .git/hooks/目录中(.git目录可能是隐藏目录,取消隐藏即可查看),我们来看下默认的 .git/hooks/目录是什么样子

git在初始化仓库的时候,已经初始化了很多个 xxx.sample文件(这些文件不会被执行),就是钩子的示例文件,比如 pre-commit.sample就是 git commit前的钩子示例文件。

2、怎么使用git hook

我们来设定一个使用场景,来实现一个具体的功能,当我们在提交代码前即git commit 前我们在控制台输出hellow git hook

我们在.git/hook/中,将pre-commit.sample文件复制一份,去掉文件的后缀名,变成pre-commit,去除除第一行以外的内容,

#!/bin/sh
# 以下为shell语法
echo "Hello world"

3、git hook的触发时机

git hooks的触发时机就和vue的生命周期,react的生命周期差不多

每个git hook也有特定的触发时机

官网介绍中的git hook及触发时机如下:

1.applypatch-msg(应用程序消息)

这个钩子由git am调用。它只有一个参数,即保存建议的提交日志消息的文件的名称。以非零状态退出会导致 git am在应用补丁之前中止。

该挂钩允许在适当位置编辑消息文件,并可用于将消息规范化为某些项目标准格式。检查消息文件后,它也可以用于拒绝提交。

启用后,默认的 applypatch-msg挂钩将运行 commit-msg挂钩(如果后者已启用)。

2.pre-applypatch(应用前批处理)

这个钩子由git am调用。它不接受任何参数,并在应用补丁程序之后、提交之前调用。

如果它以非零状态退出,则在应用补丁程序后将不会提交工作树。

它可以用来检查当前的工作树,如果不通过某些测试,则拒绝提交。

默认的 pre-applypatch钩子在启用时运行 pre-commit钩子(如果后者已启用)。

3.post-applypatch(应用程序批处理后) 这个钩子由git am调用。它不接受任何参数,在应用补丁程序并提交之后调用。

这个钩子主要用于通知,不能影响 git am的结果。

4.pre-commit(预先提交) 这个钩子由git commit调用,可以使用 --no-verify选项绕过它。它不接受任何参数,并在获取建议的提交日志消息和进行提交之前被调用。从这个脚本中退出非零状态会导致 git commit命令在创建提交之前中止。

默认的 pre-commit挂钩(如果启用)捕获带有尾随空白的行的引入,并在找到此类行时中止提交。

如果命令不会打开编辑器来修改提交消息,则使用环境变量 GIT_EDITOR=:调用所有 git commit挂钩。

当启用 hooks.allownonascii配置选项 unset或设置为 false时,默认的 pre-commit挂钩将阻止使用非ASCII文件名。

5.pre-merge-commit(合并前提交) 这个钩子由git merge[1]调用,可以使用 --no-verify选项绕过它。它不接受任何参数,并在合并成功执行之后和获取建议的提交日志消息以进行提交之前调用。从这个脚本中退出非零状态会导致Git合并命令在创建提交之前中止。

如果启用了 pre-merge-commit挂钩,则默认的预合并提交挂钩将运行 pre-commit挂钩。

如果命令不会调出编辑器来修改提交消息,则使用环境变量 GIT_EDITOR=:调用此挂钩。

如果无法自动执行合并,则需要解决冲突并单独提交结果(参见git merge)。此时,将不会执行此挂钩,但如果启用了 pre-commit挂钩,则会执行它。

6.prepare-commit-msg(准备提交消息) git commit在准备默认日志消息之后,在启动编辑器之前调用此钩子。

它需要一到三个参数。第一个是包含提交日志消息的文件的名称。第二个是提交消息的来源,可以是:message(如果给出了 -m-F选项);template(如果给出了 -t选项或配置选项 commit.template);merge(如果提交是合并或 .git/MERGE_MSG文件);squash(如果 .git/SQUASH_MSG文件存在);或 commit,接着是提交SHA-1(如果是 -c-C)或者 --amend 选项)。

如果退出状态为非零,则 git commit将中止。

钩子的目的是就地编辑消息文件,而 --no-verify选项不禁止它。非零退出意味着钩子失败,并中止提交。它不应该用作预提交挂钩的替换。

Git附带的 prepare-commit-msg钩子示例删除了commit模板注释部分中的帮助消息。

7.commit-msg(提交信息) 这个钩子由git commitgit merge调用,可以使用 --no-verify选项绕过它。它接受一个参数,即保存建议的提交日志消息的文件的名称。退出非零状态会导致命令中止。

允许钩子就地编辑消息文件,并可用于将消息规范化为某些项目标准格式。它还可用于在检查消息文件后拒绝提交。

默认的 commit-msg hook在启用时检测到重复的 Signed-off-by行,如果找到一行,则中止提交。

8.post-commit(提交后) 这个钩子由git commit调用。它不接受任何参数,并在提交后调用。

这个钩子主要用于通知,不能影响 git commit的结果。

9.pre-rebase(变基前) 这个钩子由git rebase调用,可用于防止分支重新定位。可以使用一个或两个参数调用钩子。第一个参数是派生序列的上游。第二个参数是正在重设基的分支,重设基当前分支时不设置该参数。

10.post-checkout(结账后) 更新工作树后运行git checkoutgit switch时,将调用此挂钩。钩子有三个参数:前一个HEAD的ref,新HEAD的ref(可能已经更改,也可能没有更改)和一个标志,指示签出是分支签出(更改分支,flag=1)还是文件签出(从索引中检索文件,flag=0)。此挂钩不会影响 git switchgit checkout的结果。

它也在git clone[1]之后运行,除非使用 --no-checkout-n)选项。给钩子的第一个参数是空ref,第二个参数是新头的ref,标志总是1。同样,对于 git worktree add,除非 --no-checkout签出。

此钩子可用于执行存储库有效性检查、自动显示与前一个HEAD的差异(如果不同)或设置工作目录元数据属性。

11.post-merge(合并后) 这个钩子由git merge调用,当在本地存储库上完成 git pull时就会发生这种情况。钩子接受一个参数,一个状态标志,指定正在进行的合并是否是挤压合并。如果合并由于冲突而失败,则此挂钩不会影响 git merge的结果,也不会执行。

此钩子可与相应的预提交钩子结合使用,以保存和还原与工作树相关联的任何形式的元数据(例如:permissions/ownership, ACLS等)。请参阅contrib/hooks/setgitperms.perl,以获取如何执行此操作的示例。

12.pre-push(预推) 这个钩子被git push调用,可以用来防止发生push。使用两个参数调用钩子,这两个参数提供目标远程的名称和位置,如果未使用命名远程,则两个值将相同。

有关要推送的内容的信息在钩子的标准输入中提供,输入行如下:

<local ref> SP <local sha1> SP <remote ref> SP <remote sha1> LF

例如,如果运行 git push origin master:foreign命令,钩子将收到如下行:

refs/heads/master 67890 refs/heads/foreign 12345

尽管将提供完整的、40个字符的SHA-1。如果外部参考还不存在,<remote SHA-1> 将是40 0。如果要删除引用,<local ref>将作为(delete)提供,<remote SHA-1>将为40 0。如果本地提交不是由可扩展的名称(如 HEAD~SHA-1)指定的,则将按最初的给定方式提供。

如果这个钩子退出非零状态,git push将中止而不推任何东西。有关拒绝推送的原因的信息可以通过写入标准错误发送给用户。

13.pre-receive(预先接收) 当git-receive-pack对其存储库中的 git push和updates引用作出反应时,它将调用此钩子。在开始更新远程存储库上的refs之前,将调用预接收挂钩。它的退出状态决定了更新的成功或失败。

对于接收操作,此钩子执行一次。它不需要参数,但是对于每个要更新的ref,它在标准输入上接收一行格式:

<old-value> SP <new-value> SP <ref-name> LF

其中,<old-value>是存储在ref中的旧对象名,<new-value>是存储在ref中的新对象名,<ref-name>是ref的全名。创建新ref时,<old-value>是40 0

如果钩子退出非零状态,则不会更新任何参考文件。如果钩子以0退出,则更新钩子仍然可以防止单个引用的更新。

标准输出和标准错误输出都被转发到另一端的 git send-pack,因此您可以简单地为用户回显消息。

git push命令行中给定的push选项数 --push-option=... 可以从环境变量 GIT_PUSH_OPTION_COUNT中读取,选项本身位于 GIT_PUSH_OPTION_0GIT_PUSH_OPTION_1,…中。如果协商不使用PUSH options阶段,则不会设置环境变量。如果客户端选择使用push选项,但不传输任何选项,则 count变量将设置为零,GIT_push_OPTION_count=0

有关一些注意事项,请参阅git-receive-pack中关于“隔离环境”的部分。

14.update(更新) 当git-receive-pack对其存储库中的 git push和updates引用作出反应时,它将调用此钩子。就在更新远程存储库上的ref之前,会调用更新挂钩。它的退出状态决定了REF更新的成败。

钩子对每个要更新的ref执行一次,并接受3个参数:

  • 正在更新的ref的名称,
  • 存储在ref中的旧对象名,
  • 以及要存储在ref中的新对象名。

从更新钩子的零出口允许REF被更新。退出非零状态阻止 git receive-pack更新REF。

通过确保对象名是commit对象(commit对象是由旧对象名命名的commit对象的后代),此钩子可用于防止强制更新某些ref。也就是说,执行“仅限快进”政策。

它还可以用来记录旧的..新的状态。但是,它不知道整个分支集,因此在天真地使用时,它最终会为每个ref触发一封电子邮件。post-receive钩子更适合这种情况。

在一个仅限制用户通过网络访问git命令的环境中,此钩子可用于实现访问控制,而不依赖文件系统所有权和组成员资格。请参阅git shell了解如何使用登录shell限制用户仅访问git命令。

标准输出和标准错误输出都被转发到另一端的 git send-pack,因此您可以简单地为用户回显消息。

默认的 update hook在启用时,如果 hooks.allowunannotated config选项未设置或设置为 false,则会阻止推送未注释的标记。

15.post-receive(接收后) 当git-receive-pack对其存储库中的 git push和updates引用作出反应时,它将调用此钩子。在更新所有ref之后,它在远程存储库上执行一次。

对于接收操作,此钩子执行一次。它不接受参数,但获取的信息与 pre-receive钩子在其标准输入上所做的相同。

这个钩子不会影响 git receive-pack的结果,因为它是在实际工作完成后调用的。

这将取代 post-update挂钩,因为它除了获取所有ref的名称外,还获取它们的旧值和新值。

标准输出和标准错误输出都被转发到另一端的 git send-pack,因此您可以简单地为用户回显消息。

默认的 post-receive钩子是空的,但是Git发行版的 contrib/hooks目录中提供了一个示例脚本 post-receive email,它实现了发送提交电子邮件。

git push命令行中给定的push选项数 --push-option=...可以从环境变量 GIT_PUSH_OPTION_COUNT中读取,选项本身位于 GIT_PUSH_OPTION_0GIT_PUSH_OPTION_1,…中。如果协商不使用PUSH options阶段,则不会设置环境变量。如果客户端选择使用push选项,但不传输任何选项,则count变量将设置为零,GIT_push_OPTION_count=0

16.post-update(更新后) 当git-receive-pack对其存储库中的 git push和updates引用作出反应时,它将调用此钩子。在更新所有ref之后,它在远程存储库上执行一次。

它接受可变数量的参数,每个参数都是实际更新的ref的名称。

此钩子主要用于通知,不能影响 git receive-pack的结果。

post-update钩子可以告诉推送的头是什么,但是它不知道它们的原始值和更新值是什么,所以它是一个很糟糕的地方来记录旧的..新的。post-receive钩子获取refs的原始值和更新值。如果你需要的话,你可以考虑一下。

启用后,默认的 post-update挂钩运行 git update-server-info以保持dumb transports(例如HTTP)使用的信息是最新的。如果您要发布一个可以通过HTTP访问的Git存储库,那么您可能应该启用这个钩子。

标准输出和标准错误输出都被转发到另一端的 git send-pack,因此您可以简单地为用户回显消息。

17.push-to-checkout(推送至结帐) 当git-receive-pack对其存储库中的 git push和update s引用作出反应,并且当push尝试更新当前签出的分支并且 receive.denyCurrentBranch配置变量设置为 updateInstead时,它将调用此钩子。如果工作树和远程存储库的索引与当前签出的提交有任何差异,则默认情况下拒绝此类推送;当工作树和索引都与当前提交匹配时,它们将更新以匹配分支的新推送提示。此钩子将用于重写默认行为。

钩子接收当前分支的提示将被更新的提交。它可以以非零状态退出拒绝推送(当它这样做时,它不必修改索引或工作树)。或者,当当前分支的尖端被更新为新的提交,并以零状态退出时,它可以对工作树和索引进行任何必要的更改,以使它们达到所希望的状态。

例如,钩子可以简单地运行 git read-tree -u -m HEAD "$1",以模拟git push反向运行的 git fetch,因为 git read tree -u -m的两种树形式本质上与 git switchgit checkout相同,后者切换分支,同时保持工作树中不干扰的本地更改树枝之间的差别。

18.pre-auto-gc(前自动gc) 这个钩子由 git gc --auto调用(参见git gc)。它不需要任何参数,并且从这个脚本中退出非零状态,导致 git gc --auto中止。

19.post-rewrite(重写后) 此钩子由重写提交的命令调用(使用 --amendgit rebase调用git commit;但是,git fast-importgit filter-repo之类的完整历史(重新)编写工具通常不会调用它!)。它的第一个参数表示它被调用的命令:当前是 amendrebase之一。将来可能会传递更多依赖命令的参数。

钩子接收stdin上重写的提交列表,格式如下

<old-sha1> SP <new-sha1> [ SP <extra-info> ] LF

extra-info同样依赖于命令。如果为空,则前面的SP也将被忽略。目前,没有命令传递任何 extra-info

钩子总是在自动复制便笺之后运行(参见git config中的“notes.rewrite.<command>”),因此可以访问这些便笺。

以下命令特定注释适用: rebase 对于 squashfixup操作,所有挤压的提交都将被列为被重写为挤压的提交。这意味着将有多条线路共享同一个 new-sha1。 保证提交按rebase处理的顺序列出。

20.sendemail-validate(发送电子邮件验证) 此钩子由git send-email[1]调用。它只接受一个参数,即保存要发送的电子邮件的文件的名称。退出非零状态导致 git send-email在发送任何电子邮件之前中止。

21.fsmonitor-watchman(监听看守者) 当配置选项 core.fsmonitor设置为 .git/hooks/fsmonitor-watchman时,将调用此钩子。它需要两个参数,一个版本(当前为1)和自1970年1月1日午夜以来以纳秒为单位的时间。

钩子应该输出到stdout工作目录中自请求时间以来可能已更改的所有文件的列表。逻辑应该是包含的,这样就不会遗漏任何潜在的更改。这些路径应该相对于工作目录的根目录,并由单个NUL分隔。

可以包含没有实际更改的文件。应包括所有更改,包括新创建和删除的文件。重命名文件时,应同时包含旧名称和新名称。

Git将根据给定的路径名限制它检查哪些文件进行更改,以及检查哪些目录以查找未跟踪的文件。

告诉git“所有文件都已更改”的一种优化方法是返回filename /

退出状态决定Git是否使用钩子中的数据来限制其搜索。出错时,它将返回到验证所有文件和文件夹。

22.p4-pre-submit(p4预先提交) 此钩子由 git-p4 submit调用。它不接受任何参数,也不接受标准输入。从脚本中退出非零状态,防止 git-p4 submit从启动提交。运行 git-p4 submit --help获取详细信息。

23.post-index-change(索引后变化) 当索引写入读缓存时调用此挂钩。c do_write_locked_index。

传递给钩子的第一个参数是正在更新的工作目录的指示符。“1”表示工作目录已更新,或“0”表示工作目录未更新。

传递给钩子的第二个参数是指示索引是否已更新以及跳过工作树位是否已更改的指示器。”“1”表示跳过工作树位可能已更新,“0”表示它们未更新。

钩子运行时,只有一个参数应设置为“1”。吊钩不应通过“1”、“1”。

我们平时的开发工作中肯定用不到那么多的hook,那么我们常用的hook有哪些呢?

1.提交工作流挂钩

commit操作有 4个挂钩被用来处理提交的过程,他们的触发时间顺序如下: pre-commitprepare-commit-msgcommit-msgpost-commit

2、E-mail工作流挂钩

applypatch-msg,pre-applypatch,post-applypatch

其实最常用的还是提交工作流挂钩,关于这些hook的详细作用已在上面hook介绍中详细说明,有需要的自行查看