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

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

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

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

【问题讨论】:

    标签: git unicode character-encoding diff utf-16


    【解决方案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 文件只是:

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

       *.strings diff=localizablestrings
      
    2. 将以下内容添加到您的 ~/.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-utf8to-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 转换器相关联,并进行以下两个更改:

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

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

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

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

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

        【讨论】:

          猜你喜欢
          • 2014-02-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-09-18
          • 2016-01-01
          • 1970-01-01
          • 2023-03-16
          • 1970-01-01
          相关资源
          最近更新 更多