git reset --hard 后出现了未暂存的更改

407

执行了 git reset --hard 后,git status 会在 Changes not staged for commit: 部分列出文件。

我还尝试了 git reset .git checkout -- .git checkout-index -f -a,但都无济于事。

那么,如何才能摆脱这些未暂存的更改呢?

这似乎只影响 Visual Studio 项目文件。奇怪。请参见此粘贴: http://pastebin.com/eFZwPn9Z。这些文件的特殊之处在于,在 .gitattributes 中我有:

*.sln        eol=crlf
*.vcproj     eol=crlf
*.vcxproj*   eol=crlf

另外,我的全局 .gitconfigautocrlf 被设置为 false。这是否有相关性呢?


你是在代码库的根目录下执行这些命令吗?注意 . 代表当前目录,而不是根目录。 - Alexander
1
我确实是从根仓库中进行了这些操作。 - Norswap
你的git版本是什么?你要么犯了一个愚蠢的错误,要么你的版本过旧并且存在漏洞。 - Shahbaz
这是git 1.7.4 msysgit。可能有错误,但命令似乎足够简单,我找不到错误。 - Norswap
同样的问题,我在一台机器上(Windows)添加了 .gitattributes,现在在另一台机器上(Linux),从主仓库pull之后,某些文件出现了修改,即那些具有 eol=crlf 的文件... - szx
显示剩余3条评论
22个回答

462

我曾经遇到同样的问题,它与 .gitattributes 文件有关。 然而导致问题的文件类型并没有在.gitattributes中指定。

我通过简单地运行以下命令解决了这个问题

git rm .gitattributes
git add -A
git reset --hard

9
这是在gitattributes文件中的* text=auto指令。它会自动更改文件结尾,因此当您检查状态时,文件将自动标记为“已修改”。 - Cody Django
4
这个方法可行,但我不完全明白为什么。有人可以解释一下每个命令吗? - Edwin Stoteler
28
git rm .gitattributes 可以将 .gitattributes 从索引中删除。 git add -A 将所有文件(包括 .gitattributes 的删除)添加到索引,如果真正的问题只是 .gitattributes 文件的删除,则此时应该只有 .gitattributes 文件被删除。 git reset --hard 将撤销所有未提交的更改,其中包括 .gitattributes 文件的删除。 本质上,这个步骤通过删除 .gitattributes 文件来忽略它(以及 * text=auto 指令),然后在删除后重置索引,因此将其放回,但在重置其他文件之后,因此它们不会再次被修改。 - cloudworks
5
@GameScripting,这个解决方案对我不起作用。没有名为.gitattributes的文件: “fatal: pathspec '.gitattributes' did not match any files” - Pacerier
5
我执行某个操作时出现了 fatal: pathspec '.gitattributes' did not match any files 的错误提示,我做错了什么? - mfaani
显示剩余9条评论

283

我已经通过以下步骤解决了这个问题

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

