题 如何在提交前撤消'git add'?


我使用命令错误地将文件添加到git:

git add myfile.txt

我还没跑 git commit。有没有办法撤消这个,所以这些文件不会包含在提交中?


到目前为止有48个答案(有些已删除)。除非您有一些新信息,否则请不要添加新的。


7464
2017-12-07 21:57


起源


从Git v1.8.4开始,使用下面的所有答案 HEAD 要么 head现在可以用了 @ 代替 HEAD 代替。看到 这个答案(最后一节) 了解为什么你能做到这一点。
我做了一个小小的总结,展示了取消暂存文件的所有方法: stackoverflow.com/questions/6919121/... - Daniel Alder
为什么不git checkout? - Erik Reppen
@ErikReppen git checkout 不会从提交索引中删除分阶段更改。它只会将未上传的更改恢复为上次提交的修订 - 顺便说一句,这也不是我想要的,我希望这些更改,我只是想在以后的提交中使用它们。 - paxos1977
如果您使用Eclipse,就像在提交对话框中取消选中文件一样简单 - Hamzahfrq


答案:


你可以撤消 git add 在提交之前

git reset <file>

这将从当前索引(“即将提交”列表)中删除它而不更改任何其他内容。

您可以使用

git reset

没有任何文件名来取消所有应有的更改。当在合理的时间内逐个列出太多文件时,这可以派上用场。

在旧版本的Git中,上述命令相当于 git reset HEAD <file> 和 git reset HEAD 分别,如果,将失败 HEAD 未定义(因为您尚未在您的仓库中进行任何提交)或模糊(因为您创建了一个名为的分支) HEAD,这是一件你不应该做的蠢事。这个 在Git 1.8.2中被改变了但是,在现代版本的Git中,您甚至可以在第一次提交之前使用上述命令:

用于错误输出的“git reset”(没有选项或参数)      你在历史上没有任何提交,但它现在给你了      一个空索引(用于匹配您甚至不在的不存在的提交)。


8379
2017-12-07 22:30



当然,这不是真正的撤销,因为如果错了 git add 覆盖了以前上演的uncommited版本,我们无法恢复它。我试着在下面的答案中澄清这一点。 - leonbloy
git reset HEAD *.ext 哪里 ext 是您要取消添加的给定扩展名的文件。对我来说是 *.bmp & *.zip - boulder_ruby
@Jonny,索引(又名临时区域)包含 所有 文件,而不仅仅是更改的文件。它“启动生命”(当您签出提交或克隆回购时)作为HEAD指向的提交中的所有文件的副本。所以,如果你 去掉 索引中的文件(git rm --cached)这意味着你正准备做出承诺 删除 那个文件。 git reset HEAD <filename> 另一方面,将文件从HEAD复制到索引,以便下一次提交不会显示对该文件所做的任何更改。 - Wildcard
我刚刚发现有一个 git reset -p 就像 git add -p。这太棒了! - donquixote
你真的 可以恢复覆盖以前上演但未完成的更改 但不是以用户友好的方式而不是100%安全(至少没有我找到):goto .git / objects,搜索当时创建的文件 git add 你想恢复(61/3AF3...  - >对象id 613AF3...), 然后 git cat-file -p <object-id> (可能值得恢复几个小时的工作,但也经常上课......) - Peter Schneider


你要:

git rm --cached <added_file_to_undo>

推理:

当我是新手时,我首先尝试过

git reset .

(撤消我的整个初始添加),只是为了得到这个(不那么)有用的消息:

fatal: Failed to resolve 'HEAD' as a valid ref.

事实证明,这是因为HEAD ref(branch?)直到第一次提交后才存在。也就是说,如果你的工作流程像我一样,你会遇到和我一样的初学者问题:

  1. cd到我伟大的新项目目录,尝试Git,新的热点
  2. git init
  3. git add .
  4. git status

    ......很多废话卷轴......

    =>该死,我不想加上所有这些。

  5. 谷歌“撤消git添加”

    =>查找Stack Overflow - yay

  6. git reset .

    =>致命:无法将'HEAD'解析为有效的参考号。

它进一步证明了存在 记录了一个错误 反对邮件列表中的这种无益。

