可选的githook表现为非可选的

时间:2017-05-09 17:33:29

标签: node.js git shell githooks post-checkout-hook

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

#!/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 "$1" && eval "$2"
}

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

如果package.json文件发生更改,则声明只运行npm install。

然而,在我尝试过的所有机器上。无论package.json是否已更改,npm install命令都会运行。

为了测试这个,我在我当前的提交中创建了一个新分支,然后将其检出,从而触发post-checkout git钩子。我不希望npm install运行,因为package.json未更改。

Visual Proof(注意npm警告文字):

enter image description here

3 个答案:

答案 0 :(得分:2)

ORIG_HEAD应该替换为HEAD@{1},如此question ORIG_HEAD中所述,这是一种较旧的,不太可靠的方式,可以获得先前的HEAD状态。就我而言,它没有被设定。

答案 1 :(得分:1)

TL; DR

使用不同的结帐后挂钩,使用$1代替ORIG_HEAD。 (或者,检查参数的数量以决定是否将其作为post-checkout或post-merge钩子调用,以获得相同的效果。或者,如果您知道reflog总是被启用,请使用HEAD@{1}来获取前一个值HEAD。)

讨论

在合并后的钩子中使用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

  

给出了三个参数:ref的   之前的HEAD,新HEAD的参考(可能有也可能没有   改变),以及表明结账是否是分支的标志   checkout(更改分支,flag = 1)或文件签出(检索a   来自索引的文件,flag = 0)。这个钩子不能影响结果    git checkout

(最后一句话不太正确。虽然post-checkout钩子无法阻止检查更新索引和工作树,但可以覆盖各种工作树或索引内容,并且如果它产生故障退出状态,它会导致git checkout本身也产生故障退出状态。)

这一切意味着你需要在post-checkout钩子中采取不同的动作:使用第一个参数$1来获取前一个HEAD的哈希ID。请注意,在特殊情况下, 1 后结帐挂钩在初始git clone上运行,因此$1可以是null-ref。 (我现在很好奇当你使用git checkout --orphan时它是什么,然后也不创建新的分支。似乎$1也可能是null-ref。 )

1 git clone上运行post-checkout挂钩的唯一方法是让git clone安装post-checkout挂钩。这通常是不可能的,但可以通过将Git指向您自己的模板目录来完成,这些目录具有实际的钩子而不仅仅是示例钩子。

答案 2 :(得分:1)

Torek提到:

This hook cannot affect the outcome of git checkout.

最后一句话不太正确。

尽管检出后钩子无法停止检出更新索引和工作树的操作,但是它可以覆盖各种工作树或索引内容,并且如果产生失败退出状态,则会导致git checkout本身也产生故障退出状态。

现在(3年后的2020年第四季度)正式发布在With Git 2.29中:

请参见commit 3100fd5Junio C Hamano (gitster)(2020年8月27日)。
(由Junio C Hamano -- gitster --commit 2f1757e中合并,2020年9月3日)

doc:阐明如何使用post-checkout挂钩的退出状态

因为钩子在主要检出操作完成后运行,所以它不会影响哪个分支将成为当前分支,在工作树中更新哪些路径等,这被描述为“不会影响'检出'的结果” “。

但是,挂钩的exit状态被用作'exit'命令的checkout状态,任何产生'checkout'的人都可以观察到,这是文档中缺少的内容。
解决这个问题。

githooks现在包含在其man page中:

此钩子不会影响git switchgit checkout的结果, 除了挂钩的退出状态变为这两个命令的退出状态