git commit触发器阻止字节顺序标记

时间:2015-07-16 00:04:25

标签: git awk sed

我在Windows上,有时用记事本编辑文件,记事本喜欢在文件的开头放置一个BOM(EF BB BF)。在diff中很容易忽略这一点并且使用这样的BOM提交给Git一个Python文件,我发现它在Mac上不起作用。

我想创建一个提交触发器,在提交之前删除BOM。或者至少拒绝提交。

我提出的最好的是下面的脚本,我在'pre-commit'中输入。它删除任何BOM但仅在提交之后,因此我必须进行第二次提交。

#!/bin/sh
git diff --cached --diff-filter=ACMR --name-only -z *.py | xargs -0 -n 1 sh -c '
    for FILE; do
        sed -b -i -e "1s/^\xEF\xBB\xBF//" "$FILE"
    done
' sh

我试图像这样使用命令和'q',如果匹配则退出代码为1,但它不起作用。

#!/bin/sh
git diff --cached --diff-filter=ACMR --name-only -z *.py | xargs -0 -n 1 sh -c '
    for FILE; do
        sed -b -i -e "1 /^\xEF\xBB\xBF/ {s/^\xEF\xBB\xBF//;q1};q0" "$FILE"
    done
' sh

有人可以帮忙解决吗?

2 个答案:

答案 0 :(得分:3)

你走在正确的轨道上。

预提交钩子的一个好的一般规则是不修改索引内容(即,#34;不要改变提交或工作目录,甚至不尝试")而只是为了提交失败,因此您的第二个代码块可能更接近 - 但您仍在修改文件。如果您愿意,可以执行此操作,如果确实想要,您甚至可以git add。它通常不是一个好主意:它往往太令人惊讶了,并且它通过精心设计的版本与故意不同于工作目录版本(例如由git add -p生成)来做出意想不到的事情。

此处还有两个选项:您只能检查新文件和修改过的文件(这是--diff-filter的用途);或者您可以检查索引中的每个文件。如果您想允许任何现有(但未经修改)的文件保留现有的Unicode-BOM,您肯定需要新的 - 仅修改过的方法,所以让我们坚持下去。我也会保留*.py,但我们希望保护它免受shell的影响,以便它使用git的名称以.py结尾的文件,而不是shell&# 39; S。特别是,这意味着如果索引中存在一些.py文件,并且因此将提交,如果提交继续但不在工作目录中,则会检查它们。

我们可以通过向--no-renames命令添加diff来简化diff过滤器,以便不会发生R状态。我们也知道C不应该发生,因为我们没有提供任何-C--find-copies-harder选项。因此,我们从:

开始
git diff --cached --no-renames --diff-filter=AM --name-only -- '*.py'

我已经取出了-z-z如果我们可以使用xargs -0那就很好,但我打算一次只读一个文件名相反,因为大多数这些命令一次只能在一个文件上运行。 (也可以使用xargs执行此操作,但如果您的文件名都没有包含换行符,那么没有它我们就可以了。)--将diff选项与路径(这似乎不应该是必需的,但请参阅下面的评论;无论如何,它通常都是一个好主意。)

这会生成一个要检查的文件列表,所以现在让我们检查(但不要编辑)它们。如果您使用的是Windows,则可能需要修改以下内容以使用您拥有的有限工具;由于我总是在Linux或Unix机器上,我使用head -1获取第一行,并grep检查BOM:

#! /bin/sh
git diff --cached --no-renames --diff-filter=AM --name-only -- '*.py' |
(status=0; while IFS= read path; do
    if git show ":$path" | head -1 | grep $'^\xEF\xBB\xBF' >/dev/null; then
        echo "Error: file '$path' starts with Unicode BOM.'"
        status=1
    fi
done
exit $status)

以下是各种技巧:

  • 我们在阅读过程中将IFS设置为空,以允许其他类型的空白区域。 (对于使用-z的方法,因此也处理换行符,请参阅下面的Etan Reisner's comments。)
  • 我们使用git show ":$path"来提取索引中文件的版本。这可能(例如,与git add -p一样)与工作目录中的文件版本不同。
  • 我们使用head -1丢弃除第一行以外的所有内容。
  • 我们使用grep检查BOM,我们使用shell字符串扩展($'...')进行检查,并将grep的输出定向到/dev/null,以便它不会#39; t显示(grep -q也有效但仅当特定grep支持-q时)。
  • 我们继续检查所有列出的文件,即使有些文件有BOM。
  • 要使用管道(cmd | while ...在子shell中运行while)来解决shell的子shell操作,我们在显式(带括号的)子shell中设置状态并以该状态退出该子shell。如果没有BOM,则传播子shell的状态成功,如果主shell出现故障,那么它可能成为git hook的结果。

注意:以上内容未经过完整测试(尽管我认为它是正确的)。

答案 1 :(得分:2)

您可以运行过滤器以在添加时剥离BOM:

git config filter.strip-bom.clean "sed '1s/^\xef\xbb\xbf//'"
git config filter.strip-bom.smudge cat

echo '*.c filter=strip-bom'  >> .git/info/attributes

等等其他模式。把它放在.git/info中会使它严格地回复本地,所以你有一个完全自由的手。

如果您没有使用GNU /我认为sed可能无法处理转义,过滤器文本会被双引号,因此您可以在$(printf '\xef\xbb\xbf')中获取它。

键盘到编辑框警告,但我认为语法是正确的。