包含混合编码文件的现有 Git 存储库
Existing Git repository with files in mixed encodings
我有一个包含混合编码文件的现有存储库 - 一些文件是 UTF-8,一些是 ANSI(例如 Windows-1252)。大多数情况下一切正常,除了我厌倦了在 ANSI 文件上执行差异时看到 "invalid characters",而且我特别恼火的是我不能使用我的 GUI 工具来暂存或取消暂存这些字符的帅哥。我正在寻找一种方法来说服 Git 某个文件使用非 UTF-8 编码,以便 Git 会先执行转换,然后再对其施展魔法。
据我所知,有两种实现结果的方法:
自定义二进制到文本过滤器
- 向我的 .gitconfig 添加一个转换过滤器:
[diff "win1252"]
textconv = "iconv -f windows-1252 -t utf-8"
- 在
.gitattributes
中,将文件标记为二进制文件并请求使用此过滤器将其转换为文本:
*.txt diff=win1252
这种方法在孤立的 git diff
中似乎工作得很好,但我遇到了几个我不知道如何解决的问题:
- 即使使用
core.autocrlf = true
,这种方法也不会对转换命令的输出执行 CRLF 转换,因此我的 diff 将显示更改行的行尾差异。我可以创建一个脚本 运行 iconv 来执行编码转换,然后将输出传递给 dos2unix 来执行 EOL 转换,但它看起来相当笨拙。
- 由于外部工具的使用非常频繁,我的速度明显变慢。
- 似乎命令行和我的 GUI (SourceTree) 在暂存时都不遵守转换设置。
git add -p
显示垃圾(甚至比 "unknown characters" 更糟糕)并且 SourceTree 停止暂存并显示一条错误消息,指出找不到原始文本。
虽然我可能能够学会接受#1 和#2,但#3 是一个阻塞问题,因为我主要需要完成此转换以促进其中包含 "unknown characters" 的帅哥的分期。我当前使用 git add -p
而不进行任何转换的工作流程可能会显示 "unknown characters",但至少它有效。
不切实际的 GUI 更改:我尝试过的所有其他 GUI 的问题都比这严重得多。
使用工作树编码属性
- 在
.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
) 忽略该修订,一切正常。
我有一个包含混合编码文件的现有存储库 - 一些文件是 UTF-8,一些是 ANSI(例如 Windows-1252)。大多数情况下一切正常,除了我厌倦了在 ANSI 文件上执行差异时看到 "invalid characters",而且我特别恼火的是我不能使用我的 GUI 工具来暂存或取消暂存这些字符的帅哥。我正在寻找一种方法来说服 Git 某个文件使用非 UTF-8 编码,以便 Git 会先执行转换,然后再对其施展魔法。
据我所知,有两种实现结果的方法:
自定义二进制到文本过滤器
- 向我的 .gitconfig 添加一个转换过滤器:
[diff "win1252"]
textconv = "iconv -f windows-1252 -t utf-8"
- 在
.gitattributes
中,将文件标记为二进制文件并请求使用此过滤器将其转换为文本:
*.txt diff=win1252
这种方法在孤立的 git diff
中似乎工作得很好,但我遇到了几个我不知道如何解决的问题:
- 即使使用
core.autocrlf = true
,这种方法也不会对转换命令的输出执行 CRLF 转换,因此我的 diff 将显示更改行的行尾差异。我可以创建一个脚本 运行 iconv 来执行编码转换,然后将输出传递给 dos2unix 来执行 EOL 转换,但它看起来相当笨拙。 - 由于外部工具的使用非常频繁,我的速度明显变慢。
- 似乎命令行和我的 GUI (SourceTree) 在暂存时都不遵守转换设置。
git add -p
显示垃圾(甚至比 "unknown characters" 更糟糕)并且 SourceTree 停止暂存并显示一条错误消息,指出找不到原始文本。
虽然我可能能够学会接受#1 和#2,但#3 是一个阻塞问题,因为我主要需要完成此转换以促进其中包含 "unknown characters" 的帅哥的分期。我当前使用 git add -p
而不进行任何转换的工作流程可能会显示 "unknown characters",但至少它有效。
不切实际的 GUI 更改:我尝试过的所有其他 GUI 的问题都比这严重得多。
使用工作树编码属性
- 在
.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
) 忽略该修订,一切正常。