关于版本控制:如何在提交前撤消”git add”?

How to undo 'git add' before commit?

我使用以下命令错误地向Git添加了文件:

1
git add myfile.txt

我还没有运行git commit。是否有一种方法可以撤消此操作,以便这些文件不会包含在提交中?

There are 48 answers so far (some deleted). Please don't add a new one unless you have some new information.


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

1
git reset <file>

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

你可以使用

1
git reset

没有任何文件名来取消所有应更改的标记。当有太多的文件需要在合理的时间内一个一个地列出时,这就非常有用了。

在Git的旧版本中,上述命令分别相当于git reset HEAD git reset HEAD,如果HEAD未定义(因为您尚未在回购中作出任何承诺)或不明确(因为您创建了一个名为HEAD的分支,这是您不应该做的愚蠢的事情),则上述命令将失败。不过,在Git 1.8.2中已经更改了这一点,所以在Git的现代版本中,您甚至可以在第一次提交之前使用上面的命令:

"git reset" (without options or parameters) used to error out when
you do not have any commits in your history, but it now gives you
an empty index (to match non-existent commit you are not even on).


你想要:

1
git rm --cached

推理:

当我刚接触这个的时候,我第一次尝试

1
git reset .

(要撤消整个初始添加操作),只会收到以下(不是这样)有用消息:

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

