可能的原因 #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,但没有运气。
如果您真的想尝试手动还原对文件的更改,那么最简单的解决方案似乎是删除修改后的文件,然后告诉 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 不区分大小写,会覆盖fileA
with的内容/foo/Bar/fileA
,导致它们被“修改”。
另一种情况可能是存储库中存在的单个文件,当在不区分大小写的文件系统上签出时,这些文件会重叠。例如:
/foo/bar/fileA
/foo/bar/filea
可能还有其他类似情况可能导致此类问题。
不区分大小写的文件系统上的 git 应该真正检测到这种情况并显示有用的警告消息,但它目前没有(这可能会在未来发生变化——请参阅git.git 邮件列表上的讨论和相关建议补丁)。
解决方案
解决方案是将 git 索引中的文件大小写和 Windows 文件系统上的大小写对齐。这可以在 Linux 上完成,这将显示事物的真实状态,或者在 Windows 上使用非常有用的开源实用程序Git-Unite完成。Git-Unite 将对 git 索引应用必要的案例更改,然后可以将其提交到 repo。
(1) 这很可能是使用 Windows 的人,没有对相关文件进行任何定义,并且使用的是.gitattributes
默认全局设置(参见 (2))。core.autocrlf
false
(2) http://adaptivepatchwork.com/2012/03/01/mind-the-end-of-your-line/