git rm --cached -r .
  • 重写Git索引以捕捉所有新的换行符。

    git reset --hard
    
  • 解决方案是在配置Git以处理行尾的步骤中描述的一部分。


    8
    当其他方法都无效时,这个方法很有效!我们尝试了许多其他方法,包括在其他帖子中的方法 - 因为开发团队很大,让每个人使用相同的 crlf 格式一直是一场噩梦,但是这个方法解决了问题。 - tkersh
    2
    有趣...我使用了"git rm --chached <one_specific_file>",而不是删除所有文件。 "git status" 报告该文件已被删除且未跟踪。然后,"git reset --hard" 修复了该文件以及所有其他被报告为“未暂存的更改”的文件。(在使用 git rm 之前,我尝试过 reset hard,但没有效果)。我喜欢这种神秘的源代码控制系统。 - joeking
    3
    太残忍了。你本可以通过删除仓库目录并重新克隆来做同样的事情。 - ingyhere
    5
    假设您使用的是 Windows 操作系统,这是不区分大小写的。如果您与开发人员合作,并且他们使用的是区分大小写的文件系统,例如 Mac 或 Linux,他们可能会在不同大小写的情况下提交标签、分支甚至文件。在这种情况下,您的 Git 索引会显示已经存在的文件被操作系统覆盖的情况。解决这个问题的唯一方法是在您的Git服务器上(钩子)制定命名规则或预先检测错误。 - ingyhere
    7
    这对我来说没有起作用。在运行 git reset --hard 之前,我需要运行 git add . - Derk Jan Speelman
    显示剩余5条评论

    133

    Git不会重置那些不在仓库中的文件。因此,您可以:

    $ git add .
    $ git reset --hard
    

    这将暂存所有更改,使Git能够知道这些文件,并重置它们。

    如果这不起作用,你可以尝试隐藏并丢弃你的更改:

    $ git stash
    $ git stash drop
    

    4
    这些文件已经被跟踪了。我仍然尝试了一下,但它仍然没有起作用。我的代码库肯定存在某些严重问题,但我不知道具体是什么。有关Git stash的事情,请参阅我回复Shahbaz的答案。 - Norswap
    1
    我想到了一个解决方案。提交(commit)并进行交互式变基(interactive rebase)以删除该提交(commit)。 - YuriAlbuquerque
    1
    @YuriAlbuquerque,Git并不完全理解POLAgit reset --hard的整个意图不就是“重置暂存区和工作目录以匹配”吗?如果一个文件没有被暂存,当我们执行reset --hard时,显然我们希望将其删除。 - Pacerier
    @Pacerier,我不知道为什么会做出这样的设计决策,但我可以猜测的理由是未跟踪的文件不应立即由Git管理。 - YuriAlbuquerque
    @YuriAlbuquerque,Git似乎没有太多关于设计的思考。 - Pacerier
    显示剩余2条评论

    128

    如果您使用Git for Windows,则很可能会遇到此问题

    我也遇到了同样的问题,无论是stash、hard reset、clean甚至全都尝试过了,但仍然留下了更改。后来发现Git没有正确设置x文件模式,这是Git for Windows的一个“已知问题”。本地更改在gitk和git status中显示为旧模式100755新模式100644,没有任何实际的文件差异。

    解决方法是忽略文件模式:

    git config core.filemode false
    

    点击此处查看更多信息。


    谢谢。这很有帮助。另请参阅https://dev59.com/um445IYBdhLWcg3wKHAJ#31187212 - Ryan
    这对我也起作用,当存储库在NAS中使用与运行git的计算机不同的文件系统/操作系统时(不涉及Windows)。 - furins
    这个方法对我在Windows上使用WSL解决了问题。 - undefined

    35

    好的,我已经基本解决了这个问题。

    似乎是由于 .gitattributes 文件中包含以下内容:

    *.sln        eol=crlf
    *.vcproj     eol=crlf
    *.vcxproj*   eol=crlf
    

    项目文件显示为未暂存状态,我不知道原因,希望了解git的人能够给我们一个好的解释。

    我的解决方法是删除这些文件,并在.git/config中的[core]下添加autocrlf = false

    这并不完全等同于之前的配置,因为它要求每个开发者都有autocrlf = false。我想找到更好的解决方法。

    编辑:

    我注释掉了可疑的行,取消注释,然后它就起作用了。什么...我甚至...!


    1
    我遇到了完全相同的问题,这是唯一有效的解决方案。在我的情况下,我从一个功能分支切换到了主分支,并从上游拉取了一些新更改,然后它就开始在2个修改过的文件中出现。看起来这些文件可能已经从Unix转换为Windows行尾符,这可能与此有关。不确定原因和原因如何。 - Steven Surowiec
    在Windows Git上遇到了同样的问题。已经将autocrlf = false设置好。我合并了来自上游svn仓库的更改(git svn fetch / git merge git-svn),但是有1个文件被标记为修改。奇怪的是,我以前也遇到过这个问题,而我所需要做的就是检出另一个分支(带有-f标志),然后再切换回来。我做到了,它起作用了,但是当我切换到一个特性分支时,“更改”又回来了!你在答案底部的编辑是解决方案。在我的情况下,我在我的.gitattributes文件顶部注释/取消注释了一行:* text=auto !eol - Aaron Blenkush
    1
    那个编辑对我也起作用了:注释掉.gitattributes中的行,运行git status,取消注释.gitattributes中的行,再次运行git status - Ignitor
    这是因为git正在重置那些文件,然后再次应用.gitattributes中指定的行尾。git diff可能会显示著名的红墙/绿墙,这是行尾差异的典型表现。 - Dagrooms
    我的解决方案:开始之前: 提交您当前的工作。然后: 1. git reset --hardgit checkout --force <branch> 2. git add --all --renormalize 3. git commit -m "规范化所有行结尾". 参考 - ADTC

    29
    可能原因#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#47580886git-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/

    我勒个去,终于到这儿了。第一行就得提交,但最终还是成功切换到主分支。这过程真不好玩。 - Tim Lieberman
    我认为这应该是被接受的答案,因为它指出了两个最重要的原因。在我的情况下,我正在使用MAC和它的文件系统APFS。它是保留大小写但不区分大小写的。 - undefined

    28

    如果其他答案无效,尝试添加文件,然后重置

    $ git add -A
    $ git reset --hard
    
    在我的情况下,这对于git正在跟踪的一堆空文件非常有帮助。

    2
    这个可以。我只是使用了 $ git add . 而不是 $ git add -A - Bjarne Gerhardt-Pedersen
    这对我也有效。我使用了 $ git add . 然后使用 $ git reset --hard. - TazExprez

    9

    这可能是由于大小写不敏感的文件系统造成的。如果您在同一级别的存储库中有多个名称仅因大小写而不同的文件夹,那么您将受到此影响。请使用其Web界面(例如GitHub或VSTS)浏览源代码存储库以确保。

    更多信息,请参见: https://dev59.com/hnI-5IYBdhLWcg3wF0Uc#2016426


    要查找这些文件的名称,请运行 git ls-tree -r --name-only HEAD | tr A-Z a-z | sort | uniq -d - Diomidis Spinellis
    除了文件夹之外,这也可能发生在仅区分大小写的 Windows 存储库与区分大小写的 Linux 主机上 - 不知何故,我们在其中一个文件的副本中得到了一个不同大小写的版本。 - Daniel Bowen
    完全就是我的问题! - phatnhse

    7
    您可以将更改 stash 起来,然后放弃 stash:
    git stash
    git stash drop
    

    1
    您可能也对这个感兴趣。 - Shahbaz

    6
    运行 clean 命令:
    # Remove all untracked files and directories. (`-f` is `force`, `-d` is `remove directories`)
    
    git clean -fd
    

    4
    不幸的是,这并没有解决与行尾相关的问题。 - Christopher Schneider
    这是唯一解决我的问题的方法,无论我的问题是什么。我没有.gitattributes文件,行尾也不是问题,因为我在Linux系统上,所有开发人员和我们的服务器都是Linux。 - Samuel Neff

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