【问题标题】:git reset --hard -q produces errors about whitespacegit reset --hard -q 产生关于空格的错误
【发布时间】:2016-06-01 15:40:46
【问题描述】:

我正在编写一个预提交钩子来运行我的 Python 测试,并且一切都运行良好......直到我遇到合并冲突。在此合并冲突中,引入了一些带有尾随空格的文档文件,每次我尝试提交时,都会收到以下消息:

<stdin>:28: trailing whitespace.
Ticket Link: 
<stdin>:54: trailing whitespace.
Then visit `http://app.dev` 
<stdin>:13528: trailing whitespace.
                            //most be provided for ALL resource updates, and 
<stdin>:13531: trailing whitespace.
    "deleted": false,  //indicates if this action resulted in a resource delete; 

warning: squelched 415 whitespace errors
warning: 420 lines add whitespace errors.
fatal: could not open '.git/MERGE_HEAD' for reading: No such file or directory

然后当我打开我的编辑器来写提交信息时,它完全是空的。

我的预提交脚本是:

#!/bin/sh

RED='\033[0;31m'
NC='\033[0m'

proper_pop() {
    git reset --hard -q 
    git stash apply -q --index && git stash drop -q
}

exit_and_pop() {
    proper_pop

    if [ "$1" -ne 0 ]; then
        echo "${RED}Your code failed the pre-commit hook! Please examine the output and fix your issues!${NC}"
    fi

    exit $1
}

run_and_bail() {
    bash -c "$1";
    ret=$?;

    if [ "${ret}" -ne 0 ]; then
        exit_and_pop "${ret}"
    fi
}

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

old_stash=$(git rev-parse -q --verify refs/stash)
git stash -q --keep-index
new_stash=$(git rev-parse -q --verify refs/stash)

if [ "$old_stash" = "$new_stash" ]; then
    echo "pre-commit script: No changes to test. Not running."
    sleep 1  # HACK: Editor may erase message if done too quickly, make the programmer read
    exit 0
fi

# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)

# Redirect output to stderr.
exec 1>&2

# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
    # Note that the use of brackets around a tr range is ok here, (it's
    # even required, for portability to Solaris 10's /usr/bin/tr), since
    # the square bracket bytes happen to fall in the designated range.
    test $(git diff --cached --name-only --diff-filter=A -z $against |
      LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
    cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
  git config hooks.allownonascii true
EOF
    exit_and_pop 1
fi

if ! [ -z "$(which git-pylint-commit-hook)" ]; then
    run_and_bail "git-pylint-commit-hook"
fi

if ! [ -z "$(which pep8)" ]; then
    run_and_bail "python hooks/pep8-hook-check.py"
fi

proper_pop

通过我的调试,我已经生成了最小的脚本来重现这个错误:

#!/bin/sh

RED='\033[0;31m'
NC='\033[0m'

proper_pop() {
    git reset --hard -q 
    git stash apply -q --index && git stash drop -q
}

exit_and_pop() {
    proper_pop

    if [ "$1" -ne 0 ]; then
        echo "${RED}Your code failed the pre-commit hook! Please examine the output and fix your issues!${NC}"
    fi

    exit $1
}

run_and_bail() {
    bash -c "$1";
    ret=$?;

    if [ "${ret}" -ne 0 ]; then
        exit_and_pop "${ret}"
    fi
}

if git rev-parse --verify HEAD >/dev/null 2>&1
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

old_stash=$(git rev-parse -q --verify refs/stash)
git stash -q --keep-index
new_stash=$(git rev-parse -q --verify refs/stash)

if [ "$old_stash" = "$new_stash" ]; then
    echo "pre-commit script: No changes to test. Not running."
    sleep 1  # HACK: Editor may erase message if done too quickly, make the programmer read
    exit 0
fi

proper_pop

通过这个,我了解到这行:git reset --hard -q in proper_pop 是唯一的罪魁祸首(删除它会消除错误)。没有安装其他钩子,git 版本是:1.8.1.2,但我也在版本2.5.0 上运行了这个,同样的问题发生了。有没有人任何知道发生了什么?

我尝试将 stdout 和 stderr 传送到 /dev/null 以查看该命令是否有效,并且仍然打印错误...

这完全是一个我可以解决的问题,它确实不会导致任何问题,但我不希望技术水平较低的同事看到这个错误并让它摆脱它们(我不确定每次我们合并带有尾随空格的内容时是否会打印它),所以我很想知道发生了什么以及如何解决这个问题(或告诉git reset忽略尾随空格错误)。

编辑:

这是演示此问题的完整工作存储库:https://github.com/hjc1710/so-git-hook-question,只需按照README.md 中的步骤操作,无论您是否在合并中,您都会收到错误消息。

可以在here 找到我的 git 配置(适用于 1.8.x 上的工作站和 2.5.x 上的笔记本电脑)。敏感信息已被删除,但没有一个是相关的。

【问题讨论】:

  • 运行 reset --hard 并存储似乎是预提交中的灾难。在 git 尝试做同样的事情时,你失去了 staging 并更改了 HEAD。此外,如果您在合并过程中执行 git reset 操作,您实际上已经中止了合并。也许不使用预提交,而是使用暂存分支。然后使用 CI 系统的工作流程为您测试 staging 并合并到 master。
  • 我正在尝试查看 stdin 在何处通过管道传输到 diff,因为这就是投诉的来源(&lt;stdin&gt;:28),但语法似乎不存在。您是否确认 pre-commit 挂钩会在全新安装 git 的干净机器上导致相同的问题?运行你的脚本对我来说没有产生任何错误,但这些错误确实让人想起git rebase --whitespace=warn 之类的警告。我也有兴趣查看您的 repo、全局和系统 .gitconfigs(git config -lgit config --global -lgit config --system -l)。
  • @Bujiraso 我已经用我的 git 配置(我的笔记本电脑上的 2.5.x 和我的工作站上的 1.8.x)编辑了我的原始问题,以及一个使用剥离钩子和单个文本文件。无论您是否处于合并中,都会发生错误,并且钩子只是用索引存储,并且在没有索引的情况下正确地取消存储。我没有任何全新安装的简单机器,但是如果您想使用它们,repo 中的这些说明应该会在 100% 的情况下触发错误。我知道这是通过管道传输到 git diff 的。谢谢!
  • @Sukima - 在预提交挂钩中存储更改并不是世界上最疯狂的事情。我见过的大量示例脚本都是这样做的,这样你只需要检查和测试即将提交的内容,而不是刚刚更改的内容。您还必须 reset --hard 正确撤消 stash 所做的操作,否则 git stash pop 会给您合并冲突。我明白你在说什么关于合并,我可能会通过查看挂钩运行时MERGE_MSG 是否存在来禁用合并。但是,这也发生在不合并时,所以......我很困惑!
  • @hjc1710 - 是的 - 现在我看到了你的错误。将进一步调查。

标签: git sh githooks


【解决方案1】:

感谢您让这个复制变得如此容易!

问题似乎是钩子内的 git 命令正在将空白错误写入当前的tty 而不是stdoutstderr。然后,hook runner 拾取错误,然后错误地声称它是stdout

修复来自the answer 对我之前的一个问题。

#!/bin/sh
git stash > /dev/null
unbuffer sh -c 'git stash apply -q --index  && git stash drop -q ' >/dev/null 2>&1

编辑:如果你想要 OSX 上的 unbuffer 程序,你可以使用指令here 获得它。

brew tap homebrew/dupes/expect
brew install homebrew/dupes/expect

【讨论】:

  • 我很高兴让测试变得如此简单!这是一个很难解释的问题,所以我认为回购更容易。另外,非常感谢您的回答和回复!不幸的是,unbuffer 默认情况下没有安装,我不知道它是否适用于 OSX 或 OSX 模拟是什么(我有大约 8 个 OSX 人),我真的不想带在一个外部包装中,所以我不会接受你的回答。你做了很棒的工作告诉我为什么这会发生` >/dev/null 2>&1`,我感谢你,但我更喜欢编辑本地gitconfig 的答案!
  • 无需证明不接受答案。我很高兴你从中学到了一些东西。 :)
  • 谢谢!我真的很感谢你的时间,我一直很喜欢了解 TTY 和伪 TTY,它们绝对是令人着迷
