可选的 githook 表现为非可选的

optional githook behaving as non-optional

我正尝试在我的工作流程中使用 this gist 作为 post-mergepost-checkout git 挂钩。

#!/usr/bin/env bash
# MIT © Sindre Sorhus - sindresorhus.com

# git hook to run a command after `git pull` if a specified file was changed
# Run `chmod +x post-merge` to make it executable then put it into `.git/hooks/`.

changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"

check_run() {
    echo "$changed_files" | grep --quiet "" && eval ""
}

# Example usage
# In this example it's used to run `npm install` if package.json changed
check_run package.json "npm install"

这声称只有 运行 npm install 如果 package.json 文件被更改。

但是在我试过的所有机器上。 npm install 命令 运行s 无论 package.json 是否已更改。

为了测试这一点,我在当前提交时创建了一个新分支,然后检查它,从而触发 post-checkout git 挂钩。我不希望 npm install 变为 运行,因为 package.json 没有变化。

视觉证明(注意 npm 警告文本):

ORIG_HEAD 应替换为 HEAD@{1},如此处所述 question ORIG_HEAD 是一种较旧且不太可靠的方法,据称可以获取 HEAD 的先前状态。在我的例子中,它没有被设置。

TL;DR

使用不同的 post-checkout hook,它使用 </code> 而不是 <code>ORIG_HEAD。 (或者,检查参数的数量来决定你是否被调用为 post-checkout 或 post-merge hook,以获得相同的效果。或者,如果你知道 reflogs 总是启用, 使用 HEAD@{1} 得到 HEAD 的前一个值。)

讨论

在 post-merge hook 中使用 ORIG_HEAD 是有意义的,因为 git mergeORIG_HEAD 设置为合并前的当前提交。 (如果合并是真正的合并,而不是快进,则由 MERGE_HEAD 标识的提交和由 HEAD^1 标识的提交必然相同。但是,如果合并是快进,只有 MERGE_HEAD 和 reflog 将能够找到合并前存储在 HEAD 中的先前提交哈希。)

然而,

post-checkout 挂钩中使用 ORIG_HEAD 显然是错误的,因为 git checkout 不是 设置 ORIG_HEAD。这意味着如果 ORIG_HEAD 甚至存在,它实际上指向一些随机提交。 (当然,它实际上解析为上次更新它的任何命令留下的任何提交:git mergegit rebase 或任何其他写入 ORIG_HEAD 的命令。但这里的重点是它与结帐前的当前提交没有任何关系。)A post-checkout hook:

is given three parameters: the ref of the previous HEAD, the ref of the new HEAD (which may or may not have changed), and a flag indicating whether the checkout was a branch checkout (changing branches, flag=1) or a file checkout (retrieving a file from the index, flag=0). This hook cannot affect the outcome of git checkout.

(最后一句话不太对。虽然 post-checkout 挂钩不能阻止结帐更新索引和工作树,但它 可以 覆盖各种工作树或索引内容,如果它产生失败退出状态,它会导致 git checkout 本身也产生失败退出状态。)

这意味着您需要在 post-checkout hook 中采取不同的操作:使用第一个参数 </code> 来获取前一个 <code>HEAD。请注意,在特殊情况下,1 post-结帐挂钩在初始 git clone 上是 运行,因此 </code> 可以是空参考。 (我现在很好奇当你使用 <code>git checkout --orphan 然后不创建新分支时它是什么。看起来 </code> 也可能是这里的空引用。 )</p> <hr> <p><sup>1</sup>在 <code>git clone 上获得 运行 的 post-checkout 挂钩的唯一方法是 git clone安装 post-checkout 钩子。这通常是不可能的,但可以通过将您的 Git 指向您自己的模板目录来完成,这些模板目录具有实际的挂钩,而不仅仅是示例挂钩。

托雷克提到:

This hook cannot affect the outcome of git checkout.

最后一句话不太对。

虽然 post-checkout 挂钩无法阻止结帐更新索引和 work-tree,但它可以覆盖各种 work-tree 或索引内容,如果它产生失败退出状态,它会导致 git 结帐本身也产生失败退出状态。

现在(2020 年第 4 季度,3 年后)正式记录为 With Git 2.29:

参见 commit 3100fd5 (27 Aug 2020) by Junio C Hamano (gitster)
(由 Junio C Hamano -- gitster -- in commit 2f1757e 合并,2020 年 9 月 3 日)

doc: clarify how exit status of post-checkout hook is used

Because the hook runs after the main checkout operation finishes, it cannot affect what branch will be the current branch, what paths are updated in the working tree, etc., which was described as "cannot affect the outcome of 'checkout'".

However, the exit status of the hook is used as the exit status of the 'checkout' command and is observable by anybody who spawned the 'checkout', which was missing from the documentation.
Fix this.

githooks 现在包含在其 man page 中:

This hook cannot affect the outcome of git switch or git checkout, other than that the hook's exit status becomes the exit status of these two commands.