可能的原因 #1 - 行尾标准化
可能发生这种情况的一种情况是,当有问题的文件在没有正确配置行尾 (1) 的情况下签入存储库时,导致存储库中的文件具有不正确的行尾或混合的行尾。要确认,请确认 git diff 仅显示行尾的更改(默认情况下这些可能不可见,请尝试 git diff | cat -v 将回车视为文字 ^M 字符)。
随后,有人可能添加了 .gitattributes 或修改了 core.autocrlf 设置以规范行尾 (2)。基于.gitattributes 或全局配置,Git 已将本地更改应用于您的工作副本,这些更改应用了请求的行尾规范化。不幸的是,出于某种原因,git reset --hard 不会撤消这些行规范化更改。
解决方案
重置本地行结尾的解决方法不能解决问题。每次git“看到”文件时,它都会尝试重新应用规范化,导致同样的问题。
最好的选择是让 git 通过规范化 repo 中的所有行结尾以匹配 .gitattributes 并提交这些更改来应用它想要的规范化 - 请参阅 Trying to fix line-endings with git filter-branch, but having no luck。
如果您真的想尝试手动还原对文件的更改,那么最简单的解决方案似乎是删除修改后的文件,然后告诉 git 恢复它们,尽管我注意到这个解决方案似乎并不能始终如一地工作100% 的时间(警告:不要如果您修改的文件有除行尾以外的更改!!):
git status --porcelain | grep "^ M" | cut -c4- | xargs rm
git checkout -- .
请注意,除非您在某个时候对存储库中的行尾进行规范化,否则您将继续遇到此问题。
可能的原因 #2 - 不区分大小写
第二个可能的原因是 Windows 或 Mac OS/X 不区分大小写。例如,假设存储库中存在如下路径:
/foo/bar
现在 Linux 上的某个人将文件提交到 /foo/Bar(可能是由于构建工具或创建该目录的东西)并推送。在 Linux 上,这实际上是两个独立的目录:
/foo/bar/fileA
/foo/Bar/fileA
在 Windows 或 Mac 上签出此 repo 可能会导致修改后的 fileA 无法重置,因为在每次重置时,Windows 上的 git 会签出 /foo/bar/fileA,然后由于 Windows 不区分大小写,会覆盖 @ 的内容987654339@ 和 /foo/Bar/fileA,导致它们被“修改”。
另一种情况可能是存储库中存在的单个文件,当在不区分大小写的文件系统上签出时,它们会重叠。例如:
/foo/bar/fileA
/foo/bar/filea
可能还有其他类似情况会导致此类问题。
不区分大小写的文件系统上的 git 应该真正检测到这种情况并显示有用的警告消息,但它目前没有(这可能会在未来发生变化 - 请参阅 this discussion 和 git.git 邮件列表上的相关建议补丁) .
解决方案
解决方案是将 git 索引中的文件大小写与 Windows 文件系统上的大小写对齐。这可以在 Linux 上完成,这将显示事物的真实状态,或者在 Windows 上使用非常有用的开源实用程序 Git-Unite。 Git-Unite 会将必要的案例更改应用于 git 索引,然后可以将其提交到 repo。
(1) 这很可能是使用 Windows 的人,没有为相关文件定义任何 .gitattributes,并使用 core.autocrlf 的默认全局设置 false(参见 (2))。
(2)http://adaptivepatchwork.com/2012/03/01/mind-the-end-of-your-line/