可能原因#1 - 行尾标准化
这种情况可能发生在文件被提交到代码库时,没有正确配置行尾标准化(1),导致代码库中的文件具有错误的行尾或混合行尾。要确认,请验证
git diff
是否仅显示行尾的更改(这些更改可能默认情况下不可见,可以尝试
git diff | cat -v
以将回车符显示为字面上的
^M
字符)。
随后,可能有人添加了
.gitattributes
文件或修改了
core.autocrlf
设置以进行行尾标准化(2)。根据
.gitattributes
文件或全局配置,Git已经对您的工作副本应用了本地更改,以应用所请求的行尾标准化。不幸的是,由于某种原因,
git reset --hard
无法撤消这些行尾标准化的更改。
解决方案
重置本地行尾标准化不会解决问题。每次git“看到”该文件时,它都会尝试重新应用标准化,导致相同的问题。
最好的选择是让git应用它想要的规范化方式,通过将仓库中的所有行尾标准化以匹配.gitattributes,并提交这些更改--参见
尝试使用git filter-branch修复行尾,但没有成功。
如果你真的想尝试手动恢复文件的更改,最简单的解决方案似乎是删除修改的文件,然后告诉git恢复它们,尽管我注意到这个解决方案并不总是100%可靠(
警告:如果你的修改文件除了行尾之外还有其他更改,请不要运行此操作!!)。
git status --porcelain | grep "^ M" | cut -c4- | xargs rm
git checkout -- .
请注意,除非您在某个时刻对存储库中的行尾进行规范化,否则您将继续遇到此问题。
可能的原因之二是Windows或Mac OS/X上的大小写不敏感。例如,假设存储库中存在以下路径:
/foo/bar
现在,Linux上的某个人将文件提交到/foo/Bar(可能是由于构建工具或其他创建了该目录),并进行推送。在Linux上,这实际上是两个不同的目录:
/foo/bar/fileA
/foo/Bar/fileA
在Windows或Mac上检查此存储库可能会导致无法重置的修改过的
fileA
,因为在每次重置时,Windows上的git会检出
/foo/bar/fileA
,然后由于Windows不区分大小写,会用
/foo/Bar/fileA
覆盖
fileA
的内容,导致它们被"修改"。
另一种情况可能是存储库中存在的个别文件,在不区分大小写的文件系统上检出时会重叠。例如:
/foo/bar/fileA
/foo/bar/filea
可能还有其他类似的情况可能导致这些问题。
在不区分大小写的文件系统上,git应该能够检测到这种情况并显示一个有用的警告信息,但目前还没有(这可能会在将来改变--请参见
此讨论和git.git邮件列表上的相关建议补丁)。
解决方案
解决方案是将git索引中的文件大小写与Windows文件系统中的大小写对齐。这可以在Linux上完成,以显示真实的情况,或者可以使用非常有用的开源工具
Git-Unite在Windows上完成。Git-Unite将应用必要的大小写更改到git索引中,然后可以提交到仓库中。
另一个在Git 2.16+中非常有用的工具是
git add --renormalize <pathspec>
命令。更多信息请参考
https://dev59.com/fGw05IYBdhLWcg3wkivX#47580886和
git-add man page。
--renormalize
为了修正使用错误的CRLF/LF行尾符添加的文件,可以使用core.autocrlf配置或文本属性。此选项隐含了-u选项。孤立的CR字符不会被修改,因此CRLF会被清理为LF,而CRCRLF序列只会部分清理为CRLF。将“clean”过程重新应用于所有已跟踪的文件,以强制将它们再次添加到索引中。这在更改后非常有用。
(1) 这很可能是由于某人在Windows上操作,对于所涉及的文件没有定义.gitattributes
,并且使用了默认的全局设置core.autocrlf
,其值为false
(参见(2))。
(2)
http://adaptivepatchwork.com/2012/03/01/mind-the-end-of-your-line/
.
代表当前目录,而不是根目录。 - Alexander.gitattributes
,现在在另一台机器上(Linux),从主仓库pull之后,某些文件出现了修改,即那些具有eol=crlf
的文件... - szx