热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

为什么GIT本身不支持UTF-16

如何解决《为什么GIT本身不支持UTF-16》经验,为你挑选了2个好方法。

GIT中支持多种不同的编码方案:UTF-7UTF-8UTF-32,以及非UTF的。

鉴于此,它为什么不支持UTF-16

有很多问题询问如何获取git以支持UTF-16,但我认为这尚未明确提出或得到回答。



1> torek..:

我将我(目前处于垂死状态)这本书的整整一整章(请参阅第3章,其形式比后面的几章都更好)中的很大一部分专门用于字符编码问题,因为这是一个历史性的混乱。不过,在这里值得一提的是,这个问题的前提的一部分(Git以某种方式支持UTF-7和UTF-32)是错误的:UTF-7是一个从未出现过的标准,也许永远都不应使用根本(自然,较旧的Internet Explorer版本会这样做,这会导致链接的Wikipedia页面上提到的安全性问题)。

就是说,让我们首先将字符编码代码页分开。(也请参见下面的脚注部分。)这里的基本问题是,计算机(无论如何,现代计算机)都使用一系列8位字节,每个字节代表[0..255]范围内的整数。 。较早的系统具有6、7、8甚至9位字节,尽管我认为将小于8位的任何内容称为“字节”都是令人误解的。(BBN的“ C机器”具有10位字节!)在任何情况下,如果一个字节代表一个字符符号,则我们可以得到256种符号的上限。在ASCII的糟糕年代,这已经足够了,因为ASCII只有128个符号,其中33个是非打印符号(控制代码)0x000x1f,再加上0x7fDEL或在纸带上删除的打孔器,然后在此处以十六进制表示)。

当我们需要超过94个可印刷符号和空间(0x20),我们按我们我的意思是人们使用计算机世界各地,没有具体 -所述:嗯,看看这个,我们有128个未使用的编码,0x80通过0xff,让我们使用其中一些! 因此,法语使用了ç和é等,以及诸如«和»的标点符号。捷克人需要一个带卡纸的Z,z?。俄罗斯人需要很多东西,用于西里尔字母。希腊人需要很多东西,依此类推。结果是8位空间的上半部分爆炸成许多不兼容的集合,人们称之为代码页

本质上,计算机存储一些八位字节的值,例如235十进制(0xEB十六进制),这取决于其他事情–另一个计算机程序,或者最终是人盯着屏幕,将235解释为西里尔字母?字符,还是希腊语?,或其他。如果我们使用的是一个代码页,它将告诉我们“ 235”的含义:我们应该对此施加什么样的语义。

这里的问题是我们可以支持多少个字符代码。如果我们想让西里尔字母L(?)与希腊字母L(lambda,?)共存,我们不能同时使用CP-1251 CP-1253,因此我们需要一种更好的方式来编码符号。一种明显的方法是停止使用一个字节的值来编码符号:如果使用两个字节的值,则可以0x0000通过0xffff包括在内的方式对65536个值进行编码;减去一些控制代码,仍然有很多字母的余地。但是,我们甚至迅速突破了这个限制,因此我们选择了Unicode,它可以容纳1,114,112个所谓的代码点的空间。,每个代表一种具有某种语义含义的符号。目前已使用了100,000多种,包括emoji,和。

将Unicode编码为字节或字

这是UTF-8,UTF-16,UTF-32,UCS-2和UCS-4都加入的地方。这些都是用于 Unicode码点(约100万个值之一)编码为字节流的方案。我将完全跳过UCS的内容,仅查看UTF-8和UTF-16编码,因为这是当前最有趣的两种。(另请参阅什么是Unicode,UTF-8,UTF-16?)

UTF-8编码很简单:十进制值小于128的任何代码点都被编码为包含该值的字节。这意味着普通ASCII文本字符保留为普通ASCII文本字符。从0x0080(128十进制)到0x07ff(2047十进制)的代码点编码为两个字节,它们的值都在128-255范围内,因此可以与一个字节的编码值区分开。0x0800直通0xffff范围内的代码点在相同的128-255范围内编码为三个字节,其余有效值编码为四个此类字节。 就Git本身而言,此处的关键是没有编码值类似于ASCII NUL(0x00)或斜杠(0x2f)。

这种UTF-8编码的作用是让Git 假装文本字符串(尤其是文件名)是用斜杠分隔的名称组件,它们的结尾都或者可以用ASCII NUL字节标记。这是Git在对象中使用的编码,因此UTF-8编码的树对象正好适合,而无需摆弄。

