Git弄乱了我的文件,在某些地方显示了汉字

时间:2013-07-07 19:56:37

标签: git unicode gitattributes

免责声明:Git,我的意思是'我'搞砸了。

Earlier,我希望git-gui向我展示它认为diff是二进制文件。

所以我对.\.gitattributes

进行了一些更改
*.ini       text
*.inc       text

But it didn't work. Then I made some changes.\.git\info\attributes

*.ini       text
*.inc       text
*.inc crlf diff
*.ini crlf diff

它有效。

但现在当我回到之前的承诺时,它会搞砸......

chinese characters 这就是应该的样子: english characters

在所有文件中都不会发生这种情况。 编辑:仅在包含任何特殊字符的文件中发生。

问:这是提交本身的问题还是只是一些设置?
问:我可以恢复吗?

4 个答案:

答案 0 :(得分:26)

您的ini文件以UTF-16LE格式保存,Windows编码错误地将其描述为“Unicode”。

Git的默认差异工具不适用于UTF-16,因为它不是与ASCII兼容的编码。这就是为什么git最初将文件检测为二进制文件。

LF / CRLF换行转换将每个0x0A字节视为换行符,并将其替换为0x0D-0x0A。但是,在UTF-16LE文件中,换行实际上是由0x0A-0x00发出信号,并用0x0D-0x0A-0x00替换它意味着你有一个奇数个字节,所以每个双字节代码单元的对齐在下一行不同步。因此,每一条线都会被破坏。

您的选择是:

  1. 恢复属性更改,让Git将文件处理为二进制文件(失去差异的好处)。

  2. 以ASCII兼容编码保存文件。看起来你的内容实际上没有任何非ASCII字符,所以希望这不是问题吗?通常,您希望将所有文件保存为UTF-8 - 这与ASCII兼容,但也允许使用所有Unicode字符。但这取决于Rainmeter是否支持读取那样编码的INI文件(可能不是)。

  3. 将git配置为use a different diff tool,但这会让其他人使用您的回购邮件变得更加复杂。

答案 1 :(得分:7)

我最近遇到了类似的问题。我们在根级别有一个项目范围的.gitattributes文件,其中包括以下行: -

* text=auto
*.sql     text

我们的一个团队正在使用SQL Management Studio编写SQL代码,他不知道将文件保存为UTF-16。他能够毫无问题地将代码签入Git,但在签出时,代码被翻译成中文字符,如本文所述。

有问题的文件的hexdump确认问题确实是0x000A到0x000A0D的转换。

对我们来说,解决方案是使用以下方法将文件转换为ASCII: -

  1. 从工作目录中删除违规文件
  2. 在本地目录中创建一个临时.gitattributes文件,强制git签出文件而不执行行结束转换。例如包括第*.sql binary

  3. 从Git中签出文件。您应该看到文件尚未翻译且没有中文字符。

  4. 将文件转换为ASCII。我们使用了Notepad ++,但也可以使用iconv,它是作为Git For Windows的一部分安装的。我认为如果文件包含非ASCII字符,UTF-8也是一个选项 - 但这对我们来说并不是必需的。
  5. 签入文件的ASCII版本
  6. 删除本地.gitattributes文件

答案 2 :(得分:1)

这是一个(错误的)power-shell脚本,它将在此状态下修复文件。它将用“ 0x0D 0x00 0x0A”替换序列“ 0x0D 0x00 0x0D 0x0A”,然后覆盖给出的文件。

此后,您可能应该以UTF-8之类的格式重新保存文件。

function Fix-Encoding
{
    Param(
        [String]$file
    )
    $f = get-item $file;
    $bytes = [System.IO.File]::ReadAllBytes($f.fullname);
    $output = new-object "System.Collections.Generic.List[System.Byte]"
    $output.Capacity = $bytes.Length

    for ($i = 0; $i -lt $bytes.Length; $i++)
    { 
        if ($i -lt $bytes.Length + 3)
        {
            if ($bytes[$i] -eq 0x0D -and $bytes[$i+1] -eq 0x00 -and $bytes[$i+2] -eq 0x0D -and $bytes[$i+3] -eq 0x0A) 
            {
                $output.Add(0x0D);
                $output.Add(0x00);
                $output.Add(0x0A);
                $i += 3
            }
            else {
                $output.Add($bytes[$i]);
            }
        }
     }
    [System.IO.File]::WriteAllBytes($f.fullname, $output)
}

答案 3 :(得分:0)

为@bobince添加一个很好的解释。这个问题的一个解决方案(具有特殊字符的文件除外)是将所有内容转换为utf-8。我通过在记事本++中运行目录中的所有文件(来自没有文件搞乱的计算机)的python脚本来解决这个问题。

我找到了原始脚本here

记事本++ python脚本的副本:

import os;
import sys;
filePathSrc="C:\\Temp\\UTF8"
for root, dirs, files in os.walk(filePathSrc):
    for fn in files:
      if fn[-4:] != '.jar' and fn[-5:] != '.ear' and fn[-4:] != '.gif' and fn[-4:] != '.jpg' and fn[-5:] != '.jpeg' and fn[-4:] != '.xls' and fn[-4:] != '.GIF' and fn[-4:] != '.JPG' and fn[-5:] != '.JPEG' and fn[-4:] != '.XLS' and fn[-4:] != '.PNG' and fn[-4:] != '.png' and fn[-4:] != '.cab' and fn[-4:] != '.CAB' and fn[-4:] != '.ico':
        notepad.open(root + "\\" + fn)
        console.write(root + "\\" + fn + "\r\n")
        notepad.runMenuCommand("Encoding", "Convert to UTF-8 without BOM")
        notepad.save()
        notepad.close()