【问题标题】:Why does git keep messing with my line endings?为什么 git 总是弄乱我的行尾?
【发布时间】:2016-08-22 10:13:55
【问题描述】:

我在 Windows 上,我禁用了 core.autocrlf

$ git config core.autocrlf; git config --global core.autocrlf
false
false

现在,我假设 git 不会弄乱任何行尾,但我的 repo 中的一些文件不断导致问题。

我刚刚克隆了一个新的 repo 副本,git 已经告诉我有未暂存的更改。当我git diff -R 时,我可以看到 CRLF 行结尾已添加到文件中:

diff --git b/nginx-1.11.1/contrib/geo2nginx.pl a/nginx-1.11.1/contrib/geo2nginx.pl
index bc8af46..29243ec 100644
--- b/nginx-1.11.1/contrib/geo2nginx.pl
+++ a/nginx-1.11.1/contrib/geo2nginx.pl
@@ -1,58 +1,58 @@
-#!/usr/bin/perl -w
-
-# (c) Andrei Nigmatulin, 2005
+#!/usr/bin/perl -w^M
+^M
+# (c) Andrei Nigmatulin, 2005^M

我不明白这些行尾是从哪里来的,但我也无法“恢复”这个更改。当我再次签出文件时,它仍然会被修改:

$ git checkout -f nginx-1.11.1/contrib/geo2nginx.pl
$ git status
On branch dev
Your branch is up-to-date with 'origin/dev'.
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:   nginx-1.11.1/contrib/geo2nginx.pl

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

这对我来说毫无意义,所以我只是在文件上运行dos2unix

$ dos2unix nginx-1.11.1/contrib/geo2nginx.pl
dos2unix: converting file nginx-1.11.1/contrib/geo2nginx.pl to Unix format...

现在肯定不应该有任何变化,对吧?但文件仍显示为在git status 中修改,git diff 仍会在工作副本中报告 CRLF 行结尾。

当我现在暂存并提交文件时,生成的文件将具有 LF 行结尾,即使差异显示为 CRLF 行结尾。

我没有全局 .gitattributesgit config --global core.attributesfile 不输出任何内容)。并且项目中的.gitattributes设置了* text eol=lffull .gitattributes)。

这是怎么回事,我该如何解决?

重现问题

我可以使用开源 project I'm maintaining 重现此问题:

$ git clone git@github.com:fairmanager/fm-log.git
Cloning into 'fm-log'...
remote: Counting objects: 790, done.
remote: Total 790 (delta 0), reused 0 (delta 0), pack-reused 790
Receiving objects: 100% (790/790), 201.71 KiB | 138.00 KiB/s, done.
Resolving deltas: 100% (418/418), done.
Checking connectivity... done.

$ cd fm-log/
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
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:   .idea/dictionaries/OliverSalzburg.xml
        modified:   .idea/inspectionProfiles/Project_Default.xml
        modified:   .idea/inspectionProfiles/profiles_settings.xml

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

【问题讨论】:

  • 在黑暗中拍摄:git ls-tree HEAD:nginx-1.11.1/contrib 是否显示了 geo2nginx.pl 的两个版本,只是大小写不同?
  • @torek 不,只有一个文件。

标签: windows git


【解决方案1】:

有趣:我尝试使用新添加到问题的 repo 克隆它,然后在 BSD 上看到相同的内容:

$ git clone git@github.com:fairmanager/fm-log.git
Cloning into 'fm-log'...
remote: Counting objects: 790, done.
remote: Total 790 (delta 0), reused 0 (delta 0), pack-reused 790
Receiving objects: 100% (790/790), 201.71 KiB | 0 bytes/s, done.
Resolving deltas: 100% (418/418), done.
Checking connectivity... done.
$ cd fm-log/
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
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:   .idea/dictionaries/OliverSalzburg.xml
    modified:   .idea/inspectionProfiles/Project_Default.xml
    modified:   .idea/inspectionProfiles/profiles_settings.xml

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

想看看发生了什么:

$ git diff
warning: CRLF will be replaced by LF in .idea/dictionaries/OliverSalzburg.xml.
[snip]

