为什么有两种在Git中取消暂存文件的方法?


1166

有时git建议git rm --cached取消暂存文件,有时git reset HEAD file。我什么时候应该使用哪个?

编辑:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

20
为什么?我说这是因为git的命令行界面是有机发展的,并且从未进行过重大的结构调整以使事情保持一致。(如果您不同意,请注意如何git rm既能阶段一个缺失,也unstage另外
罗马Starkov

3
@romkyns:我同意Git的界面有一些奇怪的地方,因为它是有机进化的,但是移除肯定是加法的反函数,所以rm取消逻辑不合逻辑add吗?您认为rm应该如何表现?
Zaz

6
您问题的唯一实际答案是,git init没有任何设置HEAD可重置。
Miles Rout


4
@Zaz,我会发表我的意见。 rm表示在unix上下文中删除。这与添加到索引并非相反。删除文件的功能不应与更改暂存状态的功能一起重载。如果有实现细节可以方便地进行组合,那么这仅表明git中缺少周到的抽象层,这将使可用性变得清晰。
约书亚·戈德堡

Answers:


1891

git rm --cached <filePath> 不会取消暂存文件,而是实际上暂存从存储库中删除文件(假设它之前已提交),但是将文件留在工作树中(留下未跟踪的文件)。

git reset -- <filePath>取消暂存给定文件的所有暂存更改。

就是说,如果您使用的git rm --cached是已暂存的新文件,则基本上看起来就像您刚刚暂存了该文件一样,因为它从未被提交过。

更新git 2.24
在此较新版本的git中,您可以使用git restore --staged代替git reset。参见git docs


70
我会说要git rm --cached取消暂存文件,但不会将其从工作目录中删除。
Pierre de LESPINAY 2012年

4
要删除已暂存的文件以便不再暂存,可以肯定地称为“取消暂存要添加的文件”,对吗?可以肯定的是,最终结果不是分阶段删除,因此我认为误解是完全可以理解的。
罗曼·斯塔科夫

4
因此,通常情况下,人们会意识到git rm --cached <filePath>某个文件从未存在于该仓库中之后用来从该仓库中删除一些文件:因此很可能运行此命令,然后将相关文件添加到中gitignore。我对么?
Adrien Be

13
有了如此多的问题和答案表决权,我想说的显然是我们希望在中发出unstage命令git
milosmns

4
“ git status”现在建议:使用“ git restore --staged <文件> ...”
取消登台

334

git rm --cached用于从索引中删除文件。如果文件已经在存储库中,git rm --cached则将从索引中删除该文件,将其保留在工作目录中,并且现在提交也将其从存储库中删除。基本上,提交后,您将取消版本化文件并保留本地副本。

git reset HEAD file(默认情况下使用该--mixed标志)(在文件已位于存储库中的情况下)有所不同,它将文件的索引版本替换为来自存储库(HEAD)的索引版本,从而有效地取消了对该文件的修改

对于未版本控制的文件,它将取消对整个文件的暂存,因为该文件不在HEAD中。在这方面git reset HEAD filegit rm --cached它们是相同的,但是它们不相同(如在存储库中已存在文件的情况中所述)

问题是Why are there 2 ways to unstage a file in git?-从来没有真正只有一种方法可以在git中做任何事情。那就是它的美丽:)


7
公认的答案和这个答案都很好,并说明了为什么要使用一个答案而另一个答案。但是他们没有直接回答git 为什么建议两种不同方法的隐式问题。在OP的示例的第一种情况下,刚刚完成了git init。在那种情况下,git建议使用“ git rm --cached”,因为那时存储库中没有提交,因此HEAD无效。“ git reset HEAD-a”产生:“致命:无法将'HEAD'解析为有效引用。”
sootsnoot

5
使用'git checkout',您是否会丢失对文件所做的所有更改?这与取消暂存文件不一样,除非我误会了。
John Deighan

there is never really only one way to do anything in git. that is the beauty of it-嗯...为什么?当只有一种明显的方法时,它总是很棒。这节省了我们很多时间和大脑记忆))
Oto Shavadze

128

很简单:

  • git rm --cached <file> 使git完全停止跟踪文件(与普通git rm* 不同,将其保留在文件系统中)
  • git reset HEAD <file> 自上次提交以来取消对该文件所做的任何修改(但不会在文件系统中还原它们,这与命令名可能建议的相反**)。该文件仍处于修订控制之下。

如果该文件之前未处于版本控制中(即,您正在对未初次编辑的文件进行暂存git add),则这两个命令具有相同的效果,因此,它们的外观为“两种方法”。

*请记住,@ DrewT在他的答案中提到了有关先前已提交到存储库git rm --cached的文件的注意事项。在这个问题的上下文中,对于刚刚添加但尚未提交的文件,没有什么可担心的。

**由于它的名称,我很害怕长时间无法使用git reset命令-直到今天,我仍然经常查找语法以确保自己不会搞砸。(更新:我最终花时间总结git reset了tldr页面中的用法,因此现在我有了一个更好的工作原理心智模型,并在我忘记一些细节时提供了快速参考。)


git rm <file> --cached
neonmate 2015年

8
我真的不认为2015年8月4日对这个答案的修改是一个整体改进。它可能具有固定的技术正确性(我不认为有资格对此进行评估),但恐怕它引入了诸如“取消了必须开始跟踪当前未跟踪文件的命令”之类的语言,从而使答案的语气难以获得。 ”,并使用“ index”和“ HEAD”之类的专业术语,正是这种东西吓跑了初学者。如果有人可以,请进行编辑以恢复更适合新手的语言。
waldyrious 2015年

