Git 中如何在提交之前撤消 “git add” 操作?

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

git add myfile.txt

我还没有运行git commit。如何撤消此操作以便这些更改不会包含在我的 Git 提交中?

4730177 次浏览

撤消git add未提交的更改:

git reset <file>

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


取消所有文件的所有更改:

git reset

在旧版本的Git中,上述命令分别等效于git reset HEAD <file>git reset HEAD,如果HEAD未定义(因为您还没有在存储库中进行任何提交)或模棱两可(因为您创建了一个名为HEAD的分支,这是您不应该做的愚蠢事情),则会失败。不过,这是在Git 1.8.2中更改,所以在现代版本的Git中,您甚至可以在进行第一次提交之前使用上述命令:

“git重置”(没有选项或参数)用于在以下情况下出错您的历史中没有任何提交,但它现在为您提供一个空索引(匹配你甚至不存在的不存在的提交)。

文档:git重置

如果您键入:

git status

Git会告诉你什么是暂存的,等等,包括如何取消暂存的说明:

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

我发现Git在推动我在这种情况下做正确的事情方面做得很好。

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

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

您想要:

git rm --cached <added_file_to_undo>

推理:

当我还是新手的时候我第一次尝试

git reset .

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

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

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

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

    …一堆垃圾…

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

  5. google"撤消git add"

    =>找到堆栈溢出-yay

  6. git reset .

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

事实证明,在邮件列表中,有bug记录反对这种无益。

正确的解决方案就在Git状态输出中(是的,我把它掩盖为“废话”)

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

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

注意此处其他地方的警告-git rm删除文件的本地工作副本,但如果您使用--缓存,则删除没有。这是git help rm的结果:

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

我继续使用

git rm --cached .

删除所有内容并重新开始。但不起作用,因为虽然add .是递归的,但事实证明rm需要-r来递归。唉。

git rm -r --cached .

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

git add -n .

我把所有东西都压缩到一个安全的地方,然后相信git help rm不会破坏任何东西(如果我拼写错误怎么办)。

要从暂存区域中删除新文件(并且仅在新文件的情况下),如上所述:

git rm --cached FILE

仅对意外添加的新文件使用rm--cache。

也许自从你发布你的问题以来,Git已经发展了。

$> git --versiongit version 1.6.2.1

现在,您可以尝试:

git reset HEAD .

这应该是你正在寻找的。

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

git rm --cached . -r

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

只要键入git reset,它就会恢复,就像你上次提交后从未键入git add .一样。确保你以前提交过。

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

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

git stage

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

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

git reset HEAD --

我们可以很容易地为此创建一个别名:

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

最后,我们有新的命令:

git add file1git stage file2git unadd file2git unstage file1

我个人使用更短的别名:

git a # For staginggit u # For unstaging

根据许多其他答案,您可以使用git reset

但是:

我发现这个很棒的小帖子实际上为git unadd添加了Git命令(嗯,别名):有关详细信息,请参阅git unadd或…

简单地说,

git config --global alias.unadd "reset HEAD"

现在你可以了

git unadd foo.txt bar.txt

替代/直接:

git reset HEAD foo.txt bar.txt

除了接受的答案之外,如果您错误添加的文件很大,您可能会注意到,即使在使用“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中,这个动作仍然被称为add,(不仅仅是更新它),因为文件的两个不同版本(快照)被视为两个不同的项目:因此,我们确实在向缓存中添加一个新项目,最终将在稍后提交。

有鉴于此,这个问题有点模棱两可:

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

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 irrevocably lost "version 2"-version 1+version 3

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


*(编辑:上面的内容实际上是正确的,但是仍然可以有一些稍微有点老套/复杂的方法来恢复已经上演但未提交然后被覆盖的更改-请参阅Johannes Matokic和iolsmit的评论)

当你开始一个新项目时,这里有一个方法可以避免这个棘手的问题:

  • 为您的新项目创建主目录。
  • 运行git init
  • 现在创建一个. gitignore文件(即使它是空的)。
  • 提交您的. gitignore文件。

如果你没有任何提交,Git会使git reset变得非常困难。如果你只是为了有一个而创建一个小的初始提交,之后你可以git add -Agit reset多次,只要你想,以使一切正确。

此方法的另一个优点是,如果您以后遇到行尾问题并需要刷新所有文件,则很容易:

  • 检查初始提交。这将删除您的所有文件。
  • 然后再次检查您最近的提交。这将使用您当前的行尾设置检索文件的新副本。

运行

git gui

并手动删除所有文件,或者选择所有文件并单击取消提交按钮。

请注意,如果您未能指定修订版本,则必须包含分隔符。我的控制台中的示例:

git reset <path_to_file>fatal: ambiguous argument '<path_to_file>': unknown revision or path not in the working tree.Use '--' to separate paths from revisions
git reset -- <path_to_file>Unstaged changes after reset:M    <path_to_file>

(Git版本1.7.5.4)

使用git add -i从即将提交的文件中删除刚刚添加的文件。例子:

添加您不想要的文件:

$ git add foo$ git status# On branch master# Changes to be committed:#   (use "git reset HEAD <file>..." to unstage)##       new file:   foo## Untracked files:#   (use "git add <file>..." to include in what will be committed)# [...]#

进入交互式add以撤消您的add(在这里git键入的命令是“r”(恢复),“1”(列表恢复显示的第一个条目),“back”退出恢复模式,以及“q”(退出):

$ git add -istaged     unstaged path1:        +1/-0      nothing foo
*** Commands ***1: [s]tatus     2: [u]pdate     3: [r]evert     4: [a]dd untracked5: [p]atch      6: [d]iff       7: [q]uit       8: [h]elpWhat now> rstaged     unstaged path1:        +1/-0      nothing [f]ooRevert>> 1staged     unstaged path* 1:        +1/-0      nothing [f]ooRevert>>note: foo is untracked now.reverted one path
*** Commands ***1: [s]tatus     2: [u]pdate     3: [r]evert     4: [a]dd untracked5: [p]atch      6: [d]iff       7: [q]uit       8: [h]elpWhat now> qBye.$

就是这样!这是你的证据,表明“foo”又回到了未跟踪列表中:

$ git status# On branch master# Untracked files:#   (use "git add <file>..." to include in what will be committed)# [...]#       foonothing added to commit but untracked files present (use "git add" to track)$

要重置特定文件夹(及其子文件夹)中的每个文件,您可以使用以下命令:

git reset *

此命令将取消存储您的更改:

git reset HEAD filename.txt

您也可以使用

git add -p

添加部分文件。

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>

使用*命令一次处理多个文件:

git reset HEAD *.prjgit reset HEAD *.bmpgit reset HEAD *gdb*

这个问题没有明确提出。原因是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 masterChanges to be committed:(use "git reset HEAD <file>..." to unstage)
new file:   my_new_file.txtmodified:   my_modified_file.txt

还有互动模式:

git add -i

选择选项3以取消添加文件。在我的情况下,我经常想添加多个文件,并且在交互模式下,您可以使用这样的数字来添加文件。这将需要除4以外的所有文件:1、2、3和5

要选择一个序列,只需键入1-5即可从1到5全取。

Git暂存文件

在Sourcetree中,您可以通过GUI轻松完成此操作。您可以检查Sourcetree用于取消暂存文件的命令。

我创建了一个新文件并将其添加到Git。然后我使用Sourcetree GUI取消了它。结果如下:

取消暂存文件[08/12/15 10:43]git-cdiff.mnemonicprefix=false-ccore.quotepath=false-ccredential.helper=源树重置-q--路径/到/文件/filename.java

Sourcetree使用reset取消暂存新文件。

git reset filename.txt

将从当前索引中删除名为filename.txt的文件(也称为“暂存区”,即保存“即将提交”的更改的地方),而不更改任何其他内容(工作目录不会被覆盖)。

要撤消git add,请使用:

git reset filename

假设我创建了一个新文件,newFile.txt

在此处输入图片描述

假设我不小心添加了文件,git add newFile.txt

在此处输入图片描述

现在我想撤消这个添加,在提交之前,git reset newFile.txt

在此处输入图片描述

最直观的解决方案之一是使用源树

您可以从分阶段和非分阶段拖放文件

在此输入图片描述

git add myfile.txt#这会将您的文件添加到待提交列表中

与此命令完全相反的是,

git reset HEAD myfile.txt  # This will undo it.

因此,您将处于上一状态。指定将再次处于未跟踪列表(上一状态)中。

它将重置你的头与指定的文件.所以,如果你的头没有它意味着,它将简单地重置它。

撤消已经添加的文件使用Git非常容易。要重置已经添加的myfile.txt,请使用:

git reset HEAD myfile.txt

说明:

暂存不需要的文件后,要撤消,您可以执行git resetHead是本地文件的头,最后一个参数是文件的名称。

我已经为您创建了下图中的步骤,包括在这些情况下可能发生的所有步骤:

git重置HEAD文件

git reset filename.txt

将从当前索引中删除名为filename.txt的文件,即“即将提交”区域,而不更改任何其他内容。

对于特定文件:

  • git重置my_file.txt
  • git签出my_file.txt

对于所有添加的文件:

  • git重置。
  • git签出。

注意:结帐更改文件中的代码并移动到最后更新(已提交)状态。重置不更改代码;它只是重置标题。

git reset命令可帮助您修改暂存区或暂存区和工作树。Git能够完全按照您想要的方式制作提交,这意味着您有时需要撤消对使用git add暂存的更改的更改。

您可以通过调用git reset HEAD <file to change>来做到这一点。您有两个选项可以完全摆脱更改。git checkout HEAD <file(s) or path(s)>是撤消对暂存区域和工作树的更改的快速方法。

但是,使用此命令要小心,因为它会删除对您的工作树的所有更改。Git不知道这些更改,因为它们从未提交过。一旦您运行此命令,就无法恢复这些更改。

您可以使用的另一个命令是git reset --hard。它对您的工作树同样具有破坏性-任何未提交的更改或分阶段更改在运行后都会丢失。运行git reset -hard HEADgit checkout HEAD做同样的事情。它只是不需要文件或路径即可工作。

您可以将--softgit reset一起使用。它将存储库重置为您指定的提交并暂存所有这些更改。您已经暂存的任何更改都不会受到影响,工作树中的更改也不会受到影响。

最后,您可以使用--mixed重置工作树而无需暂存任何更改。这也取消了暂存的任何更改。

2019更新

正如其他人在相关问题中指出的那样(参见这里这里这里这里这里这里这里),您现在可以使用取消暂存单个文件

git restore --staged <file>

取消所有文件(从repo的根开始):

git restore --staged .

备注

git restore2019年7中引入,并在2.23版中发布。
使用--staged标志,它恢复索引的内容(这里询问的内容)。

当使用暂存的未提交文件运行git status时,这就是Git现在建议用于取消暂存文件的方式(而不是v2.23之前的git reset HEAD <file>)。

我第一次遇到这个问题时,我在这里找到了这篇文章,从第一个答案中我知道我应该做git reset <filename>。它工作得很好。

最终,我碰巧在我的主git文件夹中有几个子文件夹。我发现只需git add .将所有文件添加到子文件夹中,然后git reset我不想添加的几个文件就很容易了。

现在我有很多文件和子文件夹。逐个git reset很乏味,但首先git add .更容易,然后重置少数沉重/不需要但有用的文件和文件夹。

我发现以下方法(未记录这里这里)相对容易。我希望它会有帮助:

假设你有以下情况:

Folder/SubFolder1/file1.txtFolder/SubFolder2/fig1.pngFolder/SubFolderX/fig.svgFolder/SubFolder3/<manyfiles>Folder/SubFolder4/<file1.py, file2.py, ..., file60.py, ...>

您想添加所有文件夹和文件,但不是fig1.png,不是SubFolderX,也不是file60.py,并且列表不断增长…

首先,制作/创建一个bash shell script并给它一个名字。说,git_add.sh

然后将所有路径添加到您想要git reset前面的git reset -- 的所有文件夹和文件。随着文件列表的增长,您可以轻松地将路径复制粘贴到脚本git_add.sh中。git_add.sh脚本应如下所示:

#!/bin/bash
git add .git reset -- Folder/SubFolder2/fig1.pnggit reset -- Folder/SubFolderXgit reset -- Folder/SubFolder4/file60.py

#!/bin/bash很重要。然后执行source git_add.sh来运行它。之后,您可以执行git commit -m "some comment",然后如果您已经设置了比特桶/Github,则可以执行git push -u origin master

免责声明:我只在Linux测试过这个。


如果您有很多文件和文件夹始终保留在本地git存储库中,但您不希望git在执行git add .时跟踪更改,例如视频和数据文件,你必须学会如何使用#1。也许从这里开始。

您可以使用git命令或GUI Git取消暂存或撤消。

单个文件

git reset File.txt

多个文件

git reset File1.txt File2.txt File3.txt

示例

假设您错误地添加了Home.jsListItem.jsUpdate.js

在此处输入图片描述

并且想要撤消/重置=>

git reset src/components/home/Home.js src/components/listItem/ListItem.js src/components/update/Update.js

在此处输入图片描述

使用gitgui的相同示例

git gui

打开一个窗口。从分阶段更改(将提交)取消选中您的文件

在此处输入图片描述

您可以在git版本2.23之后使用此命令:

git restore --staged <filename>

或者,您可以使用此命令:

git reset HEAD <filename>

我会使用git restore --staged .git restore --staged <filename>

您也可以使用git rm --cached,但是,git rm命令应该理想地用于已经跟踪的文件。

如果您想恢复上一次提交,但仍想在本地保留在提交中所做的更改,请使用以下命令:

git reset HEAD~1 --mixed
git add -A

用于将所有文件添加到您的提交(阶段)。

git reset

与'git add-A'命令相反。它删除所有暂存(表示准备好)要提交的文件。

同样,要添加要提交的特定文件,我们使用git add <filename>同样的方式,要取消(或重置)我们使用git add添加的特定文件,我们使用git reset <filename>

git重置与git add完全相反。