Git diff在.gitattributes中设置EOL为CRLF时仍将行尾视为LF

9
当我还原一个以Windows换行符结尾的文件时,如果.gitattributes将EOL定义为CRLF,则git会认为换行符已更改为LR,即使十六进制编辑器显示为CRLF也是如此。只有在.gitattributes定义EOL字符时才会发生这种情况。
没有.gitattributes:
这个工作正常。
这是我的文件Web.config的原始版本。最后两个字符是0d 0a(CR LF):
00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e0d 0a              "utf-8"?>..     

我在第一行的末尾添加了一个空格字符,20 0d 0a

00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e20 0d0a            "utf-8"?> ..    

Git diff 显示空格字符:

diff --git a/Web.config b/Web.config
index bc3c3c3..6215f5e 100644
--- a/Web.config
+++ b/Web.config
@@ -1,4 +1,4 @@
<U+FEFF><?xml version="1.0" encoding="utf-8"?>{+ +}

撤销文件并且所有更改都将消失:

$ git checkout Web.config

$ git status Web.config
On branch develop
Your branch is up-to-date with 'origin/develop'.

nothing to commit, working directory clean

使用 .gitattributes

这种方法不起作用。

在 .gitattributes 中定义 CRLF:

*.config eol=crlf

在第一行末尾添加空格字符:

00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e20 0d0a            "utf-8"?> ..    

Git diff 显示出了空格,但是CR(回车符)却没有(^M):

diff --git a/Web.config b/Web.config
index bc3c3c3..9d3bc53 100644
--- a/Web.config
+++ b/Web.config
@@ -1,248 +1,248 @@
<U+FEFF><?xml version="1.0" encoding="utf-8"?>[-^M-]{+ +}

撤销文件:

$ git checkout Web.config

$ git status Web.config
On branch develop
Your branch is up-to-date with 'origin/develop'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   Web.config

no changes added to commit (use "git add" and/or "git commit -a")

Git认为CR已从所有行中删除:

$ git diff --word-diff-regex=. Web.config
diff --git a/Web.config b/Web.config
index bc3c3c3..094d1d5 100644
--- a/Web.config
+++ b/Web.config
@@ -1,248 +1,248 @@
<U+FEFF><?xml version="1.0" encoding="utf-8"?>[-^M-]
<!--[-^M-]
  For more information on how to configure your ASP.NET application, please visit[-^M-]
  http://go.microsoft.com/fwlink/?LinkId=152368[-^M-]
  -->[-^M-]
<configuration>[-^M-]

但是在十六进制编辑器中情况并非如此:
00000000: efbb bf3c 3f78 6d6c 2076 6572 7369 6f6e  ...<?xml version
00000010: 3d22 312e 3022 2065 6e63 6f64 696e 673d  ="1.0" encoding=
00000020: 2275 7466 2d38 223f 3e0d 0a3c 212d 2d0d  "utf-8"?>..<!--.
00000030: 0a20 2046 6f72 206d 6f72 6520 696e 666f  .  For more info
00000040: 726d 6174 696f 6e20 6f6e 2068 6f77 2074  rmation on how t
00000050: 6f20 636f 6e66 6967 7572 6520 796f 7572  o configure your
00000060: 2041 5350 2e4e 4554 2061 7070 6c69 6361   ASP.NET applica
00000070: 7469 6f6e 2c20 706c 6561 7365 2076 6973  tion, please vis
00000080: 6974 0d0a 2020 6874 7470 3a2f 2f67 6f2e  it..  http://go.
00000090: 6d69 6372 6f73 6f66 742e 636f 6d2f 6677  microsoft.com/fw
000000a0: 6c69 6e6b 2f3f 4c69 6e6b 4964 3d31 3532  link/?LinkId=152
000000b0: 3336 380d 0a20 202d 2d3e 0d0a 3c63 6f6e  368..  -->..<con
000000c0: 6669 6775 7261 7469 6f6e 3e0d 0a20 203c  figuration>..  <

这里发生了什么事情,我该如何使它正常工作?
4个回答