5
同意@waldyrious。最初的答案可能并非直接来自git教科书,但它在足够的技术水平上回答了该问题。技术细节应该在注释中阐明,而不是作为掩盖原始意图的编辑。
西蒙·罗布

我已经还原了修改。我相信社区(在先前的评论和它们的投票中)已经有足够的验证,认为该编辑不利于答案的清晰性。
waldyrious

注意@DrewT警告说,如果使用rm --cached和推入,则拉同一个分支的任何人实际上都将从其工作树中删除该文件。
汤姆·黑尔

53

这个线程有点旧,但是我仍然想添加一些示范,因为它仍然不是一个直观的问题:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(不带-q)会给出有关已修改文件的警告,并且其退出代码为1,这将被视为脚本中的错误。

编辑:git checkout HEAD to-be-modified to-be-removed也可用于暂存,但将从工作区中完全删除更改

更新git 2.23.0:有时,命令会更改。现在,git status说:

  (use "git restore --staged <file>..." to unstage)

...适用于所有三种类型的变更


谢谢,从前两个答案(可能是我对术语的无知)中还不清楚,git reset将修改保留在本地(而不是git checkout可以将它们还原)。
13年

你应该把警告绕版本开始时,因为旧版本的新版本删除文件
路易斯·毛里西奥·

@LuisMauricio哪个命令删除文件?
Daniel Alder

36

如果您意外地暂存了不想提交的文件,并且想确定要保留更改,则还可以使用:

git stash
git stash pop

这将重置为HEAD并重新应用您的更改,从而使您可以重新暂存各个文件以进行提交。如果您忘记为拉取请求(git stash ; git checkout -b <feature> ; git stash pop)创建功能分支,这也很有用。


3
这是一个干净的解决方案,比键入“ git rm”更
省心

1
git stash还有其他相关的好处,因为它在reflog中创建了条目,以后可以使用。如有疑问,请继续执行操作git stash(例如git stash save -u "WIP notes to self"(“ -u”将在隐藏提交中包括任何新的/未跟踪的文件)...然后尝试git reflog show stash查看隐藏提交及其sha的列表。我建议您使用一个shell别名alias grs="git reflog show stash"
-–cweekly

15

如果有问题的文件已经在存储库中并且处于版本控制下(先前已提交等),则这两个命令会有一些细微的差异:

  • git reset HEAD <file> 在当前提交中取消暂存文件。
  • git rm --cached <file>还将取消暂存文件以供将来提交。它是未登台的,直到使用再次添加git add <file>

还有一个重要的区别:

  • 跑步后 git rm --cached <file>,并把你的分支到远程,任何人从远程拉你的分公司将得到的文件ACTUALLY从文件夹中删除,即使与你的工作仅有变得未经跟踪(即未从文件夹中删除)的文件。

最后一个差异对于包含配置文件的项目非常重要,该文件中团队中的每个开发人员都具有不同的配置(即,不同的基本url,ip或端口设置),因此,如果您使用的git rm --cached <file>是拉您分支的任何人,则必须手动重新创建配置,或者您可以将配置发送给他们,他们可以将其重新编辑回其IP设置(等),因为删除仅会影响人们从远程拉您的分支。


10

假设您stage通过进入了整个目录git add <folder>,但是您想从暂存列表(即运行时生成的列表git status)中排除文件,并将修改保留在排除的文件中(您正在处理某些内容,但尚未准备好提交,但是您不想失去工作...)。您可以简单地使用:

git reset <file>

当您运行git status,你会看到,无论文件()你resetunstaged,你的文件的剩余部分added仍然在staged列表中。


10

1。

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(使用“ git rm --cached ...”取消登台)

  • git是一个指针系统

  • 您还没有提交将指针更改为

  • “从指向的存储桶中取出文件 ”的唯一方法是删除您告诉git监视更改的文件

2。

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

git commit -ma

  • 您提交了“已保存

3。

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(使用“ git reset HEAD ...”取消登台)

  • 您此时在代码中进行了提交
  • 现在您可以将指针重置为提交' 恢复为上一次保存 '

1
实际上,这是正确回答问题的唯一答案。它实际上回答了这个问题,不是“ git rm --cached”和“ git reset HEAD”之间有什么区别,而是“为什么git不一致地将这两个都作为选项?”,答案是没有HEAD可以重置到git init第一次的时候。
Miles Rout

5

我很惊讶没有人提到git reflog(http://git-scm.com/docs/git-reflog):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

reflog是一个git历史记录,不仅可以跟踪对仓库的更改,还可以跟踪用户操作(例如,拉出,签出到其他分支等),并允许撤消这些操作。因此,您可以还原到未暂存文件的位置,而不是取消暂存错误暂存的文件。

这类似于,git reset HEAD <file>但在某些情况下可能更精细。

抱歉-并没有真正回答您的问题,只是为我经常使用的文件降级提供了另一种方式(我非常喜欢Ryan Stewart和waldyrious的回答。);)我希望它能对您有所帮助。


5

只需使用:

git reset HEAD <filename>

这会取消登台文件并保留对它所做的更改,因此,您可以根据需要依次更改分支,并将git add这些文件更改到另一个分支。保留所有更改。


3

在我看来,git rm --cached <file>从索引中删除文件而不是将文件从普通目录git rm <file>同时rm <file>删除的目录中删除,就像操作系统会从目录中删除文件而不删除其版本控制一样。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.