24.1 pre-push(推送前钩子)

从 Git 1.8.2 版本开始可用。

版本 ≥ 1.8

推送前钩子可用于阻止推送操作。这在以下情况下很有用:阻止意外手动向特定分支推送,或在建立的检查失败时阻止推送(单元测试、语法检查)。

创建推送前钩子的方法很简单:在 .git/hooks/ 目录下创建一个名为 pre-push 的文件,并确保该文件具有可执行权限:chmod +x ./git/hooks/pre-push。

以下是一个由 Hannah Wolfe 提供的示例,用于阻止向 master 分支推送:

#!/bin/bash
protected_branch='master'
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
if [ "$protected_branch" = "$current_branch" ]; then
  read -p "You're about to push master, is that what you intended? [y|n] " -n 1 -r < /dev/tty
  echo
  if echo "$REPLY" | grep -E '^[Yy]$' > /dev/null; then
    exit 0 # 推送将执行
  fi
  exit 1 # 推送将不执行
else
  exit 0 # 推送将执行
fi

以下是一个由 Volkan Unsal 提供的示例,用于在推送前确保 RSpec 测试通过:

#!/usr/bin/env ruby
require 'pty'
html_path = "rspec_results.html"
begin
 PTY.spawn("rspec spec --format h > rspec_results.html") do |stdin, stdout, pid|
    begin
      stdin.each { |line| print line }
    rescue Errno::EIO
    end
  end
rescue PTY::ChildExited
  puts "Child process exit!"
end

# 检查是否有错误

html = open(html_path).read
examples = html.match(/(\d+) examples/)[0].to_i rescue 0
errors = html.match(/(\d+) errors/)[0].to_i rescue 0
if errors == 0 then
  errors = html.match(/(\d+) failure/)[0].to_i rescue 0
end
pending = html.match(/(\d+) pending/)[0].to_i rescue 0

if errors.zero?
  puts "0 failed! #{examples} run, #{pending} pending"
  # 测试成功时的 HTML 输出:
  # puts "View spec results at #{File.expand_path(html_path)}"
  sleep 1
  exit 0
else
  puts "\aCOMMIT FAILED!!"
  puts "View your rspec results at #{File.expand_path(html_path)}"
  puts
  puts "#{errors} failed! #{examples} run, #{pending} pending"
  # 测试失败时的 HTML 输出:
  # `open #{html_path}`
  exit 1
end

正如你所看到的,有多种可能性,但核心部分是如果一切顺利则退出 0,如果有问题则退出 1。任何时候退出 1,推送将被阻止,你的代码将保持在运行 git push 之前的状态。

使用客户端钩子时,请记住,用户可以通过在推送时使用 --no-verify 选项来跳过所有客户端钩子。如果你依赖钩子来强制执行流程,可能会遇到问题。

文档:https://git-scm.com/docs/githooks#_pre_push

官方示例:https://github.com/git/git/blob/87c86dd14abe8db7d00b0df5661ef8cf147a72a3/templates/hooks--pre-push.sample

24.2 提交前验证 Maven 构建(或其他构建系统)

.git/hooks/pre-commit
#!/bin/sh
if [ -s pom.xml ]; then
  echo "Running mvn verify"
  mvn clean verify
  if [ $? -ne 0 ]; then
    echo "Maven build failed"
    exit 1
  fi
fi

24.3 自动将某些推送转发到其他仓库

$ cat .git/hooks/post-receive
#!/bin/bash
IFS=' '
while read local_ref local_sha remote_ref remote_sha
do
  echo "$remote_ref" | egrep '^refs/heads/[A-Z]+-[0-9]+$' >/dev/null && {
    ref=`echo $remote_ref | sed -e 's/^refs/heads\///'`
    echo Forwarding feature branch to other repository: $ref
    git push -q --force other_repos $ref
  }
done

在此示例中,egrep 正则表达式用于查找特定的分支格式(这里:JIRA-12345,用于命名 Jira 问题)。如果你希望转发所有分支,可以省略这部分内容。

24.4 commit-msg(提交消息钩子)

此钩子与 prepare-commit-msg 钩子类似,但它是在用户输入提交消息后而不是之前调用的。这通常用于警告开发人员如果他们的提交消息格式不正确。

此钩子仅接受一个参数:包含消息的文件的名称。如果你不喜欢用户输入的消息,可以像 prepare-commit-msg 那样直接修改该文件,或者通过退出非零状态来完全取消提交。