并且正确的解决方案就在Git状态输出中(是的,我把它作为'废话'掩盖了)

...
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
...

而解决方案确实是使用 git rm --cached FILE

注意这里其他地方的警告 - git rm 删除文件的本地工作副本,但是  如果你使用 --cached。这是结果 git help rm

--cached       使用此选项仅从索引中取消暂存和删除路径。       将保留工作树文件,无论是否已修改。

我继续使用

git rm --cached .

删除所有内容并重新开始。虽然没有工作,因为虽然 add . 结果是递归的 rm 需求 -r 递减。叹。

git rm -r --cached .

好的,现在我回到了我开始的地方。下次我要去使用 -n 做一个干运行,看看会添加什么:

git add -n .

在信任之前,我把所有东西都拉到了一个安全的地方 git help rm 有关 --cached 不破坏任何东西(如果我拼错它会怎么样)。


1954
2018-03-25 16:20



哈。我遵循了同样的过程。除了我放弃并说 rm -rf .git, git init 因为我不信任 git rm --cached 保留我的工作副本。它说了一些git在某些地方仍然过于复杂的说法。 git unstage 应该只是一个股票标准命令,我不在乎我是否可以将其添加为别名。 - Adrian Macneil
对我来说,git说 git reset HEAD <File>... - drahnr
git rm --cached <file>实际上是正确的答案,如果它是<file>初始导入到存储库中。如果您尝试取消对文件的更改,git reset是正确的答案。人们说这个答案是错误的,他们正在考虑一个不同的问题。 - Barry Kelly
这实际上会起作用,但是 只要 在第一次提交时,文件之前不存在,或者在哪里 git add 命令添加了新文件, 但不是 对现有文件的更改。 - naught101
只是为了表明git是多么不直观和错综复杂。而不是使用并行“撤消”命令,你必须找到如何撤消它们。就像试图用快速沙子释放你的腿,然后让你的手臂卡住,然后让你的另一只手臂卡住...每个命令都应该通过GUI完成,下拉菜单项可以选择...想想所有的UI,我们已经拥有的生产力提升,但我们有一个复古的命令行界面。它不像git GUI程序使这更直观。 - ahnbizcad


如果您输入:

git status

git会告诉你什么是上演的,包括如何取消演出的说明:

use "git reset HEAD <file>..." to unstage

我发现git在推动我在这样的情况下做正确的事情方面做得非常好。

注意:最近的git版本(1.8.4.x)已更改此消息:

(use "git rm --cached <file>..." to unstage)

484
2017-12-07 23:22



消息将根据是否不同而不同 added文件已被跟踪( add 只保存了一个新版本到缓存 - 这里它将显示您的消息)。在其他地方,如果文件之前没有暂存,则会显示 use "git rm --cached <file>..." to unstage - leonbloy
大!该 git reset HEAD <file> 一个是唯一一个可以在你想要取消暂存文件删除的情况 - skerit
我的git版本2.14.3说 git reset HEAD 去舞台。 - seaturtle


澄清: git add 将更改从当前工作目录移动到 集结区 (指数)。

这个过程被称为 分期。所以最自然的命令 阶段 更改(更改的文件)是显而易见的:

git stage

git add 只是更容易键入别名 git stage

可惜没有 git unstage 也不 git unadd 命令。相关的一个更难猜测或记住, 但很明显:

git reset HEAD --

我们可以轻松地为此创建一个别名:

git config --global alias.unadd 'reset HEAD --'
git config --global alias.unstage 'reset HEAD --'

最后,我们有了新的命令:

git add file1
git stage file2
git unadd file2
git unstage file1

我个人使用更短的别名:

git a #for staging
git u #for unstaging

220
2017-09-10 20:28



“动作”?这表明它已从工作目录中删除。事实并非如此。 - Thomas Weller
为什么这很明显? - Lenar Hoyt


对于已接受的答案的补充,如果您错误添加的文件很大,您可能会注意到,即使在将其从索引中删除后也是如此git reset',它似乎仍占据着空间 .git 目录。这没什么可担心的,文件确实仍在存储库中,但只是作为“松散对象”,它不会被复制到其他存储库(通过克隆,推送),并且该空间最终将被回收 - 尽管也许不是很快。如果您感到焦虑,可以运行:

git gc --prune=now

更新 (接下来是我试图澄清最多投票答案可能引起的混淆):

那么,这才是真实的 解开 的 git add

git reset HEAD <file> ?

要么

git rm --cached <file>

严格地说,如果我没弄错的话: 没有

git add  无法撤消  - 一般来说,安全。

让我们先回顾一下 git add <file> 实际上:

  1. 如果 <file> 是 以前没有跟踪git add  将其添加到缓存中,以其当前的内容。

  2. 如果 <file> 是 已被跟踪git add  保存当前内容 (快照,版本)到缓存。在GIT中,仍然会调用此操作 ,(不仅仅是 更新 因为文件的两个不同版本(快照)被视为两个不同的项目:因此,我们确实在缓存中添加了一个新项目,最终将在稍后提交。

鉴于此,问题有点含糊:

我错误地使用命令添加文件...

OP的场景似乎是第一个(未跟踪文件),我们希望“撤消”从被跟踪的项目中删除文件(而不仅仅是当前内容)。 如果 情况就是这样,然后运行就可以了 git rm --cached <file>

我们也可以跑 git reset HEAD <file>。这通常是可取的,因为它适用于两种情况:当我们错误地添加已经跟踪的项目的版本时,它也会执行撤消操作。

但有两点需要注意。

第一:有(如答案中所指出的)只有一种情况 git reset HEAD 不起作用,但是 git rm --cached 确实:一个新的存储库(没有提交)。但是,实际上,这是一个几乎无关紧要的案例。

第二:要注意这一点 git reset HEAD  不能神奇地恢复以前缓存的文件内容,它只是从HEAD重新同步它。如果我们误入歧途 git add 覆盖了以前上演的未提交版本,我们无法恢复它。这就是为什么,严格来说,我们无法撤消。

例:

$ git init
$ echo "version 1" > file.txt
$ git add file.txt   # first add  of file.txt
$ git commit -m 'first commit'
$ echo "version 2" > file.txt
$ git add  file.txt   # stage (don't commit) "version 2" of file.txt
$ git diff --cached file.txt
-version 1
+version 2
$ echo "version 3" > file.txt   
$ git diff  file.txt
-version 2
+version 3
$ git add  file.txt    # oops we didn't mean this
$ git reset HEAD file.txt  # undo ?
$ git diff --cached file.txt  # no dif, of course. stage == HEAD
$ git diff file.txt   # we have lost irrevocably "version 2"
-version 1
+version 3

当然,如果我们只是按照通常的懒惰工作流程来执行'git add'仅用于添加新文件(案例1),并且我们通过提交更新新内容,这不是非常关键的, git commit -a 命令。


137
2018-05-18 18:05



严格来说,有一种方法可以恢复已经被git add替换的已经暂存的文件。正如你所提到的,git add为该文件创建了一个git对象,它不仅在完全删除文件时,而且在被新内容覆盖时将成为一个松散的对象。但是没有命令可以自动恢复它。相反,必须手动识别和提取文件,或者使用仅为此案例编写的工具(libgit2允许这样)。但是,如果文件非常重要且很大,并且无法通过编辑以前的版本来重建,那么这只会付出代价。 - Johannes Matokic
纠正自己:找到松散的目标文件后(使用创建日期/时间等元数据) git cat-file 可以用来恢复其内容。 - Johannes Matokic
另一种方式 恢复已暂存但未提交然后覆盖的更改 通过例如另一个 git add 是通过 git fsck --unreachable 这将列出所有无法访问的obj,然后您可以检查它 git show SHA-1_ID 要么 git fsck --lost-found 那将>将悬空物体写入 .git/lost-found/commit/ 要么 .git/lost-found/other/,取决于类型。也可以看看 git fsck --help - iolsmit


git rm --cached . -r

将以递归方式“取消添加”您从当前目录添加的所有内容


85
2017-12-09 21:19



我不打算取消添加所有内容,只需添加一个特定文件。 - paxos1977
如果您之前没有任何提交,也会很有帮助。如果没有先前的提交, git reset HEAD <file> 会说 fatal: Failed to resolve 'HEAD' as a valid ref. - Priya Ranjan Singh
没有这个 增加 一个 缺失 当前目录中的所有内容。只是取消暂停更改非常不同。 - Mark Amery


git gui

并手动删除所有文件或选择所有文件并单击 从提交中删除 按钮。


77
2017-10-12 01:12



是的,我理解。我只想隐含地建议你在答案上表明“你可以使用 git-gui....“:) - Alexander Suraphel
它说,“git-gui:命令未找到”。我不确定这是否有效。 - Parinda Rajapaksha


