我可以让 git 将 UTF-16 文件识别为文本吗?

Posted

技术标签:

【中文标题】我可以让 git 将 UTF-16 文件识别为文本吗?【英文标题】:Can I make git recognize a UTF-16 file as text? 【发布时间】:2010-10-21 03:32:28 【问题描述】:

我正在 git 中跟踪一个 Virtual PC 虚拟机文件 (*.vmc),在进行更改后,git 将该文件识别为二进制文件,并且不会为我区分它。我发现文件是用 UTF-16 编码的。

可以教 git 识别这个文件是文本并适当地处理它吗?

我在 Cygwin 下使用 git,core.autocrlf 设置为 false。如有必要,我可以在 UNIX 下使用 mSysGit 或 git。

【问题讨论】:

【参考方案1】:

我已经为这个问题苦苦挣扎了一段时间,刚刚发现(对我来说)一个完美的解决方案:

$ git config --global diff.tool vimdiff      # or merge.tool to get merging too!
$ git difftool commit1 commit2

git difftool 采用与 git diff 相同的参数,但运行您选择的差异程序而不是内置的 GNU diff。所以选择一个多字节感知差异(在我的例子中,vim 处于差异模式)并使用 git difftool 而不是 git diff

发现“difftool”太长而无法输入?没问题:

$ git config --global alias.dt difftool
$ git dt commit1 commit2

Git 摇滚。

【讨论】:

不是一个完美的解决方案(宁愿有一个滚动的统一差异),但是,考虑到选择和我不愿意找到新的东西来安装,这是一个较小的邪恶。 “vimdiff”,它是! (是的,vim ...和git) 这是否也适用于仅暂存和提交大块 UTF16 文件? 我使用Beyond Compare 作为差异和合并工具。从 .gitconfig
 [difftool "bc3"] path = c:/Program Files (x86)/Beyond Compare 3/bcomp.exe [mergetool "bc3"] path = c:/Program Files (x86)/ Beyond Compare 3/bcomp.exe 
@Tom Wilson 抱歉无法通过缩进 4 个空格来格式化代码块!? 我有 git 的基本知识,但不确定它如何处理文件更改。这总是作为二进制文件还是文本(ASCII)有特殊处理/检测变化?【参考方案2】:

有一个非常简单的解决方案可以在 Unices 上开箱即用。

例如,Apple 的 .strings 文件只是:

    在存储库的根目录中创建一个.gitattributes 文件:

     *.strings diff=localizablestrings
    

    将以下内容添加到您的 ~/.gitconfig 文件中:

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

来源:Diff .strings files in Git(和 2010 年的 older post)。

【讨论】:

我这样做了,但 git 拒绝在此之后运行。我得到的错误是“/Users/myusername/.gitconfig 中的错误配置文件第 4 行”。我使用“git config --global --edit”打开我的 gitconfig 文件。有趣的是,如果我删除添加的行一切正常。有什么线索吗? 如果您复制/粘贴,我将猜测智能引号。我编辑了答案来解决这个问题。 这就像一个魅力,为了简单起见和更好的集成,它应该是公认的答案。我不明白“使用其他工具”如何成为“我可以让 git 将 UTF-16 文件识别为文本吗?”的答案? @itMaxence 严格来说,iconv 是“另一个工具”,就像 Vim 或 Beyond Compare 一样(不是 git 套件的一部分)。 感谢您的回答,它运行良好。我想知道的是,这种变化是否有可能影响这个 repo 的其他开发人员?是的,.gitattributes 已提交,但添加到 ~/.gitconfig 的行未提交。这些行是否也可以添加到.gitattributes,或者有更好的方法吗?【参考方案3】:

您是否尝试过将您的.gitattributes 设置为将其视为文本文件?

例如:

*.vmc diff

更多详情请访问http://www.git-scm.com/docs/gitattributes.html

【讨论】:

这可行,但为了正确起见,请注意这设置了 两个 属性:setdiff... 这个解决方案是我唯一可以接受的。根据@OK 评论,“set”在这里无关紧要,只需要 *.vmc diff*.sql diff 等来设置指定路径的 'diff' 属性。 (我无法编辑答案)。但是有两个注意事项:差异显示在每个字符之间有一个空格,并且无法为那些有问题的文件“暂存大块”或“丢弃大块”。【参考方案4】:

默认情况下,git 似乎不适用于 UTF-16;对于这样的文件,您必须确保没有对其进行任何CRLF 处理,但是您希望diffmerge 作为普通文本文件工作(这忽略了您的终端/编辑器是否可以处理UTF-16)。

但是查看.gitattributes manpage,这里是binary的自定义属性:

[attr]binary -diff -crlf

所以在我看来,您可以在*** .gitattributes 中为 utf16 定义一个自定义属性(请注意,我在此处添加了合并以确保将其视为文本):

[attr]utf16 diff merge -crlf