5
关键在于,无论您使用text=autotext eol=crlf还是text eol=lf,git都会执行以下操作:
  1. 将行尾转换为LF格式并存储在仓库中(即在git commit时)
  2. 在从仓库复制到工作树时(即在git checkoutgit merge时),将行尾转换为您选择的格式
这可能有些出乎意料,但请记住git的起源来自Linux世界,并不是一个缺陷。从git文档中可以得出结论:“当规范化文本文件时,其行尾在仓库中被转换为LF格式”。
作为此项措施的推论,我发现当我加入一个现有项目并需要引入.gitattributes来规范行尾时,最好编写一个PowerShell脚本(或您偏爱的任何方法)来一次性规范所有文件中的行尾。这是为了避免由.gitattributes引入的行尾变化导致的持续混淆差异。
最后,如果有所帮助,我之前在GitHub上发布了一个沙盒以测试行尾设置:https://github.com/teamtam/git-line-endings

我不确定提交时是否将行结尾转换为LR。我的存储库中有一个文件具有混合的行结尾,大多数是LR,但是有几行是CRLF。如果我没有设置.gitattributes并删除和重新检出文件,则可以在十六进制编辑器中看到混合的行结尾;git diff不显示任何更改。如果我在.gitattributes中将行结尾设置为CRLF,然后删除文件并再次检出,则所有行都将转换为CRLF,但是git diff会显示最初为CRLF的行已更改(CR已删除)。它不会显示将LF更改为CRLF的行上的任何更改。 - Ryan Jenkin
当我提到“存储库”时,我指的是通常不会触及的内部git工作方式,即(隐藏的).git文件夹。因此,当您使用.gitattributes文本进行提交时,我的理解是它会在存储库中剥离CR,但您的工作树副本(即您实际使用的文件)保持不变。这意味着当您检出时,无论存储库中的内容是自动、crlf还是lf格式,都将被转换为您指定的格式。 - TeamTam
1
这实际上是最准确的答案。简而言之,如果您在.gitattributes中创建了一些CRLF文件text,则应将其转换为blob中的LF。 - George Sovetov

3

这是我在使用 git 时遇到的一个 bug,你可以向 git 团队报告:

您可以通过电子邮件地址 git@vger.kernel.org 将 Git 社区的问题或评论发送到邮件列表。Git 的 bug 报告应发送到此邮件列表。

https://git-scm.com/community

如果你正在使用 Windows,你也可以尝试在以下网址上提交 bug 报告:

  1. https://github.com/git-for-windows/git/issues

也许这个 bug 在最新版本的 git 2.10.2 中已经被修复了,你可以将你的 git 更新到最新版本(如果还没有):

  1. https://en.wikipedia.org/wiki/Git

我之前使用的是git 1.9.5版本,升级到了2.10.2版本,但问题仍然存在。 - Ryan Jenkin
你是在使用Windows吗?请在https://github.com/git-for-windows/git/issues上开一个工单,提供此页面上的相关信息。 - user

2

首先检查您的 git config core.autocrlf 值。

为了确保只应用您的 .gitattributes 指令,请确保输入以下内容:

git config --global core.autocrlf false

然后再次克隆你的Git仓库,看问题是否仍然存在。

系统、全局或本地配置中都没有设置core.autocrlf。我在全局中将其设置为false,然后完全删除了存储库并重新克隆。我在第一行末尾添加了空格字符并保存,但git diff显示每行都已删除其CR,即使我可以在十六进制编辑器中看到它,因此与原始问题相比没有任何变化。 - Ryan Jenkin
1
“.gitattributes” 优先于本地或全局配置。 - TeamTam

2

为了在Windows上使用Unix行结束符,我正在使用:

git config --global core.autocrlf input
git config --global core.eol lf

所以在您的情况下,您应该设置:

git config --global core.autocrlf input
git config --global core.eol crlf

你可以针对特定文件夹进行操作,而不是像我上面展示的那样全局操作。

是的,问题在于当我将行尾设置为CRLF时,git diff会认为任何原本具有CRLF行尾的文件都已被修改并删除了CR,即使我可以在十六进制编辑器中看到它,并告诉它应该是CRLF。 - Ryan Jenkin
“.gitattributes” 优先于本地或全局配置。 - TeamTam

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