Git:如何还原两个固执地停留在“已更改但未提交”状态的文件?

111

我有一个repo,其中有两个文件,据说我在本地对它们进行了更改。

所以我陷入了这种情况:

$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dir1/foo.aspx
#       modified:   dir2/foo.aspx
#
no changes added to commit (use "git add" and/or "git commit -a")

运行 git diff 命令时提示整个文件内容已经更改,但是从肉眼观察来看好像并不是这样(有些共同的行范围似乎漏掉了diff)。

有趣的是我记不得在本地更改过这些文件。该存储库与一个远程存储库一起使用(私有的,在GitHub.com上)。

无论我尝试了什么,都无法放弃这些本地更改。我已尝试:

$ git checkout -- .
$ git checkout -f
$ git checkout -- dir1/checkout_receipt.aspx
$ git reset --hard HEAD
$ git stash save --keep-index && git stash drop
$ git checkout-index -a -f
换句话说,我已经尝试了如何丢弃Git中未暂存的更改?中描述的所有方法以及更多方法,但这两个文件仍然显示为“已更改但未提交”。
到底是什么原因导致这两个文件处于这种状态,看起来无法还原?
PS:在上面列出的我已经尝试过的命令列表中,我误写了git revert,实际上我想写的是git checkout。对此我感到抱歉,并向那些建议我尝试checkout的人表示感谢。我已编辑问题进行了更正,确实已经尝试过checkout

git diff --ignore-space-changegit diff --ignore-all-spacegit diff 的输出中有什么区别吗? - jdd
@jermiahd 是的!使用任何一个标志,git diff 都会显示这些文件是相同的。 - Greg Hendershott
2
可能是https://dev59.com/hnI-5IYBdhLWcg3wF0Uc的重复问题。无论如何,我更喜欢那里被接受的答案,即设置`git config --global core.autocrlf false`而不是“true”。 - Johann
2
这里的答案对我和许多其他人都有效。[1] - Mike K
2
当在不区分大小写的文件系统中检出包含同一目录中具有不同大小写的2个或更多文件的存储库时,也会发生这种情况。删除或重命名其中一个文件以解决此问题。 - user1436916
15个回答

186

我花了几个小时试图解决一个类似的问题 - 已检出的远程分支,即使删除所有文件并再次运行git checkout -f(或来自此帖子的其他变体),仍然顽固地显示四个文件为“已更改但未更新”!

这四个文件是必需的,但肯定不是我修改的。我的最终解决方案 - 说服 Git 它们没有被更改。以下方法适用于所有已检出文件,显示“修改”的状态 - 确保您已经提交/保存了真正被修改的任何文件!:

git ls-files -m | xargs -i git update-index --assume-unchanged "{}"

在 Mac OSX 上,xargs 的操作略有不同(感谢评论中的 Daniel):

git ls-files -m | xargs -I {} git update-index --assume-unchanged {}

我把这个留作下次使用的占位符。


14
我有几个难以处理的文件,运行了这个命令后,git status显示没有更改,但当我试图切换分支时,git仍然告诉我不能切换,因为这两个文件有本地更改。不确定我做错了什么,但似乎只是掩盖了问题而没有真正解决它?运行该命令后,我也无法提交这些文件。对我来说,解决方法是删除这些文件,提交并切换分支。 - RodH257
6
谢谢!我尝试了所有其他答案中提到的技巧 - 都没有起作用。在Mac上不能直接使用该行命令,只需对每个文件运行 git update-index --assume-unchanged <filename> 即可解决问题。 - Yonatan Karni
7
这正是我所需要的,尽管在Mac上使用xargs似乎有点不同(我在运行10.10 Yosemite)。这对我最终起作用了: git ls-files -m | xargs -I {} git update-index --assume-unchanged {} - Daniel
4
撤销命令的效果:git ls-files -v|grep '^h' | cut -c3- | xargs -i git update-index --no-assume-unchanged "{}" - Marinos An
4
这个解决方案并没有修复问题,而是隐藏了它。在使用assume-unchaged之后有一些操作无法执行,就像@RodH257的情况一样。我相信对于像git checkout -- filegit stashgit reset --hard HEAD这样的命令不起作用的情况,最正确的答案已经被回答过了,那就是编辑.gitattributes文件。 - Marinos An
显示剩余5条评论

38

文件中的行尾是什么?我打赌它们是CRLF。如果是这样,请查看此指南

简而言之,您需要确保git在提交时将行尾转换为LF,并提交这些文件。仓库中的文件应始终是LF,检出的文件应为操作系统的本地文件,前提是您正确设置了git。


1
谢谢。我已经有了 git config --global core.autocrlf true,另一方在 GitHub 上推送到仓库时也是这样做的。 - Greg Hendershott
1
然后,你只需要按照指南中最后一个<pre>块中的步骤来修复存储库中的文件。 - Tekkub
5
我不同意在存储库中始终使用LF作为行尾符号(特别是如果其他人已经提交了CRLF),也不同意操作系统应该始终使用本地格式。我的 Windows 编辑器和环境(主要用于 PHP、HTML、CSS 等)可以很好地适应 LF 行尾符号。 - Simon East
一个天才的答案,我忘记了我最近使用gitattributes来强制在repo文件中使用LF,并且没有预料到git会自动更改文件。我们有Windows和Linux开发人员的混合,这让我们疯狂,因为不同平台上的编辑器不断切换行终止符,一旦这个变化传播到所有人,这个问题就会消失。 - Oly Dungey