从那里您可以在任何.gitattributes 文件中指定类似的内容:

*.vmc utf16

还请注意,即使git 认为它是二进制文件,您仍然应该能够diff 文件:

git diff --text

编辑

This answer 基本上说带有 UTF-16 甚至 UTF-8 的 GNU diff 不能很好地工作。如果您想让git 使用不同的工具来查看差异(通过--ext-diff),那么答案建议Guiffy。

但您可能需要的只是diff 一个仅包含 ASCII 字符的 UTF-16 文件。使其工作的一种方法是使用 --ext-diff 和以下 shell 脚本:

#!/bin/bash
diff <(iconv -f utf-16 -t utf-8 "$1") <(iconv -f utf-16 -t utf-8 "$2")

请注意,转换为 UTF-8 也可能适用于合并,您只需确保双向完成即可。

至于查看 UTF-16 文件的差异时到终端的输出:

尝试这样区分会导致 二进制垃圾喷到屏幕上。 如果 git 使用 GNU diff,它会 似乎 GNU diff 不是 识别 unicode。

GNU diff 并不真正关心 unicode,因此当您使用 diff --text 时,它只会区分并输出文本。问题是您使用的终端无法处理发出的 UTF-16(结合 ASCII 字符的差异标记)。

【讨论】:

尝试像这样区分会导致二进制垃圾喷到屏幕上。如果 git 使用 GNU diff,那么 GNU diff 似乎不支持 unicode。 GNU diff 并不真正关心 unicode,因此当您使用 diff --text 时,它只会区分并输出文本。问题是您使用的终端无法处理发出的 UTF-16(结合 ASCII 字符的差异标记)。 @jared-oberhaus - 有没有办法只为某些类型的文件(即给定的扩展名)触发这个脚本? 考虑在您的答案中添加一个备注,即此更改不会自动执行任何操作。 git add -- renormalize . 命令应在现有存储库上执行。【参考方案5】:

git 最近开始理解 utf16 等编码。 查看gitattributes 文档,搜索working-tree-encoding

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

如果(比如说)文件是 UTF-16,在 Windows 机器上没有 BOM,那么添加到您的 .gitattributes 文件中

*.vmc text working-tree-encoding=UTF-16LE eol=CRLF

如果在 *nix 上使用 UTF-16(带 bom):

*.vmc text working-tree-encoding=UTF-16-BOM eol=LF

(将*.vmc 替换为*.whatever 以获取您需要处理的whatever 类型的文件)

请参阅:Support working-tree-encoding "UTF-16LE-BOM"。


稍后添加

跟随@Hackslash,你可能会发现这是不够的

 *.vmc text working-tree... 

要获得你需要的漂亮的文本差异

 *.vmc diff working-tree...

两者都放效果也很好

 *.vmc text diff working-tree... 

但可以说是

冗余 — eol=... 暗示 text 冗长 - 一个大型项目很容易拥有数十种不同的文本文件类型

问题

Git 有一个宏属性binary,这意味着-text -diff。相反的+text +diff 内置不可用,但 git 提供了用于合成它的工具(我认为!)

解决方案

Git 允许定义新的宏属性。

我建议您在 .gitattributes 文件的顶部

 [attr]textfile text diff

然后对于所有需要文本和差异的路径

 path textfile working-tree-encoding= eol=...

请注意,在大多数情况下,我们希望使用默认编码 (utf-8) 和默认 eol (native),因此可能会被删除。

大多数线条应该是这样的

*.c textfile
*.py textfile
Etc

为什么不直接使用 diff?

实用:在大多数情况下,我们需要原生 eol。这意味着没有 eol=... 。所以text 不会被暗示,需要明确说明。

概念:文本与二进制是根本区别。 eol、编码、差异等只是它的一些方面。

免责声明

由于我们生活在一个奇怪的时代,我没有一台带有当前工作 git 的机器。所以我目前无法检查最新添加的内容。如果有人发现有问题,我会修改/删除。

【讨论】:

要让我的 UTF-16LE-BOM 文件正常工作,我必须使用 *.vmc diff working-tree-encoding=UTF-16LE-BOM eol=CRLF @HackSlash :感谢您的提醒。我猜你是在说 text 单独你没有得到很好的文本差异?您能否检查一下 both textdiff 是否一切正常?在这种情况下,我会提出不同的建议 正确,text 单独导致二进制比较。我可以做difftext diff 并且它有效。我需要添加 -BOM 只是因为我的文件有一个 BOM,YMMV。 @HackSlash 我已经合并了你的发现。如果你能去看看就太好了! 谢谢@Rusi,对我来说很有意义。【参考方案6】:

解决方法是过滤cmd.exe /c "type %1"。 cmd 的 type 内置函数将进行转换,因此您可以使用 git diff 的 textconv 功能来启用 UTF-16 文件的文本差异(也应该与 UTF-8 一起使用,尽管未经测试)。

引用 gitattributes 手册页:


