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

5

我有一个现有的存储库,其中包含混合编码的文件 - 一些文件是UTF-8编码,一些文件是ANSI编码(例如Windows-1252)。大多数情况下都可以正常工作,但当对ANSI文件执行差异操作时,我看到“无效字符”已经感到厌烦了,而且我特别烦恼的是我不能使用GUI工具来暂存或取消暂存这些字符。我正在寻找一种方法来说服Git认为某个文件使用非UTF-8编码,以便Git首先执行转换,然后再对其进行操作。

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

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

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

    这种方法在独立的git diff中似乎可以正常工作,但我遇到了几个问题,不知道该如何解决:

    1. 即使使用core.autocrlf = true,这种方法也不会对转换命令的输出执行CRLF转换,因此我的差异将显示更改行中的行尾差异。我可以创建一个脚本来运行iconv执行编码转换,然后将输出传递给dos2unix执行EOL转换,但这似乎有些笨重。
    2. 由于经常使用外部工具,我经历了显着的减速。
    3. 似乎无论是命令行还是我的GUI(SourceTree),都不会在暂存时遵守转换设置。git add -p显示垃圾(甚至比“未知字符”更糟),而SourceTree则停止暂存,并显示找不到原始文本的错误消息。

    虽然我可能能够适应#1和#2,但#3是一个阻碍问题,因为我大多数需要完成这些转换以便于暂存带有“未知字符”的块。我的当前工作流程是使用没有任何转换的git add -p,虽然可能会显示“未知字符”,但至少它可以工作。

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

    使用working-tree-encoding属性

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

    据我所知,这种方法涵盖了上述所有投诉,并且在命令行和GUI中都可以正常工作。不幸的是,有一个重要的警告:它仅适用于在设置此属性后创建的文件。对于在添加此属性之前创建的文件,Git将显示更改(从“未知字符”到windows-1252),其中包含这些未编码字符的每个文件都会受到影响。此外,在克隆存储库之后,它将抱怨“无法将' a.txt '从UTF-8编码为windows-1252”。似乎文件实际上已经正确地克隆(与原始文件逐字节匹配),但仍然显示差异。基本上,我必须提交每个带有“未知字符”的文件以将其重新编码为UTF-8存储在存储库中,这将导致我的历史记录混乱,使Blame几乎无法使用。

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


    有没有办法克服所描述方法的缺点,或者有没有另一种不会受到这些缺点影响的方法?

    1个回答

    7
    您使用working-tree-encoding已经走在了正确的道路上,但是还需要再执行一步操作。
    在创建.gitattributes文件的同一次提交中,运行git add --renormalize .,它将根据指定的编码过滤所有的工作区文件。然后,您需要在同一次提交中提交所有更改的文件和.gitattributes文件,之后它们将以UTF-8格式存储在仓库中,但在您的工作区中仍然是Windows-1252格式。
    这样做的缺点是git blame将不得不跳过该提交,但是您可以使用--ignore-rev--ignore-revs-file(或配置选项blame.ignoreRevsFile)来忽略该修订版本,并且一切都将正常工作。

    那个 blame.ignoreRevsFile 看起来是一个很好的解决方案,即使对于一些过去的大规模更改也是如此。我需要在这里进行一些实验,但我认为这可能是正确的方法。谢谢!一旦我完成它,我会将答案标记为解决方案。 - pepak
    此外,还有merge.renormalize配置设置,会在运行合并之前使用当前设置重新运行所有这些转换。 - jthill

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接