为什么Git将这个文本文件视为二进制文件?

209

我想知道为什么 Git 告诉我这个?

$ git diff MyFile.txt
diff --git a/MyFile.txt b/MyFile.txt
index d41a4f3..15dcfa2 100644
Binary files a/MyFile.txt and b/MyFile.txt differ

它们不是文本文件吗?

我已经检查了.gitattributes文件,但它是空的。为什么我会收到这个消息?我不能像以前那样获取差异了。

添加:

我注意到文件权限中有一个@,这是什么意思?这可能是原因吗?

$ls -all
drwxr-xr-x   5 nacho4d  staff    170 28 Jul 17:07 .
drwxr-xr-x  16 nacho4d  staff    544 28 Jul 16:39 ..
-rw-r--r--@  1 nacho4d  staff   6148 28 Jul 16:15 .DS_Store
-rw-r--r--@  1 nacho4d  staff    746 28 Jul 17:07 MyFile.txt
-rw-r--r--   1 nacho4d  staff  22538  5 Apr 16:18 OtherFile.txt

5
可能是一个UTF-8编码的文件。 - Marnix van Valen
1
它应该是UTF16小端LF。 - nacho4d
2
在Mac OS X上的ls手册中:如果文件或目录具有扩展属性,则由-l选项打印的权限字段后面跟随一个@字符。使用-@选项查看这些扩展属性。 - adl
6
很奇怪,因为Git根本不应该知道有任何扩展属性。如果你能够重现这个问题,最好在Git邮件列表上提出来。按照vger.kernel.org列表的惯例,您不必订阅即可发布(人们会将答案CC给您),而且还应该避免订阅git@vger.kernel.org列表,因为邮件量相当大。 - Jan Hudec
可能是重复的问题:为什么Git认为我的.cs文件是二进制的? - Nick Grealy
显示剩余4条评论
17个回答

109

这意味着当git检查文件的实际内容时(它不知道任何给定扩展名不是二进制文件-如果您想显式告诉它,可以使用属性文件-请参见man页面)。

检查文件内容后,发现其中有些内容不是基本ASCII字符。由于它是UTF16格式,所以我希望它会有'奇怪'的字符,因此认为它是二进制文件。

如果文件具有国际化(i18n)或扩展字符格式,则可以告诉git的方法有很多。我对设置的确切方法不够熟悉,你可能需要阅读一下全面手册;-)

编辑:快速搜索SO找到can-i-make-git-recognize-a-utf-16-file-as-text,这应该会给你一些线索。


11
你的观点差不多但并非完全没有错。Git确实检查了实际文件并发现了一些“奇怪”的字符。但是,它并不认为UTF-16是文本文件。因为文本被定义为基于ASCII的(内置的diff仅能提供可用的结果),而UTF-16则不是。是的,有一种方法可以告诉git对于指定模式的文件使用特殊的差异处理方式(使用“.gitattributes”)。 - Jan Hudec
2
我应该补充一下,“有趣的字符”实际上指的是零字节。 - Jan Hudec
6
我们都是正确的,但是从不同的角度来看。我们都说“Git检查内容以确定其类型。” 我们都说,为了让git知道应该将其视为UTF16,用户需要通过.gitattributes等告诉git。 - Philip Oakley
8
在你的观点中,所有文件都是二进制的。 - stolsvik
4
@stolosvik和JanH,UTF-8包含基本的0-127 ASCII字符以及所有其他Unicode字符,而不需要使用空(00h)字节来表示除"NUL"字符('C'字符串终止符)以外的任何内容。因此,Git的文本定义是,该内容(前1k字节)在UTF-8编码时不应具有空字节。您可以尝试访问https://dev59.com/l3E95IYBdhLWcg3wmvCh了解更多信息。我的原始评论是指当将UTF-16编码数据视为字节对时,ASCII代码点的高字节将为00的情况。 - Philip Oakley
显示剩余7条评论

54

如果您没有设置文件类型,Git 会尝试自动确定文件类型,如果文件包含非常长的行和一些 宽字符(例如 Unicode),则会将其视为二进制文件。使用.gitattributes文件,您可以定义Git如何解释该文件。手动设置 diff 属性可让Git将文件内容解释为文本,并执行常规的差异比较。