Git拥有可以想象的每一个动作的命令,但需要广泛的知识来使事情正确,因此它最好是反直觉的......

你之前做过的事:

  • 更改了文件并使用了 git add ., 要么 git add <file>

你想要什么:

  • 从索引中删除该文件,但保留其版本并留下工作副本中未提交的更改:

    git reset head <file>
    
  • 将文件重置为HEAD的最后一个状态,撤消更改并将其从索引中删除:

    # Think `svn revert <file>` IIRC.
    git reset HEAD <file>
    git checkout <file>
    
    # If you have a `<branch>` named like `<file>`, use:
    git checkout -- <file>
    

    这是必要的 git reset --hard HEAD 不适用于单个文件。

  • 去掉 <file> 从索引和版本控制,保持未版本化文件的工作副本更改:

    git rm --cached <file>
    
  • 去掉 <file> 完全从工作副本和版本控制:

    git rm <file>
    

73
2018-03-29 11:14



我无法忍受'git reset head <file>'和'git rm --cached <file>的区别。你能解释一下吗? - jeswang
@jeswang文件要么是git'已知',要么跟踪它们中的变化。或者它们不是'版本化'。 reset head 撤消当前的更改,但git仍在监视该文件。 rm --cached 将文件从版本控制中取出,因此git不再检查它的更改(并且还删除最终索引的当前更改,通过先前的git告诉它 add),但更改的文件将保存在您的工作副本中,即在HDD上的文件夹中。 - sjas
不同的是 git reset HEAD <file> 是临时的 - 该命令将仅应用于下一个提交,但是 git rm --cached <file> 将取消暂停,直到它再次添加 git add <file>。也, git rm --cached <file> 意味着如果您将该分支推送到远程,任何拉动分支的人都将从其文件夹中删除该文件。 - DrewT


问题没有明确提出。原因是 git add 有两个含义:

  1. 添加一个 新文件 到暂存区,然后撤消 git rm --cached file
  2. 添加一个 改性 将文件归档到暂存区域,然后撤消 git reset HEAD file

如果有疑问,请使用

git reset HEAD file

因为它在两种情况下都做了预期的事情。

警告: 如果你这样做 git rm --cached file 在一个文件上 改性 (以前存储在存储库中的文件),然后将删除该文件 git commit!它仍然存在于您的文件系统中,但如果有其他人提取您的提交,该文件将从其工作树中删除。

git status 会告诉你文件是否是 新文件 要么 改性

On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   my_new_file.txt
    modified:   my_modified_file.txt

62
2018-01-16 19:54



+1。在这个页面上出现了大量高度评价的答案和评论 git rm --cached somefile。我希望这个答案能够在页面上显示出一个突出的位置,它可以保护新手免受所有虚假声明的误导。 - Mark Amery


解开 已添加的文件非常容易使用 混帐,用于重置 myfile.txt 已添加,使用:

git reset HEAD myfile.txt

说明:

在您上传不需要的文件后,要撤消,您可以这样做 git resetHead 是本地文件的头部,最后一个参数是文件的名称。

我将在下面的图片中为您创建更多详细信息,包括在这些情况下可能发生的所有步骤:

git reset HEAD


60
2018-06-28 10:43





如果您正在进行初始提交并且无法使用git reset,只需声明“Git破产”并删除.git文件夹并重新开始


55
2017-11-19 16:39



一个提示是在删除文件夹之前复制.git / config文件(如果已添加远程源)。 - Tiago
@ChrisJohnsen发表评论。有时,您希望提交除一个以外的所有文件: git add -A && git rm --cached EXCLUDEFILE && git commit -m 'awesome commit'  (当没有先前的提交时,这也有效 Failed to resolve 'HEAD' 问题) - Barry