UTF-16编码每个字符使用两个成对的字节。对于Git和路径名,这有两个问题。首先,一对中的一个字节可能会意外地相似/,并且所有ASCII值字符必须编码为一对字节,其中一个字节0x00类似于ASCII NUL。因此,Git需要知道:此路径名已使用UTF-16进行编码,并且适用于字节对。树对象中没有空间可以容纳此信息,因此Git需要一个新的对象类型。其次,每当我们将16位值分成两个单独的8位字节时,我们都会以某种顺序执行此操作:我要么先给您更高的有效字节,然后再给您较低的字节;或者我先给您一个低位有效字节,然后给您高位有效字节。第二个问题导致UTF-16具有字节顺序标记。UTF-8不需要字节顺序标记就足够了,那么为什么不在树中使用它呢?Git也是如此。

对树来说很好,但是我们也有提交,标签和斑点

Git对这四种对象中的三种做自己的解释:

    提交包含哈希ID。

    树包含路径名,文件模式和哈希ID。

    标签包含哈希ID。

这里未列出的是blob,并且在大多数情况下,Git不会对blob进行任何解释。

为了易于理解提交,树和标签,Git在大多数情况下将所有三个约束都限制在UTF-8中。但是,Git 确实允许提交中的日志消息标签中的标签文本在某种程度上(大部分是)未解释。它们位于Git解释的标头之后,因此,即使此时有些特别棘手或丑陋的地方,也很安全。(这里存在一些较小的风险,因为出现在标题下方的PGP签名确实会被解释。)特别是对于提交,现代Git将在解释部分中包含一个编码标题行,然后Git可以尝试提交消息进行解码正文,然后重新编码它转换为任何程序解释Git吐出的字节使用的编码。1个

相同的规则可用于带注释的标记对象。我不确定Git是否具有对标签执行此操作的代码(大多数情况下都可以重复使用提交代码,但是标签更常见的是具有PGP签名,并且在此处强制使用UTF-8可能更明智)。由于树是内部对象,因此无论如何它们的编码基本上都是不可见的-您不需要意识到这一点(我在书中指出的问题除外)。

这留下斑点,是大猩猩。


1这是计算世界中反复出现的主题:重复编码和解码所有内容。考虑一下某些事物是如何通过WiFi或电缆网络连接到达的:它被编码为某种无线电波或类似的无线电波,然后一些硬件将其解码为比特流,然后其他一些硬件将其重新编码为字节流。硬件和/或软件剥离标题,以某种方式解释剩余的编码,适当地更改数据,并对位和字节重新编码,以供另一层硬件和软件处理。一切都完成了,真是一个奇迹。


斑点编码

Git喜欢声称它与文件中存储的实际数据完全无关,就像Git斑点一样。甚至大部分都是如此。或者,好吧,一半正确。或者其他的东西。只要Git所做的一切就是存储您的数据,那是完全正确的!Git只是存储字节。这些字节的含义取决于您。

