GIT pre-commit hook 在 modified/added 文件中搜索非 UTF-8 编码(如果找到则拒绝提交)

GIT pre-commit hook which searches non-UTF-8 encodings among modified/added files (and rejects commit if it finds any)

我将 Git 用于 Windows(和 TortoiseGit)。

我的目标是防止 modified/added 中至少有一个非 UTF-8 文件的提交。

非常感谢任何帮助。

所以答案是(感谢 phd and great thx to torek 他的有用笔记):

    git diff --name-only --staged --diff-filter d | xargs -I {} bash -c 
 "iconv -f utf-8 -t utf-16 {} &>/dev/null || { echo {} - is non-UTF8!; exit 1; }"

此代码遍历提交时更改的所有文件(删除的除外 - 即添加、修改、复制和重命名)并检查是否有任何非 UTF8 文件。列出所有找到的文件并中止提交。

您现有的解决方案可能就足够了。虽然它不是 100% 正确:这里是剩余的问题,所有这些都是小问题,您可以稍后(如果有的话)在您有空的时候修复:

  • 您只需要 git diff ... --staged(或 --cached),因为 Git 将提交 index/staging-area 中的任何文件,并且git diff 将其与 HEAD 提交中的内容进行比较,并告诉您那里有什么不同。如果索引中的文件副本与 HEAD 中的文件副本不同,您应该检查索引副本。

  • 从技术上讲,最好在此处使用 git diff-index --cached,以便不遵守任何用户的 git diff 配置。也就是说,git diff-index 是 Git 中的 plumbing 命令,这意味着它旨在被其他计算机程序使用:它 运行 在完全可预测的方式仅基于参数,而不是基于任何 git config 设置。但是,如果您是为自己执行此操作,并且您配置 git diff 以致它破坏了您自己对 git diff 的使用,那么,那是您自己的错。 :-)

  • 您也可以考虑在此处使用 --diff-filter 来排除已删除的文件。否则,您的检查器将始终无法删除(因为 iconv 将无法读取已删除的文件)。

  • 最重要的:iconv将从工作树读取文件。正如我在第一个要点中指出的那样,Git 将提交 暂存的 ,而不是工作树中的内容。

举个例子——在 TortoiseGit 中可能可行也可能不可行——考虑一下如果你这样做会发生什么:

$ git checkout master
$ printf '000' > badfile    # put bad non-UTF-8 crud into file
$ git add badfile                    # copy file into index
$ echo 'good data' > badfile         # replace work-tree contents
$ git commit

此提交将 提交 索引中的错误内容——0 的三个字节没有换行符,但是你的预提交挂钩将运行iconv -f utf-8 -t utf-16遍历good文件的内容,阅读good data,那当然很好。

要解决此问题,您的预提交过滤器必须从要提交的每个文件的索引 中提取数据。你如何去做取决于你。最简单(但可能最慢)的方法是使用 git checkout-index 将整个索引内容提取到临时工作区。更好的方法可能是将每个 in-index(in-staging-area)路径名转换为有效的索引说明符(即 path/to/file 变为 :path/to/file)并使用 git cat-file -p $specifier | iconv ... 扫描每个.但所有这些都将相当低效,尤其是在 Windows 上。为了提高效率,您可能需要编写一个 Python 脚本,该脚本使用 git cat-file --batch 一次提取它们,并在那里进行格式检查。