【解决方案2】:

正如this answer 中的第二个脚注所指出的,git stash apply 只是调用git diff ... | git apply --index。警告来自这些命令。

当您有尾随空格、存储包含尾随空格的文件,然后应用存储时,就会发生这种情况。 更准确地说,每当您尝试将git apply 尾随换行符添加到代码体时,都会发生这种情况

如果您创建一个带有尾随空格的 git 补丁并尝试通过 cat a.patch | git diff --index 应用它,则会产生相同的错误。

至于解决这个问题,按理说在本地、全局或系统gitconfig 中设置core.whitespace=nowarn 会使其静音,但这对我来说似乎不是真的,但YMMV . (这些设置似乎没有被读取)。 运行git config core.whitespace -blank-at-eol,正如 cmets 中所指出的那样。

除了上述之外,我看到的唯一解决方案是在 git-stash bash 脚本中找到有问题的行,然后自己将 --whitespace=nowarn 参数添加到 git apply。我应该提到的是一个相当非标准、不可移植的解决方案。你必须让每个人都这样做,很可能每次 git 更新时也是如此。但是,如果他们无休止地打扰您,那就是您可以做的。

从好的方面来说,正如你所提到的,它可以解决,但如果我没有把事情弄得一团糟,那么在 git 上打开一个错误可能是值得的,而且它 甚至 忽略了系统 gitconfig。我希望它至少应该尊重这一点。

【讨论】:

  • 如果你想要一个命令来替换那一行,下面的代码对我有用sudo sed -ie '/git diff.*git apply/s/apply --cached/apply --cached --whitespace=nowarn/' [location of git-stash script]。对我来说,它位于/usr/lib/git-core/git-stash
  • 可以确认,如果您打开 /usr/lib/git-core/git-stash 并将行 422 更新为:git diff-tree --binary $s^2^..$s^2 | git apply --whitespace=nowarn --cached整个问题就会消失。但是,您是 100% 正确的,这是 可移植的,而且是一件 可怕 的事情,所以这不是我要做的事情。但是,我认为您的 git config 略有错误。如果我编辑本地 git 配置 (.git/config) 并添加 core.whitespace=nowarn 它不起作用,并且查看文档,这不是受支持的命令,但添加 core.whitespace=-blank-at-eol 有效!
  • 我想我会在 git 上打开一个问题,说 git stash apply 也应该尊重 --whitespace=nowarn,就像 git apply 一样,但我不认为这是一个比这更大的错误.非常感谢您的时间和精力!你真的做了很多工作来追捕这个问题,找出问题所在并提出解决方案(我最终会使用)。你赢得了赏金和正确的答案!
  • @hjc1710 我可以确认对422 行的更改可以解决问题。很高兴我能帮上忙,谢谢!
  • 太棒了!感谢您确认@Bujiraso。 Here 是我用来一劳永逸地摆脱错误的最后一个 gitconfig!幸运的是,我们有一个每个人都可以运行的设置脚本,因此将其添加到本地 git 配置中不是问题。
猜你喜欢
  • 1970-01-01
  • 2018-01-13
  • 2018-01-26
  • 2014-11-12
  • 1970-01-01
  • 2018-05-30
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
相关资源
最近更新 更多