似乎HEAD:.idea/ 文件中实际上有回车符(最后一行没有换行符,因此在右尖括号后出现提示):

$ git show HEAD:.idea/dictionaries/OliverSalzburg.xml | vis
<component name="ProjectDictionaryState">\^M
  <dictionary name="OliverSalzburg">\^M
    <words>\^M
      <w>colorizer</w>\^M
      <w>multiline</w>\^M
    </words>\^M
  </dictionary>\^M
</component>$ 

工作树版本同样有回车(没有剪切和粘贴,但vis 显示相同的\^M 行结尾)。

因此,至少在这种情况下发生的情况是由于.gitattributes 设置:

$ head -2 .gitattributes 
# In general, use LF for text
* text eol=lf

Git 将在提交期间将这些文件转换为仅 LF,而不是包含 CR-LF 结尾的 HEAD 版本。这就是git status 在这里所说的。

注释掉.gitattributes 中的* text eol=lf 会使状态消失(因为文件不会被转换),当然.gitattributes 现在被标记为已修改。有趣的是,再次放回属性行,状态完全静默:有必要强制git checkout 替换工作树版本以恢复状态(例如,手动rm -rf .idea 并再次签出,或git reset --hard )。

(大概——我在这里猜测一下——当 Git 注意到工作树和 HEAD 版本不同时,索引条目会在 git checkout 期间被特别标记。这使得 Git 仔细检查文件。修改 .gitattributes 和通过git status 运行内部差异可能会取消标记索引条目。这部分纯粹是推测,旨在解释git status 的奇怪行为...)

【讨论】:

  • 我想我真的没有完全理解行尾转换以及它们是如何应用的,尤其是在提交时。过去,我会暂存并提交示例中的这三个文件,这将提交 LF 行结尾,但在我的工作副本中留下 CRLF。在更改分支后,这会造成进一步的麻烦,并且 CRLF 会在某个时候再次出现在 repo 中。
  • 我也从来没有完全清楚;我认为该机制在各个方面都发生了微妙的变化。 Git 的 smudge 和 clean 过滤器有类似的问题(Git 有时会多次运行 smudge 或 clean 过滤器,git merge 可以选择在每个文件的所有三个版本上运行属性,因为旧提交的属性可能不同)。
  • @OliverSalzburg 可以肯定的是,这正是我在回答中提到的结论,不是吗?
  • @VonC 是的。我花了一段时间才了解.gitattributes 在项目中的相关性。我之前假设如果没有启用autocrlf,它就没有任何相关性。
  • @OliverSalzburg 然而,我在回答中提到的第一件事是.gitattributes。我提到了你的 repo 的 xml 文件,在 torek (我一直很欣赏他的回答)加入之前一整分钟用 crlf 提交。
【解决方案2】:

如果本地设置git config core.autocrlf 确实是false,那么这些eol 更改必须来自.gitattributes file(请参阅man page)。

在您的本地存储库中查找一个,其中包含 eol 规则(如 * text=auto eol=crlf I mentioned here)。

check if you have a global gitattributes 文件。

git config --global core.attributesfile

关于fairmanager/fm-log,可以看到commit e6f823b中添加的“修改”文件之一commit e6f823b末尾带有crlf。

由于the .gitattributes 具有* text eol=lf 规则,因此工作目录签出blob 带有lf eol,因此git diff

【讨论】:

  • 抱歉之前没有提到。该项目实际上有一个.gitattributes,但它不包括你提到的内容。我更新了问题。
  • @OliverSalzburg 它可以 .gitattributes 在其中一个子文件夹中
  • 好点。但是,相关文件的路径中没有任何内容。
  • @OliverSalzburg 也许系统级别有指令? (在 git 安装路径)。你用的是什么版本的 Git?
  • git --version prints git version 2.9.3.windows.1 我不知道系统级别的任何其他指令。 Git 安装到%localappdata%\Programs\Git\mingw64\bin,其中不包括任何.gitattributes
猜你喜欢
  • 2015-12-13
  • 1970-01-01
  • 2013-04-05
  • 1970-01-01
  • 1970-01-01
  • 2012-06-18
  • 1970-01-01
  • 1970-01-01
  • 2020-08-17
相关资源
最近更新 更多