如何取消暂存大量文件而不删除内容

581

我不小心使用git add -A添加了很多临时文件。

我使用以下命令成功将这些文件撤销暂存,并成功移除了脏索引。

git ls-files -z | xargs -0 rm -f
git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
上述命令在git help rm中列出。但可悲的是,尽管我已经给定了缓存选项,但我的文件也被删除了。如何清除索引而不丢失内容?同时,如果有人能够解释一下这个管道操作的工作方式,那将会很有帮助。

8
rm -f 不是 Git 命令,也没有 --cached 选项。在你执行 git rm 命令之前,本地文件已经被删除了,因此我认为你不能合理地责怪 git rm 命令造成的问题。 - CB Bailey
9
@sarat,请考虑将正确答案更改为Ian Maddox所提供的受到高票支持的答案,因为git reset --hard并不是正确的答案,实际上会删除内容。这会让用户感到困惑,就像我一样。 - user2210287
2
@sarat,正如Marco所说,继续吧。这个页面的流量很大。 - Ross
9个回答

1223

git reset

如果你只是想撤销一个过度添加的 "git add" 操作:

git reset

您的更改将被取消暂存,并准备好供您根据需要重新添加。


不要运行git reset --hard命令。

它不仅会取消已添加的文件,还会恢复您在工作目录中所做的任何更改。如果您在工作目录中创建了任何新文件,则不会删除它们。


1
我经常发现我需要执行 git checkout -- * - Den-Jason

39

如果你有一个干净的版本库 (或 HEAD 没有设置) [1],你可以简单地做如下操作

rm .git/index
当然,这将需要您重新添加那些您想要添加的文件。
注(如评论中所述):这通常只会在仓库是全新的(“原始状态”)或者没有进行任何提交时发生。更技术性地说,只要没有检出或工作树即可。
只是让它更清楚 :)

