包含混合编码文件的现有 Git 存储库

Existing Git repository with files in mixed encodings

我有一个包含混合编码文件的现有存储库 - 一些文件是 UTF-8,一些是 ANSI(例如 Windows-1252)。大多数情况下一切正常,除了我厌倦了在 ANSI 文件上执行差异时看到 "invalid characters",而且我特别恼火的是我不能使用我的 GUI 工具来暂存或取消暂存这些字符的帅哥。我正在寻找一种方法来说服 Git 某个文件使用非 UTF-8 编码,以便 Git 会先执行转换,然后再对其施展魔法。

据我所知,有两种实现结果的方法:

自定义二进制到文本过滤器

  1. 向我的 .gitconfig 添加一个转换过滤器:
    [diff "win1252"]
    textconv = "iconv -f windows-1252 -t utf-8"
  1. .gitattributes 中,将文件标记为二进制文件并请求使用此过滤器将其转换为文本:
    *.txt    diff=win1252

这种方法在孤立的 git diff 中似乎工作得很好,但我遇到了几个我不知道如何解决的问题:

  1. 即使使用 core.autocrlf = true,这种方法也不会对转换命令的输出执行 CRLF 转换,因此我的 diff 将显示更改行的行尾差异。我可以创建一个脚本 运行 iconv 来执行编码转换,然后将输出传递给 dos2unix 来执行 EOL 转换,但它看起来相当笨拙。
  2. 由于外部工具的使用非常频繁,我的速度明显变慢。
  3. 似乎命令行和我的 GUI (SourceTree) 在暂存时都不遵守转换设置。 git add -p 显示垃圾(甚至比 "unknown characters" 更糟糕)并且 SourceTree 停止暂存并显示一条错误消息,指出找不到原始文本。

虽然我可能能够学会接受#1 和#2,但#3 是一个阻塞问题,因为我主要需要完成此转换以促进其中包含 "unknown characters" 的帅哥的分期。我当前使用 git add -p 而不进行任何转换的工作流程可能会显示 "unknown characters",但至少它有效。

不切实际的 GUI 更改:我尝试过的所有其他 GUI 的问题都比这严重得多。

使用工作树编码属性

  1. .gitattributes 中,将文件标记为具有自定义编码的文本文件:
    *.txt    text working-tree-encoding=windows-1252

据我所知,这种方法涵盖了上面列出的所有问题,并且在命令行和 GUI 中都运行良好。不幸的是,有一个重要的警告:它仅适用于设置此属性后创建 的文件。对于在我添加此属性之前创建的文件,Git 将为包含这些未编码字符的每个文件显示一个更改(从 "unknown characters" 到 windows-1252)。此外,在克隆存储库后,它会抱怨它 "failed to encode 'a.txt' from UTF-8 to windows-1252"。似乎该文件实际上已正确克隆(与原始文件逐字节匹配),但它仍然显示出差异。基本上,我必须提交带有 "unknown characters" 的每个文件,以将其重新编码为存储库中的 UTF-8,这会导致我的历史记录变得糟糕,并且几乎使 Blame 无法使用。

似乎一个现实的方法可能是使用类似 git filter-branch 的东西,但是对于整个存储库(有没有类似的东西?)将所有现有文件转换为 UTF-8 并且 将属性添加到第一次提交,但我担心做这么大的事情。此外,我预计我会丢失之前的提交 ID,这将是不幸的(我用提交 ID 标记我的可执行文件以轻松找到构建它们的版本)。


是否有任何方法可以克服所描述方法的缺点,或者是否有其他方法不会受到它们的影响?

您使用 working-tree-encoding 的方法是正确的,但您还需要再执行一步。

在您创建 .gitattributes 文件的同一提交中,运行 git add --renormalize . 将获取所有工作树文件并根据指定的编码过滤它们。然后你会想要在同一次提交中提交所有更改的文件和 .gitattributes 文件,此后它们将作为 UTF-8 存储在 repo 中,但在你的 Windows-1252工作树。

这确实有一个缺点,即 git blame 必须在该提交之后跳回,但您可以指定 --ignore-rev--ignore-revs-file(或配置选项 blame.ignoreRevsFile ) 忽略该修订,一切正常。