我在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
有人可以帮忙解决吗?
答案 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
时)。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')
中获取它。
键盘到编辑框警告,但我认为语法是正确的。