原来这是因为头参考(分支?)直到第一次提交之后才存在。也就是说,如果您的工作流程和我的工作流程类似,则会遇到与我相同的初学者问题:

  • CD到我伟大的新项目目录试用Git,新的热度
  • git init
  • git add .
  • git status

    …很多垃圾卷轴…

    =>该死,我不想把这些都加进去。

  • 谷歌"撤销git添加"

    =>查找堆栈溢出-yay

  • git reset .

    =>致命:无法将"head"解析为有效引用。

  • 进一步发现邮件列表中记录了一个针对这一点的错误。

    在Git状态输出中,正确的解决方案就在那里(是的,我把它描述为"垃圾")。

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

    而解决办法确实是使用git rm --cached FILE

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

    --cached
    Use this option to unstage and remove paths only from the index.
    Working tree files, whether modified or not, will be left.

    我继续使用

    1
    git rm --cached .

    删除所有内容并重新开始。但没有起作用,因为虽然add .是递归的,但结果rm需要-r来递归。叹息。

    1
    git rm -r --cached .

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

    1
    git add -n .

    我把所有的东西都放在一个安全的地方,然后才相信git help rm关于--cached没有破坏任何东西(如果我拼错了怎么办)。


    如果键入:

    1
    git status

    Git将告诉您什么是阶段性的,等等,包括关于如何取消固定的说明:

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

    我发现Git在这种情况下很好地督促我做正确的事情。

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

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


    澄清:git add将更改从当前工作目录移动到临时区域(索引)。

    此过程称为"分段"。因此,准备更改(更改的文件)的最自然的命令是显而易见的:

    1
    git stage

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

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

    1
    git reset HEAD --

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

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

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

    1
    2
    3
    4
    git add file1
    git stage file2
    git unadd file2
    git unstage file1

    就我个人而言,我使用更短的别名:

    1
    2
    git a #for staging
    git u #for unstaging


    除了接受的答案之外,如果错误添加的文件很大,您可能会注意到,即使在使用"EDOCX1"(5)将其从索引中删除后,它仍会占用.git目录中的空间。这没什么好担心的,文件确实仍在存储库中,但只是作为一个"松散对象",它不会被复制到其他存储库(通过克隆、推送),空间最终会被回收——尽管可能不会很快。如果你焦虑,你可以跑:

    1
    git gc --prune=now

    更新(以下是我试图澄清大多数投票结果可能引起的一些混乱):

    那么,哪个是git add的真正撤销?

    git reset HEAD 号?

    EDOCX1?11?

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

    一般来说,git add不能安全地撤销。

    让我们首先回顾一下git add 实际做了什么:

  • 如果之前没有跟踪git add会将其添加到缓存中,并包含当前内容。

  • 如果已跟踪git add将当前内容(快照、版本)保存到缓存中。在Git中,此操作仍然称为添加(而不仅仅是更新它),因为文件的两个不同版本(快照)被视为两个不同的项:因此,我们确实在向缓存中添加一个新项,以便稍后提交。

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

    I mistakenly added files using the command...

    OP的场景似乎是第一个(未跟踪的文件),我们希望"撤消"从跟踪的项目中删除该文件(而不仅仅是当前内容)。如果是这样,那么运行git rm --cached 就可以了。

    我们也可以运行git reset HEAD 。一般来说,这是更好的选择,因为它在两种情况下都有效:当我们错误地添加了一个已跟踪项的版本时,它也会执行撤销操作。

    但有两个警告。

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

    第二:要知道,git reset HEAD无法神奇地恢复先前缓存的文件内容,它只是从头部重新同步。如果我们的错误引导git add覆盖了以前的阶段性未提交版本,我们就无法恢复它。这就是为什么,严格来说,我们不能撤销[*]。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    $ 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

    当然,如果我们只是遵循通常的懒散的工作流程,只为添加新文件(案例1)而执行"git-add",并通过commit、git commit -a命令更新新内容,这就不是很关键了。

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


    1
    git rm --cached . -r

    将以递归方式"取消添加"当前目录中添加的所有内容


    1
    git gui

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


    撤消已添加的文件很容易使用git,对于已添加的重置myfile.txt,请使用:

    1
    git reset HEAD myfile.txt

    说明:

    在您准备了不需要的文件之后,要撤消,可以执行git resetHead是本地文件的头,最后一个参数是文件名。

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

    git reset HEAD file


    Git对每一个可以想象到的动作都有指令,但是需要广泛的知识来把事情做好,因此它充其量是反直觉的…

    你以前所做的:

    • 更改文件并使用git add .git add

    你想要什么:

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

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

      1
      2
      3
      4
      5
      6
      # 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不适用于单个文件。

    • 从索引和版本控制中删除,将未版本控制的文件保留在工作副本中:

      1
      git rm --cached <file>
    • 完全删除工作副本和版本控制中的

      1
      git rm <file>


    这个问题提出得不清楚。原因是git add有两种含义:

  • 向临时区域添加新文件,然后使用git rm --cached file撤消。
  • 将修改后的文件添加到临时区域,然后使用git reset HEAD file撤消。
  • 如有疑问,请使用

    1
    git reset HEAD file

    因为在这两种情况下,它都能达到预期的效果。

    警告:如果对已修改的文件(存储库中以前存在的文件)执行git rm --cached file,则将在git commit上删除该文件!它仍然存在于您的文件系统中,但是如果有其他人提取了您的提交,则该文件将从其工作树中删除。

    git status将告诉您文件是新文件还是修改过的:

    1
    2
    3
    4
    5
    6
    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


    如果您在初始承诺中不能使用git reset,只需声明"git破产",删除.git文件夹并重新开始。


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

    但是:

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

    简单地说,

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

    现在你可以

    1
    git unadd foo.txt bar.txt

    可以使用git removegit rm进行此操作,并带有--cached标志。尝试:

    1
    git help rm


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

    添加不需要的文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ 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)
    # [...]#

    进入交互式添加以撤消添加(在git中键入的命令有"r"(还原)、"1"(列表中的第一个条目显示)、"return"退出还原模式和"q"(退出):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    $ git add -i
               staged     unstaged path
      1:        +1/-0      nothing foo

    *** Commands ***
      1: [s]tatus     2: [u]pdate     3: [r]evert     4: [a]dd untracked
      5: [p]atch      6: [d]iff       7: [q]uit       8: [h]elp
    What now> r
               staged     unstaged path
      1:        +1/-0      nothing [f]oo
    Revert>> 1
               staged     unstaged path
    * 1:        +1/-0      nothing [f]oo
    Revert>>
    note: foo is untracked now.
    reverted one path

    *** Commands ***
      1: [s]tatus     2: [u]pdate     3: [r]evert     4: [a]dd untracked
      5: [p]atch      6: [d]iff       7: [q]uit       8: [h]elp
    What now> q
    Bye.
    $

    就是这样!这是你的证据,显示"foo"又回到了未跟踪的列表中:

    1
    2
    3
    4
    5
    6
    7
    8
    $ git status
    # On branch master
    # Untracked files:
    #   (use"git add <file>..." to include in what will be committed)
    # [...]
    #       foo
    nothing added to commit but untracked files present (use"git add" to track)
    $


    以下是一种避免新项目启动时出现这种令人烦恼的问题的方法:

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

    如果你没有任何承诺,git会让你很难做git reset。如果您只是为了拥有一个承诺而创建一个小小的初始承诺,那么之后,您可以根据自己的意愿多次执行git add -Agit reset,以使所有事情都正确。

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

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


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

    1
    2
    $> git --version
    git version 1.6.2.1

    现在,您可以尝试:

    1
    git reset HEAD .

    这应该是你想要的。


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

    1
    2
    3
    4
    5
    6
    7
    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)


    要从临时区域删除新文件(并且仅在有新文件的情况下),请按照上面的建议执行以下操作:

    1
    git rm --cached FILE

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


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

    1
    2
    3
    git reset HEAD *.prj
    git reset HEAD *.bmp
    git reset HEAD *gdb*


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

    1
    git reset *


    只需键入git reset,它就会恢复,就像上次提交以来从未键入git add .。确保你以前已经承诺过。


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

    enter image description here

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

    enter image description here

    现在我想在提交之前撤消这个添加,EDOCX1[4]

    enter image description here


    对于特定文件:

    • git reset my_file.txt
    • git checkout my_file.txt

    对于所有添加的文件:

    • git reset .
    • git checkout .

    注意:签出会更改文件中的代码并移动到上次更新(提交)状态。重置不会更改代码;它只是重置头段。


    此命令将取消显示您的更改:

    1
    git reset HEAD filename.txt

    您也可以使用

    1
    git add -p

    添加部分文件。


    撤消Git添加使用

    git reset filename


    我很惊讶没有人提到交互模式:

    1
    git add -i

    选择选项3取消添加文件。在我的例子中,我经常想添加多个文件,通过交互模式,您可以使用这样的数字来添加文件。这只需要4:1、2、3、5

    要选择一个序列,只需键入1-5就可以从1到5中选择所有序列。

    Git临时文件


    1
    git reset filename.txt

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


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

    与这个命令完全相反的是,

    1
    git reset HEAD myfile.txt  # this will undo it.

    所以,你将处于前一状态。指定的将再次出现在未跟踪列表中(以前的状态)。

    它将用指定的文件重置您的头部。所以,如果你的头没有它的意思,它将简单地重置它


    在sourcetree中,您可以通过GUI轻松地完成这项工作。您可以检查sourcetree使用哪个命令来取消文件的存储。

    我创建了一个新文件并将其添加到Git。然后,我使用sourcetree gui取消了它的绑定。结果是:

    Unstaging files [08/12/15 10:43]
    git -c diff.mnemonicprefix=false -c core.quotepath=false -c credential.helper=sourcetree reset -q -- path/to/file/filename.java

    sourcetree使用reset取消对新文件的存储。


    最直观的解决方案之一是使用sourcetree。

    您只需从分阶段和未分阶段拖放文件即可。enter image description here


    1
    git reset filename.txt

    Will remove a file named filename.txt from the current index, the"about to be committed" area, without changing anything else.


    git reset命令帮助您修改临时区域或临时区域和工作树。Git能够完全按照您想要的方式处理提交,这意味着您有时需要撤消对Git Add所进行的更改。

    你可以打电话给git reset HEAD 。有两个选项可以完全消除更改。git checkout HEAD 是一种撤消对临时区域和工作树的更改的快速方法。但是,请小心使用此命令,因为它会删除对工作树的所有更改。Git不知道这些更改,因为它们从未被提交过。运行此命令后,无法恢复这些更改。

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

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

    最后,您可以使用--mixed重置工作树,而不进行任何更改。这也会取消阶段性的任何更改。


    我也面临同样的情况。我用这个命令解决了这个问题:--

    1
    git reset


    1
    git stash && git stash pop

    会毁掉舞台的。


    命令git reset --hard HEAD应该起作用。需要注意的一点是,您需要将目录(cd更改回正常工作目录。否则,如果从目录运行命令,则错误地执行了git add .……您将无法恢复,而是得到其他文章中提到的错误,这些错误涉及"未知修订或路径不在工作树中"。