你确定这是安全的操作吗?我刚刚做了这个(实际上是将索引移出路径),结果所有其他文件都被标记为待删除。 - inger
@inger “如果你有一个原始的存储库”,显然你没有。 - sehe
其实,我想知道你所说的“pristine repo”是什么意思(我在谷歌上也没有找到)。所以你的意思是一个空的repo? - inger
@inger Pristine是指您尚未提交任何内容的情况,这很常见:执行初始的git add .时,我通常会对我不想添加的文件数量感到惊讶:) 在这种非常特殊的情况下,我确实只会清除索引。 - sehe
1
@inger 同意。我不得不假设 OP 恰好遇到了这种情况——他没有具体说明,但他的描述留下了这种可能性。无论如何,我只是分享信息,不能影响投票 :(。已经在答案文本中添加了一个警告词,以防将来有所帮助。 - sehe
显示剩余2条评论

19

使用 git reset HEAD 来在不删除文件的情况下重置索引。(如果你只想重置索引中的特定文件,你可以使用 git reset HEAD -- /path/to/file)。

管道操作符,在 shell 中,将左侧进程的 stdout 作为 stdin 传递给右侧进程。它基本上相当于:

$ proc1 > proc1.out
$ proc2 < proc1.out
$ rm proc1.out

但实际上是 $ proc1 | proc2,第二个进程可以在第一个进程输出数据之前开始获取数据,而且没有实际的文件涉及。


但是如何在多个文件中使用它,我以前从未提交过这些文件。 - sarat
5
只需键入 git reset HEAD 而不指定其他内容,它将重置整个索引。然后,您只需重新添加您想要的文件即可。 - Amber
我遇到了以下错误。我以前从未提交过这些项目。$ git reset HEAD fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree. Use '--' to separate paths from revisions - sarat
1
尝试只使用 git reset 命令,不带 HEAD 参数。 - Amber
我已经尝试过了,但最终出现了以下错误:$ git reset fatal: Failed to resolve 'HEAD' as a valid ref. - sarat

18

2019更新

正如其他相关问题中所指出的(请参见这里这里这里这里这里这里这里),您现在可以使用git restore --staged <file>取消暂存文件。

要取消暂存项目中的所有文件,请从存储库的根目录运行以下命令(该命令是递归的):

git restore --staged .

如果您只想取消暂存目录中的文件,请在运行上述命令之前导航到该目录,或者运行:

git restore --staged <directory-path>

笔记

  • git restore 命令于 2019年7月 推出,并在2.23版本中发布。
    使用 --staged 标志时,它会从 HEAD 恢复工作树的内容(因此与 git add 相反,不会删除任何更改)。

  • 这是一个新命令,但旧命令的行为保持不变。因此,早期使用 git resetgit reset HEAD 的答案仍然完全有效。

  • 当运行 git status 时,如果有暂存但未提交的文件,Git 现在建议使用此命令来取消暂存文件(而不是之前版本 v2.23 之前使用的 git reset HEAD <file> 命令)。


11
git stash && git stash pop

2
我会执行 'git stash && git stash pop',这样存储的东西也会被删除。'apply' 命令只会将存储的内容应用到代码中,不会从存储列表中删除。 - chitti

8
如果 HEAD 没有设置(即,您还没有进行过任何提交,但是您不想仅仅删除 .git,因为您已经设置了其他要保留的存储库配置),您也可以执行以下操作:
git rm -rf --cached .

取消暂存所有更改。这与sehe的解决方案基本相同,但避免了涉及Git内部的麻烦。

这实际上告诉Git删除缓存区中的所有内容。 - Visionary Software Solutions
1
缓存也被称为暂存区,所以我不确定你的意思是什么。 - jjlin
从sehe那里得知,rm .git/index非常危险 - 一定要阅读他关于全新repo的注意事项!这对于其他99.999%的时间来说更安全。我没有仔细阅读,结果在我的工作副本上执行了rm .git/index,不得不将其删除并重新克隆。 - phpguru
不要使用此命令!它会将所有项目更改为未跟踪状态(包括未暂存的项目)。这不是原问题的解决方案。正确的解决方案是使用“git rm”命令,仅指定您想要取消暂存的文件:git rm -rf --cached <要取消暂存的文件>。 - Monte Creasor
只有在您有一个尚未提交任何内容的新存储库(这就是“HEAD未设置”的含义)时,才应使用此命令,但您不想仅仅删除.git,因为您已经设置了其他要保留的存储库配置。我已编辑以澄清此问题。 - jjlin

6

警告: 除非您想要丢失未提交的工作,否则不要使用以下命令!

已经解释了如何使用git reset,但您还要求解释管道命令,因此在这里进行解释:

git ls-files -z | xargs -0 rm -f
git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached

命令 git ls-files 列出 git 知道的所有文件。选项 -z 强制按照特定格式列出它们,该格式为 xargs -0 期望的格式,然后在这些文件上调用 rm -f ,这意味着不会征求您的批准即可将其删除。
换句话说,“列出git所知道的所有文件并删除它们的本地副本”。
接下来是 git diff ,它显示 git 所知道的不同版本之间的更改。这些可以是不同树之间的变化,本地副本和远程副本之间的差异等等。
在此处使用,它显示未暂存的更改; 您已更改但尚未提交的文件。选项 --name-only 意味着您只想获得(完整的)文件名,而 --diff-filter=D 意味着您只对已删除的文件感兴趣。 (嘿,我们刚刚删除了一堆东西吗?) 然后管道传输到之前看到的 xargs -0 ,它在它们上面调用 git rm --cached ,这意味着它们从缓存中移除,而工作树应该保持不变——除非您刚刚从您的工作树中删除了所有文件。现在它们也从您的索引中删除了。
换句话说,所有更改,无论是暂存的还是未暂存的,都消失了,您的工作树为空。哭一场,从原始或远程检出文件,然后重新开始你的工作。诅咒那些编写这些地狱般的行的变态人; 我完全不知道为什么会有人想这样做。

TL;DR: 您已经清除了所有内容; 从现在开始使用 git reset


3

很抱歉,第一条命令会无条件地从工作副本中删除所有在git暂存区中的文件。第二条命令会取消暂存所有被跟踪但现在已被删除的文件。不幸的是,这意味着您将失去对这些文件的任何未提交修改。

如果您想让您的工作副本和索引回到上次提交时的状态,您可以(小心地)使用以下命令:

git reset --hard

我说“小心”是因为git reset --hard会删除您工作副本和索引中未提交的更改。但是,在这种情况下,似乎您只想回到上次提交的状态,并且未提交的更改已经丢失了。

更新:根据您在Amber答案中的评论,听起来您还没有创建任何提交(因为无法解析HEAD),所以恐怕这不会有帮助。

至于这些管道的工作原理:git ls-files -zgit diff --name-only --diff-filter=D -z都输出由字节0分隔的文件名列表。(这很有用,因为与Unix类似的系统上,与换行符不同,0字节保证不会出现在文件名中。)程序xargs从其标准输入构建命令行, 默认情况下通过从标准输入获取行并将其添加到命令行的末尾来完成。 -0选项表示期望标准输入以0字节分隔。 xargs可以多次调用该命令以使用所有来自标准输入的参数,确保命令行永远不会变得太长。

作为一个简单的例子,如果您有一个名为test.txt的文件,其内容如下:

hello
goodbye
hello again

如果执行命令xargs echo whatever < test.txt,则会调用以下命令:

echo whatever hello goodbye hello again

我从未进行过任何提交,因此它会显示无法解析HEAD。在这种情况下我们该怎么做?非常感谢您详细解释管道。 - sarat
8
如果你刚刚修改了.gitignore文件,并且使用"git add --all"命令添加了很多文件,请不要运行"git reset --hard"命令取消暂存。否则,这些文件将被删除! - Ajoy
6
哇啊啊!!也许你需要强调一下“carefully”这个词。我刚看到了这三个单词“git reset --hard”,然后所有未提交的文件都……咻!不见了!!! - Vineeth Chitteti

2
如果您想取消暂存所有更改,请使用以下命令:
git reset --soft HEAD

如果您想要取消暂存的更改并从工作目录中还原它们,
git reset --hard HEAD

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