Git Hook默默地失败了

jth*_*h41 6 git git-submodules githooks post-checkout-hook

我有一个post-checkout和后合并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 
Run Code Online (Sandbox Code Playgroud)

奇怪的是,如果.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结束第一个文件更改未找到的脚本?

视觉证明: 在此输入图像描述

tor*_*rek 7

set -e是这里的问题:-e意思是"出口,如果事情失败",其中"失败"被定义为"退出非零".

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

+ check_run .gitmodules 'git submodule update --init --recursive'
+ echo ''
+ grep --quiet .gitmodules
Run Code Online (Sandbox Code Playgroud)

(然后别的什么).脚本退出之后grep --quiet.

这是实际的定义check_run:

check_run() {
    echo "$changed_files" | grep --quiet "$1" && eval "$2"
}
Run Code Online (Sandbox Code Playgroud)

这解析左边是哪里,右边是.left && rightecho ... | grep ...eval "$2"

我们看到左侧部分运行而右侧部分没有运行.这里我们需要了解一些关于shell的内容:即使有-eset,如果某些内容失败,它们也不会立即退出,只要该内容是测试的一部分.1 所以这不是问题所在.

但它仍然是问题所在,因为它的退出状态是它运行的最后一件事的退出状态.它最后运行的是左侧管道,它是.管道的退出状态是它的最后一个组件,退出状态2即.如果grep找到字符串(并且同时抑制其输出),则grep退出零;如果不找到,则返回1;对于一般错误,grep退出2,因此退出状态为1.left && rightecho ... | grep ...grep--quiet

因此,退出状态也是1.left && right

因此,既然-e有效,外壳退出了!

治愈要么是为了避免-e(但这意味着如果其他东西意外失效,外壳犁,这可能有点危险),或确保不会使外壳退出.left && right

有一种直接的方法来做后者:替换为:left && rightif left; then right; fi

if echo "$changed_files" | grep --quiet "$1"; then
    eval "$2"
fi
Run Code Online (Sandbox Code Playgroud)

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

有一种不同的,稍微有点棘手的方法来做一个不同的效果:替换为."和"表达式绑定得更紧密,因此这意味着:left && rightleft && right || true

  • 评估
  • 如果成功(退出零),则评估正确
  • 如果&&失败(左侧退出非零,或右侧运行且右侧退出非零),则评估|| true退出0 的零件.

因此:

echo "$changed_files" | grep --quiet "$1" && eval "$2" || true
Run Code Online (Sandbox Code Playgroud)

总是以0,eval-ing "$2"当且仅当左侧的失败(没有找到grepped-表达).

如果check_run即使eval "$2"失败也要继续,请使用second(|| true)版本.如果你想check_run停止(并且让整个shell退出),-e如果eval "$2"失败,请使用first(if ...; then)版本.


1真正的古4BSD /bin/sh,很久以前它去开源,有一个bug:-e 令在这些情况下壳退出.(我想我曾经自己修过一个,但是当4BSD进入一个新的时候,不是基于史蒂夫伯恩的原始代码,那个错误就不存在了.)

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