【问题标题】:Can't get git to play nice with iconv and utf-16无法让 git 与 iconv 和 utf-16 配合得很好
【发布时间】:2016-10-04 14:03:11
【问题描述】:

我正在尝试让 git 将 UTF-16 识别为文本,以允许我本地区分和修补文本,但我无法让 textconv 参数正常工作。

我可以手动调用

iconv -f utf-16 -t utf-8 some-utf-16-file.rc

一切都很好。但是如果我配置我的 .gitconfig 如下

[diff "utf16"]
    textconv = "iconv -f utf-16le -t utf-8"

还有我的 .git 属性:

# Custom for MFC
*.rc text eol=crlf diff=utf16

但是,如果我运行git diff,则会显示以下内容:

iconv: C:/Users/Mahmoud/AppData/Local/Temp/IjLBZ8_OemKey.rc:104:1: incomplete character or shift sequence

使用 procmon 我能够在创建此过程时对其进行跟踪:

sh -c "iconv.exe -f utf-16le -t utf-8 \"$@\"" "iconv.exe -f utf-16le -t utf-8" C:/Users/Mahmoud/AppData/Local/Temp/JLOkVa_OemKey.rc

...我实际上可以正常运行(但在实际文件上)。

有什么想法吗?

(请注意,我知道让 git 与 UTF-16 一起工作的各种解决方案。我特别想解决这个问题,即为什么 iconv 本身可以工作,但在被 git 调用时它不起作用。此外,此错误最初是在尝试“重复”问题中的链接解决方案之一时遇到的。谢谢大家。)

【问题讨论】:

  • @paulsm4 我的问题实际上是关于让 git 和 iconv 工作得很好,而不是让 git 与 UTF-16 一起工作;不过谢谢!
  • 不确定 - 这可能与 iconv.exe 是二进制文件有关吗?
  • 请记住 - 从 DOS/Windows“命令行”中,有一大堆不同的参与者参与其中:包括 Cygwin 和 Windows。请阅读我引用的链接:“GNU diff 并不真正关心 unicode,因此当您使用 diff --text 时,它只会区分并输出文本。问题是您使用的终端无法处理 UTF发出的 -16 (与作为 ASCII 字符的差异标记结合)。”请阅读多种不同的解决方法。
  • 在将该文件提供给 iconv 之前,git 会重写行尾字符(这会使 utf-16 混乱)吗?

标签: git utf-16 msysgit iconv


【解决方案1】:

只使用diff,它应该可以工作:

*.rc diff=utf16

texteol 导致 git 在将数据传递给 iconv 之前替换行尾,之后它不再是有效的 utf16,如 noted in comments

【讨论】:

  • 感谢您解释问题的原因并提供解决方案以继续使用尝试的方法(但正确)。太糟糕了,这不能与交互/补丁 git add 一起使用,但它不可能,因为过滤视图和源材料之间没有保证的一对一映射。
【解决方案2】:

Git 2.21(2019 年 2 月)添加了新的编码 UTF-16LE-BOM:发明以强制编码为带有 BOM 的小端字节顺序的 UTF-16,无法通过使用直接生成iconv.

参见Torsten Bögershausen (tboegi)commit aab2a1a(2019 年 1 月 30 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 0fa3cc7,2019 年 2 月 7 日)

支持工作树编码“UTF-16LE-BOM”

希望在工作树中使用 UTF-16 文件的用户可以这样设置 .gitattributes

test.txt working-tree-encoding=UTF-16

unicode 标准本身定义了 3 种允许的 UTF-16 编码方式。 以下 3 个版本全部转换回 UTF-8 中的 'g' 'i' 't':

a) UTF-16, without BOM, big endian:
$ printf "\000g\000i\000t" | iconv -f UTF-16 -t UTF-8 | od -c
0000000    g   i   t

b) UTF-16, with BOM, little endian:
$ printf "\377\376g\000i\000t\000" | iconv -f UTF-16 -t UTF-8 | od -c
0000000    g   i   t

c) UTF-16, with BOM, big endian:
$ printf "\376\377\000g\000i\000t" | iconv -f UTF-16 -t UTF-8 | od -c
0000000    g   i   t

Git 使用libiconv 将索引中的 UTF-8 转换为索引中的 ITF-16 工作树。
结帐后,生成的文件有一个 BOM 并以“UTF-16”编码, 在上面的版本(c)中。
这是 iconv 生成的,更多细节如下。

iconv(和libiconv)可以生成UTF-16、UTF-16LE或UTF-16BE:

d) UTF-16
$ printf 'git' | iconv -f UTF-8 -t UTF-16 | od -c
0000000  376 377  \0   g  \0   i  \0   t

e) UTF-16LE
$ printf 'git' | iconv -f UTF-8 -t UTF-16LE | od -c
0000000    g  \0   i  \0   t  \0

f)  UTF-16BE
$ printf 'git' | iconv -f UTF-8 -t UTF-16BE | od -c
0000000   \0   g  \0   i  \0   t

没有办法在 Git 工作树中从上面生成版本 (b), 但这正是某些应用程序所需要的。
(所有完全支持 unicode 的应用程序都应该能够读取所有 3 个变体, 但实际上,我们还没有实现)。

当生成 UTF-16 作为输出时,iconv 生成大端版本 带有 BOM。 (可能出于历史原因选择大端序)。

iconv 可以使用“UTF-16LE”生成字节序很小的 UTF-16 文件 作为编码,并且该文件没有 BOM。

并非所有用户(尤其是在 Windows 下)都对此感到满意。
有些工具不能完全识别 unicode,只能处理版本 (b)。

今天无法使用iconv(或libiconv)生成版本(b)。
查看 iconv 的历史,似乎 (c) 版本将用于所有未来的 iconv 版本(出于兼容性原因)。

解决这个难题并引入一个特定于 Git 的“UTF-16LE-BOM”。
libiconv 无法处理编码,因此 Git 将其拾取,处理 BOM 并使用 libiconv 转换流的其余部分。 (为了保持一致性,添加了 UTF-16BE-BOM)

【讨论】:

    【解决方案3】:

    git 最近开始理解编码,即iconv 现在在某种程度上是内置的。查看gitattributes 文档,搜索working-tree-encoding

    [确保您的手册页匹配,因为这是相当新的!]

    如果(比如说)文件是 utf-16,在 windows 机器上没有 bom,那么添加到你的 gitattributes 文件中

    some-utf-16-file.rc text working-tree-encoding=UTF-16LE eol=CRLF
    

    如果 *nix 上的 utf-16 little endinan (with bom) make it

    some-utf-16-file.rc text working-tree-encoding=UTF-16 eol=LF
    

    【讨论】:

    • 感谢您的更新!我猜这将适用于交互式补丁添加,所以非常受欢迎!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 2011-11-27
    • 1970-01-01
    • 2014-04-07
    • 2019-05-18
    相关资源
    最近更新 更多