当您运行git diff或时git merge,这个故事会分崩离析,因为diff算法以及合并代码都是面向的。行以换行符终止。(如果您使用的是CRLF而不是换行符的系统,那么CRLF对的第二个字符换行符,因此这里没有问题-Git可以使用不终止的最后一行,尽管这会导致一些次要问题如果文件使用UTF-16编码,则很多字节似乎是ASCII NUL,因此Git只是将其视为二进制。

可修复的:Git可以将UTF-16数据解码为UTF-8,通过其所有现有的面向行的算法(现在将看到换行终止的行)提供该数据,然后将数据重新编码回UTF -16。这里有很多较小的技术问题。最大的决定是确定某个文件 UTF-16,如果是,则确定哪个字节序(UTF-16-LE或UTF-16-BE?)。如果文件具有字节顺序标记,则可以解决字节序问题,并且可以将UTF-16-ness编码为.gitattributes与您当前声明文件binary或一样text,因此都可以解决。这只是一团糟,还没有人做过这项工作。

脚注式:代码页可以被认为是(糟糕的)编码形式

上面我提到过,我们使用Unicode所做的事情是将21位代码点值编码为一定数量的8位字节(UTF-8中为1到4个字节,UTF-16中为2个字节,这是一个丑陋的小技巧)用UTF-16调用的替代方法将21位值压缩到16位容器中,有时使用成对的16位值。这个编码技巧意味着我们可以表示所有合法的21位代码点值,尽管我们可能需要多个8位字节才能这样做。

当我们使用代码页(CP- number)时,我们正在做的是,或至少可以将其视为 256个值(适合一个8位字节的值)映射该21位代码点空间中。我们从不超过256个这样的代码点中选出一些子集,然后说:这些是我们允许的代码点。 我们将第一个编码为,0xa0将第二个编码为0xa1,依此类推。我们总是留出至少一些控制代码的空间-通常是0x00直通0x1f范围内的所有32个代码-通常我们会像Unicode本身一样保留整个7位ASCII子集(请参阅https://en.wikipedia.org/wiki/ List_of_Unicode_characters),这就是为什么我们最通常从开始0xa0

当人们编写适当的Unicode支持库时,仅使用这种形式的索引就可以将代码页简单地转换为转换表。困难的部分是为所有代码页创建准确的表,其中有很多代码页。

关于代码页的好处是,每个字符又是一个字节。不好的是,当您说:我使用此代码页时,只需选择一次符号集 从那时起,您就被锁定在Unicode的这一小部分中。如果切换到另一个代码页,则部分或全部八位字节值表示不同的符号。



2> VonC..:

Git代码库中对UTF-8的首次提及可以追溯到d4a9ce7(2005年8月,v0.99.6),它是关于邮箱补丁的:

可选地,使用'-u'标志,.info和.msg的输出将从其原始字符集转译为utf-8。这是为了鼓励人们在提交消息中使用utf8以实现互操作性。

这是由Junio C Hamano签名的。?

在提交3a59e59(2017年7月,Git v2.6.0-rc0)中阐明了字符编码

“ git编码不可知”仅对于blob对象才是真的。
例如,树和提交对象的“非NUL字节”要求不包括UTF-16 / 32,/索引文件中“ ” 的特殊含义以及提交对象中的空格和换行符消除了EBCDIC和其他非ASCII编码。

Git期望<0x80的字节是纯ASCII,因此与ASCII范围部分重叠的CJK编码也是有问题的。
例如fmt_ident(),假设它是ASCII' \' ,则从用户名中删除结尾的0x5C 。
但是,有200多个GBK双字节代码以0x5C结尾。

UTF-8是Linux上的默认编码,而Mac和Windows版本中的相应路径转换已将UTF-8 NFC建立为事实上的路径名标准。

有关最后一个补丁的更多信息,请参见“ git,msysgit,重音符号,utf-8,确定的答案 ”。

最新版本Documentation/i18n.txt包括:

Git在某种程度上与字符编码无关。

Blob对象的内容是未解释的字节序列。核心级别没有编码转换。

路径名以UTF-8规范化形式C编码
这适用于:

树对象

索引文件

引用名称以及路径名称

命令行参数

环境变量和

配置文件(.git/configgitignoregitattributesgitmodules

您可以在提交0217569中看到UTF-8路径转换的示例(2012年1月,Git v2.1.0-rc0,其中添加了Win32 Unicode文件名支持。

更改opendir/ readdir以使用Windows Unicode API并在UTF-8 / UTF-16之间转换。

关于命令行参数,请参见。提交3f04614(2011年1月,Git v2.1.0-rc0),在启动时将命令行参数从UTF-16转换为UTF-8。


注意:在Git 2.21(2019年2月)之前,代码和测试假定提供的系统iconv()在被要求编码为UTF-16(或UTF-32)时将始终在其输出中使用BOM,但是显然某些实现会输出big-endian而没有BOM。
添加了编译时旋钮,以帮助此类系统(例如NonStop)将BOM添加到输出中以提高可移植性。

utf8:处理不为UTF-16编写BOM的系统

序列化UTF-16(和UTF-32)时,有三种可能的方式来写入流。一个人可以用大端或小端格式的BOM写入数据,或者一个人不用大端格式的BOM写入数据。

大多数系统的iconv实现都选择以某种字节序来编写BOM,因为这是最简单的方法,并且可以抵抗Windows上的误解,而Windows上UTF-16和Little-endian序列化非常普遍。
为了与Windows兼容并避免在此处的意外使用,Git始终希望使用BOM编写UTF-16,并且拒绝在没有它的情况下读取UTF-16。

但是,musl的iconv实现编写不带BOM的UTF-16,依靠用户将其解释为big-endian。这会导致t0028及其相关功能失败,因为Git在没有BOM的情况下不会读取文件。


推荐阅读
author-avatar
z515420281
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有