为什么git一直在搞我的行结尾?

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

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

现在,我认为,git不会混淆任何行结尾,但我的回购中的一些文件不断导致问题。

我刚刚克隆了一个新的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行尾。

当我现在执行并提交文件时,即使diff显示CRLF行尾,结果文件也会有LF行尾。

我没有一个全局的.gitattributesgit config --global core.attributesfile不输出任何东西)。 而项目中的.gitattributes* text eol=lf set( full .gitattributes )。

这里发生了什么,我该如何解决这个问题?

重现问题

我可以用我正在维护的一个开源项目来重现这个问题:

 $ 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") 

Solutions Collecting From Web of "为什么git一直在搞我的行结尾?"

有趣的是:在新增的问题回购中,我尝试了克隆它,在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/ files实际上有回车符(最后一行没有换行符,因此紧接在括号后面):

 $ 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>$ 

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

那么在这种情况下至少发生了什么,是由于.gitattributes设置:

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

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

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

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

如果本地设置git config core.autocrlf确实为false ,那么这些eol更改必须来自.gitattributes文件 (请参见手册页 )。

在你的本地仓库中寻找一个,这将包括EOL规则(如我在这里提到的 * text=auto eol=crlf )。

或者检查你是否有全局的gitattributes文件。

 git config --global core.attributesfile 

关于fairmanager/fm-log ,你可以看到一个“修改”的文件.idea/dictionaries/OliverSalzburg.xml添加在提交e6f823b与crlf在最后。

由于.gitattributes* text eol=lf规则,因此工作目录checkout blob和lf eol,因此git diff