只需将.gitattributes添加到您的代码库根目录并将diff属性设置到路径或文件中即可。以下是一个示例:

src/Acme/DemoBundle/Resources/public/js/i18n/* diff
doc/Help/NothingToSay.yml                      diff
*.css                                          diff

如果您想检查文件上是否设置了属性,可以借助git check-attr来完成。

git check-attr --all -- src/my_file.txt

有关Git属性的另一个不错的参考资料可以在这里找到。


4
这很有帮助,但实际上是错误的--正确的属性是 diff,而不是 texttext 属性并不是告诉 git 使用文本进行比较,而是控制如何处理行尾(标准化为 LF)。有关更多详细信息,请参见您链接到的 .gitattributes。 - ErikE
感谢 @ErikE。根据您的评论和 Git 文档,我已更新了我的帖子。 - stollr
5
此外,您可以设置所需执行的差异类型。例如,如果是XML文件,则可以使用diff=xml而不是仅使用diff - Sandy Chapman
1
check-attr的反义词是什么 - 有set-attr吗?我最初意外将文件保存为UTF-16,然后提交和推送它,现在BitBucket仍将其视为UTF-16,即使重新保存为UTF-8,再次提交和推送它。这基本上使我的拉取请求无法阅读,因为审阅者需要单击每个单独的注释以添加审查注释。 - John Zabroski

34

我遇到了这个问题,即Git GUI和SourceTree将Java/JS文件视为二进制文件,因此不会显示差异。

.git/info中创建一个名为attributes的文件,并使用以下内容解决了这个问题:

*.java diff
*.js diff
*.pl diff
*.txt diff
*.ts diff
*.html diff
*.sh diff
*.xml diff
如果你希望这适用于所有存储库,则可以在$ HOME/.config/git/attributes中添加文件attributes

1
还要注意 <project-root>/.gitattributes 文件,它使更改对所有贡献者都生效,并且仅适用于相关项目。 - jpaugh
2
添加 * diff 对我很有帮助:它显示了所有类型文件的差异。但是你的解决方案更好,因为避免了在大型二进制文件中显示不必要的差异。 - Boolean_Type
耶!这很有帮助! - WildCat

21

如果你在文本文件中有一行超长的字符串,Git 将会确定它是二进制文件。我将一个超长的字符串分解成几个源代码行后,突然间这个文件从“二进制”变成了一个我可以在 SmartGit 中查看的文本文件。

因此,在编辑器中不要一直向右键入太远而不使用“Enter”键,否则后来 Git 将认为你创建了一个二进制文件。


1
这是正确的信息。我正在尝试控制一个非常大的 MySQL 转储(.sql文件)的 diffs,但是 git 把它当作二进制文件处理,即使它只有 ASCII/UTF8 数据。原因是行很长(插入值 (one),(two),(three),(...),(3 million...),每次提交,git 存储库的大小不增加 1.7gb,只增加约 350mb。也许,git 在保存之前对"二进制"文件进行了压缩。 - Alexandre T.
@AlexandreT。Git确实压缩文件blob(使用GZip,如果我没记错的话)。 - jpaugh

16

我在使用新的编辑器编辑文件后遇到了同样的问题。事实证明,新的编辑器使用的编码(Unicode)不同于我的旧编辑器(UTF-8)。因此,我只需告诉我的新编辑器使用UTF-8保存我的文件,然后git再次正确显示我的更改,并且没有将其视为二进制文件。

我认为问题很简单,即git不知道如何比较不同编码类型的文件。因此,您使用的编码类型并不重要,只要保持一致即可。

虽然我没有测试过,但我肯定如果我只是使用新的Unicode编码提交了我的文件,下次修改该文件时它就会正确地显示更改,而不会将其检测为二进制文件,因为这时它将比较两个Unicode编码的文件而不是一个UTF-8文件和一个Unicode文件。

您可以使用像Notepad++这样的应用程序轻松查看和更改文本文件的编码类型;在Notepad++中打开文件并使用工具栏中的编码菜单即可。


3
Unicode不是一种编码方式,而是一个字符集,UTF-8是它的一种编码方式,即将Unicode代码点进行编码的方式。 - phuclv
1
这并没有解决问题,只是避免了它。问题在于git或其diff工具不能正确识别文本文件,或者不容易允许用户覆盖其行为。 - Preza8

13

这也(至少在Windows上)是由采用UTF-8带BOM编码的文本文件引起的。将编码更改为普通的UTF-8立即使Git将该文件视为类型=文本。


我有两个文件,Notepad++将它们识别为UTF-8带BOM编码。但是SourceTree/git将其中一个识别为二进制文件,另一个则识别为文本文件。除此之外,我没有任何确切的说法,只能说这并不是完全准确的陈述。 - goug
2
在我的情况下,我的文件是带有BOM的UTF-16编码,使用Notepad++将编码更改为普通的UTF-8解决了问题,同时我还需要在.gitattributes文件中手动添加以下内容:*.extension diff - Bud Damyanov

7
我们遇到了这样的情况,每当我们尝试更改一个 .html 文件时,它被视为二进制文件。这样就无法查看差异了。说实话,我没有检查这里的所有解决方案,但对我们起作用的是以下方法:
  1. 删除该文件(实际上将其移动到我的桌面上),并提交 git 删除操作。Git会显示“已删除文件,模式为100644(常规),二进制文件不同”。
  2. 重新添加该文件(实际上是从我的桌面移回项目中)。Git会显示“新文件,模式为100644(常规),1个块,135次插入,0次删除”。该文件现在被添加为普通文本文件。
从现在开始,我所做的任何文件更改都被视为普通文本差异。您也可以压缩这些提交(1、2和3是您实际进行的更改),但我更喜欢能够在未来看到我所做的事情。压缩 1 和 2 将显示二进制更改。

类似于从VS上传了一个或两个(成功编译的)cpp文件。这使得Github的比较界面看起来很荒谬。在这样的交互中,你不会希望成为钟摆上的一只苍蝇,VS一方说这是Github的问题,而Github则说这是VS的问题。:( - Laurie Stearn

7

我曾经遇到过同样的问题。当我在谷歌上搜索解决方案时,我找到了这个帖子,但是我仍然没有发现任何线索。但是通过研究,我认为我已经找到了原因,下面的示例将清楚地解释我的线索。

    echo "new text" > new.txt
    git add new.txt
    git commit -m "dummy"

目前,文件new.txt被视为文本文件。

    echo -e "newer text\000" > new.txt
    git diff

您将获得此结果。

diff --git a/new.txt b/new.txt
index fa49b07..410428c 100644
Binary files a/new.txt and b/new.txt differ

并尝试这个

git diff -a

您将会得到以下内容。
    diff --git a/new.txt b/new.txt
    index fa49b07..9664e3f 100644
    --- a/new.txt
    +++ b/new.txt
    @@ -1 +1 @@
    -new file
    +newer text^@

6
尝试使用file查看编码细节 (参考链接):
cd directory/of/interest
file *

它会产生如下有用的输出:
$ file *
CR6Series_stats resaved.dat: ASCII text, with very long lines, with CRLF line terminators
CR6Series_stats utf8.dat:    UTF-8 Unicode (with BOM) text, with very long lines, with CRLF line terminators
CR6Series_stats.dat:         ASCII text, with very long lines, with CRLF line terminators
readme.md:                   ASCII text, with CRLF line terminators

8
“file”不是Git命令,它是一个完全独立的工具,在Windows上与Git一起打包。是否有文档显示这就是Git用于检测二进制文件的工具? - Max
1
是的,file 是一个 Linux 工具,但它与 Git 一起打包在 C:\Program Files\git\usr\bin 中。 - patricktokeeffe

2

我曾经遇到这样一个情况,.gitignore 文件中有意包含了双重的 \r (回车)序列。

这个文件被 Git 识别为二进制文件。添加一个 .gitattributes 文件可以解决这个问题。

# .gitattributes file
.gitignore diff

1
工作正常。我还有一个双\r来忽略一些操作系统的“Icon\r\r”文件。很高兴知道原因以及解决方法。 - hsandt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接