Git Hook默默地失败了

时间:2017-05-09 20:08:18

标签: git git-submodules githooks post-checkout-hook

我有一个结账后和合并后的githook这些内容:

#!/bin/bash
# MIT © Sindre Sorhus - sindresorhus.com
set -eux  

changed_files="$(git diff-tree -r --name-only --no-commit-id HEAD@{1} HEAD)"

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

echo ''
echo 'running git submodule update --init --recursive if .gitmodules has changed'
check_run .gitmodules "git submodule update --init --recursive"

echo ''
echo 'running npm install if package.json has changed'
check_run package.json "npm prune && npm install"

echo ''
echo 'running npm build:localhost'
npm run build:localhost 

奇怪的是,如果.gitmodules没有变化,脚本会结束而不是检查package.json。 (它甚至不会在第12行之后执行回波线)

删除check_run调用并仅使用直接命令似乎工作正常。

删除check_run .gitmodules "git submodule update --init --recursive"也可以。但是,下一行中会显示相同的行为:check_run package.json "npm prune && npm install"如果package.json尚未更改

是否有一些我遗漏的东西导致check_run在第一个文件更改未找到时结束脚本?

视觉证明: enter image description here

1 个答案:

答案 0 :(得分:7)

此处set -e是问题所在:-e表示“如果出现故障则退出”,其中“失败”被定义为“退出非零”。

我们在输出中看到,作为最后几行:

+ check_run .gitmodules 'git submodule update --init --recursive'
+ echo ''
+ grep --quiet .gitmodules

(然后别的)。脚本在grep --quiet之后退出。

以下是check_run的实际定义:

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

此解析为left && right,其中echo ... | grep ...eval "$2"

我们看到左侧部分跑了而右侧部分没跑。这里我们需要了解一些关于shell的内容:即使设置了-e,如果某些内容失败,它们也不会立即退出,只要某些内容是测试的一部分< / em>。 1 所以这不是问题所在。

但它仍然是问题,因为left && right作为退出状态,它运行的最后一件事的退出状态。它运行的最后一件事是管道,它是echo ... | grep ...。管道的退出状态是其最后一个组件的退出状态, 2 ,即grep。如果grep找到字符串(并且使用--quiet,也会抑制其输出),则退出零;如果不是,则返回1,并且一般错误为2,因此退出状态为1.

因此,left && right的退出状态也为1。

因此,由于-e生效,shell退出了!

治愈要么是为了避免-e(但这意味着如果其他事情意外失败,那么外壳会砰的一声,这可能会有点危险),或者确保left && right做到了不要让shell退出。

有一种直接的方法来执行后者:将left && right替换为if left; then right; fi

if echo "$changed_files" | grep --quiet "$1"; then
    eval "$2"
fi

请注意,如果eval "$2"失败,shell仍会退出。

使用不同的效果执行此操作有一种不同且稍微棘手的方法:将left && right替换为left && right || true。 “和”表达式绑定得更紧密,因此这意味着:

  • 评估
  • 如果成功(退出零),请评估正确
  • 如果&&失败(退出非零,或正确运行且正确退出非零),请评估|| true部分,退出0。

因此:

echo "$changed_files" | grep --quiet "$1" && eval "$2" || true

总是退出0,eval - ing "$2"当且仅当左侧失败时(找不到grepped-for表达式)。

如果您希望check_run即使eval "$2"失败也要开启,请使用第二个(|| true)版本。如果check_run如果-e失败,您希望eval "$2"停在if ...; then下,并使用第一个(/bin/sh)版本。

1 真正古老的4BSD -e早在它开源之前就有一个错误:$PIPESTATUS 让shell退出这些案例。 (我想我自己一次修了那个,但是当4BSD进入一个新的时候,不是基于史蒂夫伯恩的原始代码,那个错误根本就不存在。)

2 在bash中,您可以更详细地控制它。特别是,您可以在{{1}}数组变量中获取管道的每个组件的状态。