为啥有两种方法可以在 Git 中取消暂存文件?

Posted

技术标签:

【中文标题】为啥有两种方法可以在 Git 中取消暂存文件?【英文标题】:Why are there two ways to unstage a file in Git?为什么有两种方法可以在 Git 中取消暂存文件? 【发布时间】:2011-10-18 15:32:05 【问题描述】:

有时 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
#

【问题讨论】:

为什么?我会说这是因为 git 的命令行界面是有机发展的,并且从未进行过重大重组以使事情保持一致。 (如果您不同意,请注意git rm 如何stage deletionunstage addition @romkyns:我同意 Git 的界面有几个奇怪之处,因为它是有机进化的,但删除肯定是加法的反函数,所以 rm 撤消 @987654327 不合逻辑@?你认为rm 应该如何表现? 您的问题的唯一实际答案是,在 git init 之后没有要重置的 HEAD 最好的文档:help.github.com/articles/changing-a-remote-s-url @Zaz,我会给出我的意见。 rm 表示在 unix 上下文中删除。这与添加到索引中并不相反。删除文件的函数不应被用于更改暂存状态的函数重载。如果有实现细节可以方便地组合,那只是表明 git 缺乏深思熟虑的抽象层,这将使可用性变得清晰。 【参考方案1】:

git rm --cached &lt;filePath&gt; 不会取消暂存文件,它实际上是暂存文件从存储库中删除(假设它之前已经提交)但离开工作树中的文件(留下未跟踪的文件)。

git reset -- &lt;filePath&gt;取消暂存给定文件的任何暂存更改。

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

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

【讨论】:

我会说 git rm --cached 取消暂存文件但不会将其从工作目录中删除。 删除暂存待添加的文件,使其不再暂存,这肯定可以称为“取消暂存待添加的文件”,对吗?最终结果是不是分阶段删除,这是肯定的,因此我认为误解是完全可以理解的。 所以通常情况下,人们会使用git rm --cached &lt;filePath&gt; 从存储库中删除一些文件意识到它不应该在存储库中:所以很可能运行这个命令 & 然后将相关文件添加到gitignore。我说的对吗? 问题和答案都有这么多投票,我想说显然我们希望在git 中有一个unstage 命令。 "git status" 现在建议:使用 "git restore --staged ..." 取消暂存【参考方案2】:

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

git reset HEAD file(默认使用--mixed标志)的不同之处在于,如果文件已经在repo中,它将文件的索引版本替换为repo中的版本(HEAD) ,有效地取消对它的修改

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

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

【讨论】:

接受的答案和这个都很棒,并解释为什么你会使用一个与另一个。但他们没有直接回答 为什么 git 建议两种不同方法的隐含问题。在 OP 示例的第一种情况下,刚刚完成了 git init。在这种情况下,git 建议使用“git rm --cached”,因为此时存储库中没有提交,因此 HEAD 无效。 “git reset HEAD --a”产生:“致命:无法将'HEAD'解析为有效的参考。” 使用 'git checkout',您不会丢失您对文件所做的所有更改吗?这与取消暂存文件不同,除非我有误解。 there is never really only one way to do anything in git. that is the beauty of it - 嗯……为什么?当只有一种明显的方法时,它总是很棒。这节省了我们大量的时间和大脑记忆))【参考方案3】:

这个帖子有点老了,不过还是想加个小示范,因为还不是直观的问题:

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 会还原它们)。 您应该在版本的开头加上警告,因为旧版本会删除新版本中的文件 @DanielAlder sry,我刚刚重新测试,它没有删除,我的错误。【参考方案4】:

在我看来,git rm --cached &lt;file&gt; 会从索引中删除文件,而不会将其从普通 git rm &lt;file&gt; 会同时执行这两种操作的目录中删除,就像操作系统 rm &lt;file&gt; 会从目录中删除文件而不删除一样它的版本控制。

【讨论】:

【参考方案5】:

如果有问题的文件已经在 repo 中并且在版本控制下(以前提交等),这 2 个命令会有一些细微的差别:

git reset HEAD &lt;file&gt; 在当前提交中取消暂存文件。 git rm --cached &lt;file&gt; 也将取消暂存文件以供将来提交。在使用 git add &lt;file&gt; 再次添加之前,它是未暂存的。

还有一个更重要的区别:

在运行 git rm --cached &lt;file&gt; 并将您的分支推送到远程之后,任何从远程拉取您的分支的人都会从他们的文件夹中删除文件实际上,即使在您的本地工作集中该文件只是变为未跟踪(即未从文件夹中物理删除)。

最后一个区别对于包含配置文件的项目很重要,其中团队中的每个开发人员都有不同的配置(即不同的基本 url、ip 或端口设置),所以如果你使用git rm --cached &lt;file&gt; 任何拉你分支的人将不得不手动重新创建配置,或者您可以将您的配置发送给他们,他们可以将其重新编辑回他们的 ip 设置(等),因为删除只会影响人们从远程拉动您的分支。

【讨论】:

【参考方案6】:

