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

有时 git 建议使用git rm --cached取消git reset HEAD file文件,有时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
#

答案

git rm --cached <filePath> 不会 取消 转储文件,它实际上是分阶段从存储库中删除文件 (假设以前已经提交过),但是将文件留在工作树中(使您无法跟踪)文件)。

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

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

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

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

git reset HEAD file (默认情况下使用--mixed标志)是不同的,如果该文件已经在--mixed ,它将有效地用来自仓库(HEAD)的文件替换索引版本取消对它的修改

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

关于Why are there 2 ways to unstage a file in git?取消Why are there 2 ways to unstage a file in git? - 从来没有真正只有一种方法可以在 git 中执行任何操作。那就是它的美丽:)

很简单:

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

如果该文件之前未处于版本控制中(即,您正在对未初次使用git add ed 的文件进行分段操作),则这两个命令具有相同的效果,因此,它们的出现是 “两种方式做某事”。

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

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

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

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 stash
git stash pop

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

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

  • git reset HEAD <file>取消当前提交中的文件。
  • git rm --cached <file>还将取消git rm --cached <file>该文件以供将来提交。直到使用git add <file>再次git add <file>它为止,它都处于未登台状态。

还有一个重要的区别:

  • 运行后git rm --cached <file> ,并把你的分支到远程,任何人从远程拉你的分公司将充分利用自己的文件夹中实际删除文件,即使在您的本地工作仅有变成未被跟踪的文件(即不从文件夹中实际删除)。

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

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

git reset <file>

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

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 ...” 取消登台)

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

我很惊讶没有人提到 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 的回答。);)我希望它能对您有所帮助。

只需使用:

git reset HEAD <filename>

这会取消暂存文件并保留对它所做的更改,因此,如果需要,您可以依次更改分支,然后git add这些文件git add到另一个分支。保留所有更改。