24

2
在 .gitattributes 文件中删除 text=auto 设置对我有用,然后在执行 git reset --hard 命令之后,再将该设置添加回去,这些文件就不再显示为已修改! - ErikE
1
这个 text=auto 设置显然有问题。我正在处理来自多个操作系统的提交仓库,但我仍然没有弄清楚保留它还是放弃它会给我带来更多的问题。 - Marinos An
1
@MarinosAn 是的,有的,具体来说,git 允许您在第一次添加此设置时保留现有的文本文件和错误的行尾。这是不正确的,除非您自己记得这样做,否则最终会遇到其中一个不可撤销的更改。 - Roman Starkov

12

另一个可能性是差异(阻止您使用checkout命令还原这些文件)的其中一个是文件模式。这就是我遇到的情况。在我的git版本中,您可以通过使用

git diff dir1/foo.aspx

发现它,并将显示文件模式更改。尽管如此,它仍然不会让您恢复它们。为此,请使用以下任一方法之一:

git config core.filemode false

或通过在文本编辑器中添加

[core]

filemode = false

完成此操作后,您可以使用

git reset HEAD dir1/foo.aspx

此时文件应该会消失。

(我从如何使git忽略模式更改(chmod)?的答案中获取了所有这些信息。)


1
如果你使用的是Windows系统,Eyal的诊断/解决方案应该是你的第一个猜测。 - AlcubierreDrive
特别注意,不要从cmd.exe使用cygwin git。如果您想在cmd.exe中使用git,请安装msysgit。 - AlcubierreDrive
只是确认这确实是Windows上的问题。 - Dejan Marjanović
对于我在Windows上,这不是问题(core.filemode已经设置为false)。在我的情况下,修复/解决方法是Alan Forsyth的答案中的一个。 - Venryx

8
我遇到了相同的问题,有趣的是,这些文件在Windows上被更改了,但从WSL查看时并没有变化。无论如何调整换行符、重置等操作都不能改变它。

最终,我在这个回答中找到了解决方案。以下为方便起见的文本:


我使用以下步骤解决了这个问题

1) 从Git的索引中删除每个文件。

git rm --cached -r .

2) 重新编写 Git 索引以获取所有新的行尾。

git reset --hard

这个解决方案是git网站上描述的步骤的一部分。 https://help.github.com/articles/dealing-with-line-endings/


4

这个问题也可能是由于Git将大小写差异视为不同的文件,而Windows将它们视为相同的文件所造成的。如果只更改了文件名的大写形式,则该仓库的每个Windows用户最终都会陷入这种情况。

解决方法是确认文件内容正确,然后重新提交。我们需要将两个文件内容合并在一起,因为它们是不同的。然后拉取并出现合并冲突,您可以通过删除重复文件来解决它。重新提交合并解决方案,就可以回到稳定状态了。


1
我在 Mac 上也遇到了这个问题,以防其他人也遇到这个问题,这不仅仅是 Windows 的问题。对我来说,问题出在一个目录的大小写发生了改变,虽然在多次提交后一切都正常,但只要该路径下的文件发生变化,它就会卡住。我们不得不使用 git rm 命令删除旧路径,然后添加“新”(已存在的)路径,这样问题就解决了。 - doz87

4
尝试撤销本地更改:
git checkout -- dir1/foo.aspx
git checkout -- dir2/foo.aspx

我脑海中一直想着“还原”,但我本来想写的是checkout。我已经尝试过checkout了。无论如何,谢谢你的回答。它对我的原始问题是一个很好的答案,所以我会点赞。 - Greg Hendershott

3

我有一些幻影更改的文件,它们显示为已修改,但实际上是相同的。

运行以下命令有时可以解决问题:
(关闭git的“智能”但通常不太有用的换行符转换)

git config --local core.autocrlf false

但在另一种情况下,我发现是由于根目录中的.gitattributes文件存在某些行尾设置,它尝试对某些文件应用autocrlf,即使已关闭该选项。这实际上没有帮助,因此我删除了.gitattributes,提交后,该文件不再显示为已修改。


在 .gitattributes 文件中删除 text=auto 设置对我有用,然后在执行 git reset --hard 命令之后,再将该设置添加回去,这些文件就不再显示为已修改! - ErikE

2
git checkout dir1/foo.aspx
git checkout dir2/foo.aspx

我脑子里一直想着“还原”,实际上我想写的是checkout。我已经尝试过checkout了。不管怎样,谢谢你的回答。它是对我最初问题的一个好回答,所以我会点赞。 - Greg Hendershott

2
您可能遇到了与目录命名大小写相关的问题。您的一些同事可能已经将目录名称从例如 myHandler 更改为 MyHandler。如果您稍后推送和拉取了原始目录的某些文件,您在远程存储库上将有两个单独的目录,而在Windows上您只能有一个,而在本地机器上只有一个。这会给您带来麻烦。
要检查是否存在这种情况,请看远程存储库是否具有双重结构。
要解决此问题,请在repo之外备份父目录,然后删除父目录并将其推送。进行拉取(此时应出现标记为已删除的第二个)并再次推送。之后,从备份中重新创建整个结构并再次推送更改。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接