很简单:

git rm --cached &lt;file&gt; 使 git 完全停止跟踪文件(将其留在文件系统中,不像普通的 git rm*) git reset HEAD &lt;file&gt; 取消暂存自上次提交以来对文件所做的任何修改(但不会在文件系统中恢复它们,这与命令名称可能暗示的内容相反**)。该文件仍处于修订控制之下。

如果该文件之前不在修订控制中(即您第一次取消暂存一个刚刚git added 的文件),那么这两个命令具有相同的效果,因此这些命令的外观是“做某事的两种方式”。

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

** 因为它的名字,我很害怕使用 git reset 命令——直到今天我仍然经常查看语法以确保我不会搞砸。 (更新:我终于抽出时间来summarize the usage of git reset in a tldr page,所以现在我对它的工作原理有了一个更好的心理模型,并且当我忘记一些细节时可以快速参考。)

【讨论】:

我是git rm &lt;file&gt; --cached 我真的不认为这个答案的edit of Aug 4 2015 是一个整体的改进。它可能具有固定的技术正确性(我觉得没有资格评估它),但我担心它通过引入诸如“取消开始跟踪当前未跟踪文件的必要性”之类的语言使答案的基调变得更难理解”,并使用诸如“索引”和“头”之类的行话,正是那种吓跑初学者的东西。如果有人可以,请编辑以恢复对新手更友好的语言。 同意@waldyrious。最初的答案可能不是直接来自 git 教科书,但它以足够的技术水平回答了这个问题。技术细节应该在 cmets 中得到澄清,而不是掩盖原始意图的编辑。 我已恢复编辑。我相信社区(在之前的 cmets 和对他们的投票中)已经充分验证了编辑不利于答案的清晰度。 注意@DrewT 警告说,如果使用rm --cached 并推送,任何拉同一分支的人都会将文件从他们的工作树中实际删除。【参考方案7】:

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

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

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

这类似于git reset HEAD &lt;file&gt;,但在某些情况下可能更细化。

抱歉 - 并没有真正回答您的问题,而只是指出了另一种方式来取消我经常使用的文件(我非常喜欢 Ryan Stewart 和 waldyrious 的回答。);) 我希望它有所帮助。

【讨论】:

【参考方案8】:

如果您不小心暂存了您不想提交的文件,并希望确保保留更改,您也可以使用:

git stash
git stash pop

这会重置 HEAD 并重新应用您的更改,从而允许您重新暂存单个文件以进行提交。如果您忘记为拉取请求创建功能分支,这也很有帮助 (git stash ; git checkout -b &lt;feature&gt; ; git stash pop)。

【讨论】:

这是一个干净的解决方案,比输入“git rm”更不用担心 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"【参考方案9】:

假设您通过git add &lt;folder&gt; 访问整个目录stage,但您想从暂存列表(即运行git status 时生成的列表)中排除一个文件并保留排除文件中的修改(您正在处理某事并且尚未准备好提交,但您不想丢失您的工作......)。你可以简单地使用:

git reset &lt;file&gt;

当您运行git status 时,您将看到您reset 的任何文件都是unstaged,而您added 的其余文件仍在staged 列表中。

【讨论】:

idk 为什么其他人都喜欢这么复杂。这是一个很好的答案。【参考方案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 -m a

你提交了,'保存'

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 ...”取消暂存)

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

【讨论】:

这实际上是唯一正确回答问题的答案,IMO。它实际上回答了这个问题,这不是'git rm --cached'和'git reset HEAD'之间的区别是什么,而是'为什么git不一致地将两者都作为选项?',答案是没有HEAD可以重置当你第一次git init 时。【参考方案11】:

只需使用:

git reset HEAD &lt;filename&gt;

这会取消暂存文件并保留您对其所做的更改,因此您可以反过来根据需要更改分支并将git add 这些文件转移到另一个分支。保留所有更改。

【讨论】:

【参考方案12】:

仅适用于 2.23 及以上版本,

除了这些建议,您可以使用 git restore --staged &lt;file&gt; 以便 unstage 文件。

【讨论】:

它适用于--stage--staged 这两个选项。【参考方案13】:

在 > 2.2 的较新版本中,您可以使用git restore --staged &lt;file_name&gt;。 注意这里 如果您想一次取消暂存(移动到更改)您的文件,您可以使用上面的命令和您的文件名。例如

git restore --staged abc.html

现在,如果您想一次取消暂存所有文件,可以执行以下操作

git restore --staged .

请注意空格和点 (.),表示考虑暂存所有文件。

【讨论】:

以上是关于为啥有两种方法可以在 Git 中取消暂存文件?的主要内容,如果未能解决你的问题,请参考以下文章

为啥有两种方法可以在 Git 中取消暂存文件?

为啥有两种方法可以在 Git 中取消暂存文件?

为啥有两种方法可以在 Git 中取消暂存文件?

JGit Java Git 库取消暂存文件

在 Git 中重置所有暂存文件的最简单方法是啥?

SourceThree的简单使用