以下示例用于检查提交消息中是否包含以单词 ticket 开头并跟有数字的内容:

word="ticket [0-9]"
isPresent=$(grep -Eoh "$word" $1)
if [[ -z $isPresent ]]; then
  echo "Commit message KO, $word is missing"; exit 1;
else
  echo "Commit message OK"; exit 0;
fi

24.5 本地钩子

本地钩子仅影响它们所在的本地仓库。每个开发人员都可以更改自己的本地钩子,因此它们不能可靠地用于强制执行提交策略。它们旨在使开发人员更容易遵循某些准则并避免未来的潜在问题。

有六种类型的本地钩子:pre-commit、prepare-commit-msg、commit-msg、post-commit、post-checkout 和 pre-rebase。

前四个钩子与提交相关,允许你在提交生命周期的每个部分中进行一些控制。最后两个钩子用于在执行 git checkout 和 git rebase 命令时执行一些额外操作或安全检查。

所有“pre-”钩子都可以更改即将进行的操作,而“post-”钩子主要用于通知。

24.6 post-checkout(检出后钩子)

此钩子与 post-commit 钩子类似,但它在每次成功检出引用时调用。这可以是一个有用的工具,用于清除工作目录中的自动生成文件,否则这些文件可能会引起混淆。

此钩子接受三个参数:

  1. 前一个 HEAD 的引用。
  2. 新的 HEAD 的引用。
  3. 标志,指示是分支检出还是文件检出(分别为 1 和 0)。

其退出状态不会影响 git checkout 命令。

24.7 post-commit(提交后钩子)

此钩子在 commit-msg 钩子之后立即调用。它不能更改 git commit 操作的结果,因此主要用于通知目的。

此脚本不接受任何参数,其退出状态不会以任何方式影响提交。

24.8 post-receive(推送后钩子)

此钩子在推送操作成功后调用。通常用于通知目的。

此脚本不接受任何参数,但会通过标准输入接收与 pre-receive 相同的信息:

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

24.9 pre-commit(提交前钩子)

此钩子在每次运行 git commit 时执行,用于验证即将提交的内容。你可以使用此钩子检查即将提交的快照。

此类型的钩子可用于运行自动化测试,以确保即将提交的更改不会破坏项目现有功能。此类型的钩子还可以检查空白字符或行尾错误。

不向 pre-commit 脚本传递任何参数,退出非零状态将取消整个提交。

24.10 prepare-commit-msg(准备提交消息钩子)

此钩子在 pre-commit 钩子之后调用,用于在文本编辑器中填充提交消息。这通常用于修改自动生成的提交消息,例如合并提交或拆分提交。

此钩子接受一到三个参数:

  1. 包含消息的临时文件的名称。
  2. 提交类型,可以是:
    • message(使用 -m 或 -F 选项),
    • template(使用 -t 选项),
    • merge(如果是合并提交),
    • squash(如果是拆分其他提交)。
  3. 相关提交的 SHA1 哈希值。仅在使用 -c、-C 或 --amend 选项时提供。

与 pre-commit 类似,退出非零状态将取消提交。

24.11 pre-rebase(变基前钩子)

此钩子在 git rebase 开始更改代码结构之前调用。通常用于确保变基操作是适当的。

此钩子接受两个参数:

  1. 系列分支是从哪个上游分支分叉的。
  2. 正在变基的分支(如果变基当前分支,则为空)。

你可以通过退出非零状态来取消变基操作。

24.12 pre-receive(推送前钩子)

此钩子每次有人使用 git push 将提交推送到仓库时执行。它始终位于推送的目标远程仓库中,而不是起始(本地)仓库。

钩子在任何引用更新之前运行。通常用于强制执行任何类型的开发策略。

此脚本不接受任何参数,但每个被推送的引用都会通过标准输入传递给脚本,每行一个,格式如下:

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

24.13 update(更新钩子)

此钩子在 pre-receive 之后调用,与 pre-receive 的工作方式相同。它在实际更新之前被调用,但与 pre-receive 不同,它为每个被推送的引用单独调用,而不是一次性处理所有引用。

此钩子接受以下三个参数:

  1. 正在更新的引用的名称。
  2. 存储在引用中的旧对象名称。
  3. 存储在引用中的新对象名称。

此信息与传递给 pre-receive 的相同,但由于 update 是为每个引用单独调用的,你可以拒绝某些引用,同时允许其他引用。