Git 索引和工作树 EOL 值与 .gitattributes 中设置的值不匹配

Posted

技术标签:

【中文标题】Git 索引和工作树 EOL 值与 .gitattributes 中设置的值不匹配【英文标题】:Git index and working-tree EOL values do not match those set in .gitattributes 【发布时间】:2022-01-19 16:34:23 【问题描述】:

我在 Windows 上,通过 git bash CLI 使用 git。我有一个使用UTF-8 编码的.java 文件,我从旧的svn 服务器导入到git。我的同事也在 Windows 上,但使用 Eclipse IDE 中的 git 客户端,经常抱怨这个特定文件在结帐时有空格更改(即没有任何手动修改)。我相信问题与git 将文件视为二进制文件有关,但我不确定。 ls-files --eol 的输出为:

$ git ls-files --eol -- src/Props.java 
i/-text w/-text attr/text=auto eol=crlf src/Props.java

以上似乎表明git 认为文件的存储版本是二进制的(i/-text w/-text 位),但也识别存储库中的属性设置(attr/text=auto eol=crlf 位)。这怎么可能?有没有办法修复它,以便存储在索引/工作树中的是crlf?我是否正在寻找正确的地方来解决这个问题?

【问题讨论】:

text=auto 告诉 Git 猜测。您看到的输出表明 Git 确实猜到了,它的猜测是“这些是二进制文件”。猜测是不可配置的,但您可以强制 Git 相信文件是文本(或不是文本),text-text.gitattributes 中。但是,如果 Git 猜测该文件是二进制文件,则它可能不是文本(例如,它可能存储为 UTF16,对于 Git 来说不是文本,如果将其视为文本并进行 EOL 转换,它可能会损坏 Git )。 很难判断文件是否已被某些错误的 Windows 软件从 UTF-8 转换为 UTF-16-LE,因为检查文件的其他软件会发现它是 UTF-16 -LE,将其转换为 UTF-8,然后检查它并自豪地宣布该文件现在是 UTF-8。当你的工具对你撒谎时——许多现代工具都会撒谎——事情就会变得困难。 好的,假设我真的想告诉git 将此文件视为text 并以crlf 行结尾。我需要某种方法来确定编码是什么,然后用某种方法替换那些欺骗git 相信它是二进制的字符,对吧?关于如何做这两件事的想法?我完全控制了这个文件,它不需要是 UTF-16(或任何其他特殊编码),所以手动修改文件不是问题。 (FWIW,Notepad++ 认为它是 UTF-8 而file -i Props.java 给出了text/x-java; charset=us-ascii 如果文件真的是文本,奇怪的是 Git 会猜错,但只需将 .gitattributes 中的 text=auto 更改为 text 就会告诉 Git 文件是文本。 (更改或添加的内容取决于.gitattributes 文件中已有的内容:如果您有* text=auto,您可以在其下方添加*.java text 以覆盖.java 文件,例如。添加eol=crlf 以制作Git 在从存储库到工作树的途中将 \n 转为 \r\n,并且仅在从工作树到存储库的途中将 \r\n 转为 \n,如果这是你想要的。) 请注意,不同的eol=设置指导Git在退出时是否做\n => \r\n,以及是否做\r\n => \n方式。设置是:两者都做,或者只做输入到存储库端(\r\n => \n)的转换。这些是唯一可用的转换选项:例如,在进入存储库选项的过程中没有 \n 到 \r\n。 -text(或binary)表示放手text表示放手eol=设置转换。 【参考方案1】:

当你使用text=auto 时,它要求 Git 查看文件,如果它在文件开头的一定字节数内找到任何 NUL 字节,它认为文件是二进制的,否则它认为文件为文本。通常这种方法效果很好,但有时效果不佳。

如果您使用的是 UTF-16,则不是这样的地方之一。这在 Windows 上非常流行,并且仍在某些编程语言中使用,但通常被认为在其他任何地方都已过时,转而支持 UTF-8。带有 ASCII 范围字符(即大多数英文文本和编程语言)的 UTF-16 将包含 NUL 字节,这将使 Git 认为文本是二进制的。请注意,对于 Git,任何 LF 和 CR 字符由它们在 US-ASCII 中的值以外的值(如 UTF-16 中的两字节序列)表示的任何东西都是二进制的,并且强制在那里进行文本转换会破坏事情。

如果这些文件确实是某种与 ASCII 兼容的编码(例如,UTF-8)并且您想强制 Git 将这些文件检测为文本,您可以修改您的 .gitattributes 文件:

*.java text

如果您还希望 EOL 为 CRLF,请执行以下操作:

*.java text eol=crlf

任何时候你将 Git 设置为执行文本转换,它都会将 LF 结尾存储在存储库中。无论工作树的结尾如何,这都是您想要的,因为它可以使git diff 之类的工具更好地工作,而无需担心您的尾随空格(CR)。设置 eol=crlf 只是强制它成为工作树中的 CRLF,这将是大多数程序工作所需要的。

请注意,一般来说,建议只是让人们使用他们喜欢的行尾,并且仅在需要功能时设置行尾,例如用于 shell 脚本的 LF 和用于批处理脚本的 CRLF,其中的替代方法是它只是行不通。但是,在某些环境中工具会导致问题,因此您可能希望在这些环境中也设置配置。

【讨论】:

以上是关于Git 索引和工作树 EOL 值与 .gitattributes 中设置的值不匹配的主要内容,如果未能解决你的问题,请参考以下文章

GIT版本管理

git status 在内部的工作方式与 git diff 在显示未跟踪文件方面有何不同?

如何防止 Git/Gitlab 修改 EOL 字符?

Git命令在不修改工作树的情况下保存存储?

禁用 git EOL 转换

常用git命令大全