执行二进制文件的文本差异

有时希望查看某些二进制文件的文本转换版本的差异。例如,可以将文字处理器文档转换为 ASCII 文本表示,并显示文本的差异。尽管这种转换会丢失一些信息,但生成的差异对于人类查看很有用(但不能直接应用)。

textconv 配置选项用于定义执行此类转换的程序。该程序应采用单个参数,即要转换的文件的名称,并在标准输出上生成结果文本。

例如,要显示文件的 exif 信息而不是二进制信息的差异(假设您安装了 exif 工具),请将以下部分添加到您的 $GIT_DIR/config 文件(或 $HOME/.gitconfig 文件)中:

[diff "jpg"]
        textconv = exif

mingw32 的解决方案,cygwin 粉丝可能不得不改变方法。问题在于传递文件名以转换为 cmd.exe - 它将使用正斜杠,并且 cmd 假定反斜杠目录分隔符。

第 1 步:

创建将转换为标准输出的单参数脚本。 c:\path\to\some\script.sh:

#!/bin/bash
SED='s/\//\\\\\\\\/g'
FILE=\`echo $1 | sed -e "$SED"\`
cmd.exe /c "type $FILE"

第 2 步:

设置 git 以便能够使用脚本文件。在你的 git 配置中(~/.gitconfig.git/config 或查看 man git-config),输入:

[diff "cmdtype"]
textconv = c:/path/to/some/script.sh

第三步:

通过使用 .gitattributes 文件指出要应用此工作的文件(请参阅 man gitattributes(5)):

*vmc diff=cmdtype

然后在您的文件上使用git diff

【讨论】:

几乎和 Tony Kuneck 一样,但没有 "c:/path/to/some/script.sh" entropy.ch/blog/Developer/2010/04/15/… 我在上面显示的用于 Windows 的 Git 脚本中遇到了一些问题,但我发现以下内容很好,并且还可以处理路径中的空格:cmd //c type "$1//\//\\" 这无需创建脚本文件即可工作:textconv = powershell -NoProfile -Command \"&amp; Get-Content \\$args[0]\"【参考方案7】:

我编写了一个小型 git-diff 驱动程序 to-utf8,它应该可以轻松区分任何非 ASCII/UTF-8 编码的文件。您可以使用此处的说明安装它:https://github.com/chaitanyagupta/gitutils#to-utf8(to-utf8 脚本在同一个 repo 中可用)。

请注意,此脚本需要在系统上同时提供 fileiconv 命令。

【讨论】:

【参考方案8】:

最近在 Windows 上遇到了这个问题,Windows 的 git 附带的 dos2unixunix2dos 垃圾箱解决了问题。默认情况下,它们位于C:\Program Files\Git\usr\bin\请注意,仅当您的文件不需要需要为 UTF-16 时,这才有效。例如,有人不小心将 python 文件编码为 UTF-16,而实际上它不需要需要(在我的情况下)。

PS C:\Users\xxx> dos2unix my_file.py
dos2unix: converting UTF-16LE file my_file.py to ANSI_X3.4-1968 Unix format...

PS C:\Users\xxx> unix2dos my_file.py
unix2dos: converting UTF-16LE file my_file.py to ANSI_X3.4-1968 DOS format...

【讨论】:

【参考方案9】:

如其他答案中所述,git diff 不会将 UTF-16 文件作为文本处理,这使得它们在 Atlassian SourceTree 中无法查看。如果文件名/或后缀已知,则以下修复将使这些文件在 SourceTree 下正常可见和可比较。

如果 UTF-16 文件的文件后缀已知(例如 *.uni),则所有具有该后缀的文件都可以与 UTF-16 到 UTF-8 转换器相关联,并进行以下两个更改:

    使用以下行在存储库的根目录中创建或修改 .gitattributes 文件:

     *.uni diff=utf16
    

    然后修改用户主目录(C:\Users\yourusername\.gitconfig)中的.gitconfig文件,内容如下:

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

这两项更改应立即生效,无需将存储库重新加载到 SourceTree。它将文本转换应用于所有 *.uni 文件,使它们像其他文本文件一样可见和可比较。如果其他文件需要这种转换,您可以在 .gitattributes 文件中添加额外的行。 (如果指定的文件不是 UTF-16,您将得到该文件的不可读结果。)

请注意,此答案是对 Tony Kuneck 答案的简化重写。

【讨论】:

以上是关于我可以让 git 将 UTF-16 文件识别为文本吗?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以让表格将我的 REGEXEXTRACT 结果识别为日期?

如何让 Visual Studio 2010 将文件扩展名识别为 C#/ASPX/C/C++ 文件?

为啥 Qt::mightBeRichText() 不能将 HTML 表格标签识别为富文本?

如何将文件内容识别为 ASCII 或二进制

如何让 iOS 应用识别为 AudioUnit?

如何使 <textarea> 仅将撇号识别为文本? [关闭]