我可以让 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。
【讨论】:
这可行,但为了正确起见,请注意这设置了 两个 属性:set
和 diff
...
这个解决方案是我唯一可以接受的。根据@OK 评论,“set”在这里无关紧要,只需要 *.vmc diff
、 *.sql diff
等来设置指定路径的 'diff' 属性。 (我无法编辑答案)。但是有两个注意事项:差异显示在每个字符之间有一个空格,并且无法为那些有问题的文件“暂存大块”或“丢弃大块”。【参考方案4】:
默认情况下,git
似乎不适用于 UTF-16;对于这样的文件,您必须确保没有对其进行任何CRLF
处理,但是您希望diff
和merge
作为普通文本文件工作(这忽略了您的终端/编辑器是否可以处理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 text
和 diff
是否一切正常?在这种情况下,我会提出不同的建议
正确,text
单独导致二进制比较。我可以做diff
或text 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 \"& Get-Content \\$args[0]\"
【参考方案7】:
我编写了一个小型 git-diff 驱动程序 to-utf8
,它应该可以轻松区分任何非 ASCII/UTF-8 编码的文件。您可以使用此处的说明安装它:https://github.com/chaitanyagupta/gitutils#to-utf8(to-utf8
脚本在同一个 repo 中可用)。
请注意,此脚本需要在系统上同时提供 file
和 iconv
命令。
【讨论】:
【参考方案8】:最近在 Windows 上遇到了这个问题,Windows 的 git 附带的 dos2unix
和 unix2dos
垃圾箱解决了问题。默认情况下,它们位于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++ 文件?