如何在提交之前撤消“git add”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/348170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 06:00:27  来源:igfitidea点击:

How do I undo 'git add' before commit?

gitversion-controlgit-commitgit-stage

提问by paxos1977

I mistakenly added files to Git using the command:

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

git add myfile.txt

I have not yet run git commit. Is there a way to undo this, so these files won't be included in the commit?

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

回答by genehack

You can undo git addbefore commit with

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

git reset <file>

which will remove it from the current index (the "about to be committed" list) without changing anything else.

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

You can use

您可以使用

git reset

without any file name to unstage all due changes. This can come in handy when there are too many files to be listed one by one in a reasonable amount of time.

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

In old versions of Git, the above commands are equivalent to git reset HEAD <file>and git reset HEADrespectively, and will fail if HEADis undefined (because you haven't yet made any commits in your repository) or ambiguous (because you created a branch called HEAD, which is a stupid thing that you shouldn't do). This was changed in Git 1.8.2, though, so in modern versions of Git you can use the commands above even prior to making your first commit:

在旧版本的 Git 中,上述命令分别等效于git reset HEAD <file>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).

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

回答by Rhubarb

You want:

你要:

git rm --cached <added_file_to_undo>

Reasoning:

推理:

When I was new to this, I first tried

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

git reset .

(to undo my entire initial add), only to get this (not so) helpful message:

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

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

It turns out that this is because the HEAD ref (branch?) doesn't exist until after the first commit. That is, you'll run into the same beginner's problem as me if your workflow, like mine, was something like:

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

  1. cd to my great new project directory to try out Git, the new hotness
  2. git init
  3. git add .
  4. git status

    ... lots of crap scrolls by ...

    => Damn, I didn't want to add all of that.

  5. google "undo git add"

    => find Stack Overflow - yay

  6. git reset .

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

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

    ...大量废话卷轴...

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

  5. 谷歌“撤消git添加”

    => 找到堆栈溢出 - 是的

  6. git reset .

    => 致命:无法将“HEAD”解析为有效引用。

It further turns out that there's a bug loggedagainst the unhelpfulness of this in the mailing list.

结果进一步证明,在邮件列表中记录一个错误,指出它没有帮助。

And that the correct solution was right there in the Git status output (which, yes, I glossed over as 'crap)

并且正确的解决方案就在 Git 状态输出中(是的,我将其掩饰为“废话”)

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

And the solution indeed is to use git rm --cached FILE.

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

Note the warnings elsewhere here - git rmdeletes your local working copy of the file, but notif you use --cached. Here's the result of git help rm:

请注意此处其他地方的警告 -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.

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

I proceed to use

我继续使用

git rm --cached .

to remove everything and start again. Didn't work though, because while add .is recursive, turns out rmneeds -rto recurse. Sigh.

删除所有内容并重新开始。但是没有用,因为 whileadd .是递归的,结果rm需要-r递归。叹。

git rm -r --cached .

Okay, now I'm back to where I started. Next time I'm going to use -nto do a dry run and see what will be added:

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

git add -n .

I zipped up everything to a safe place before trusting git help rmabout the --cachednot destroying anything (and what if I misspelled it).

我相信前拉上了一切到一个安全的地方git help rm关于--cached不破坏任何东西(和什么如果我拼错了它)。

回答by Paul Beckingham

If you type:

如果您键入:

git status

Git will tell you what is staged, etc., including instructions on how to unstage:

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

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

I find Git does a pretty good job of nudging me to do the right thing in situations like this.

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

Note: Recent Git versions (1.8.4.x) have changed this message:

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

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

回答by takeshin

To clarify: git addmoves changes from the current working directory to the staging area(index).

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

This process is called staging. So the most natural command to stagethe changes (changed files) is the obvious one:

这个过程称为staging。因此,暂存更改(更改的文件)的最自然命令是显而易见的命令:

git stage

git addis just an easier-to-type alias for git stage

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

Pity there is no git unstagenor git unaddcommands. The relevant one is harder to guess or remember, but it is pretty obvious:

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

git reset HEAD --

We can easily create an alias for this:

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

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

And finally, we have new commands:

最后,我们有新的命令:

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

Personally I use even shorter aliases:

我个人使用更短的别名:

git a # For staging
git u # For unstaging

回答by leonbloy

An addition to the accepted answer, if your mistakenly-added file was huge, you'll probably notice that, even after removing it from the index with 'git reset', it still seems to occupy space in the .gitdirectory.

除了已接受的答案之外,如果您错误添加的文件很大,您可能会注意到,即使在使用“ git reset”将其从索引中删除后,它似乎仍然占据.git目录中的空间。

This is nothing to be worried about; the file is indeed still in the repository, but only as a "loose object". It will not be copied to other repositories (via clone, push), and the space will be eventually reclaimed - though perhaps not very soon. If you are anxious, you can run:

这没什么好担心的;该文件确实仍在存储库中,但只是作为“松散对象”。它不会被复制到其他存储库(通过克隆、推送),并且空间最终会被回收——尽管可能不会很快。如果你着急,你可以运行:

git gc --prune=now


Update(what follows is my attempt to clear some confusion that can arise from the most upvoted answers):

更新(以下是我试图清除可能由最受好评的答案引起的一些混乱):

So, which is the real undoof git add?

所以,这是真正的撤销git add

git reset HEAD <file>?

git reset HEAD <file>?

or

或者

git rm --cached <file>?

git rm --cached <file>?

Strictly speaking, and if I'm not mistaken: none.

严格来说,如果我没记错的话:none

git addcannot be undone- safely, in general.

git add无法撤消- 一般情况下安全的。

Let's recall first what git add <file>actually does:

让我们先回忆一下git add <file>实际做了什么:

  1. If <file>was not previously tracked, git addadds it to the cache, with its current content.

  2. If <file>was already tracked, git addsaves the current content(snapshot, version) to the cache. In Git, this action is still called add, (not mere updateit), because two different versions (snapshots) of a file are regarded as two different items: hence, we are indeed adding a new item to the cache, to be eventually committed later.

  1. 如果<file>之前没有跟踪git add则将添加到缓存中,并包含其当前内容。

  2. 如果已经<file>跟踪git add则将当前内容(快照、版本)保存到缓存中。在 Git 中,这个动作仍然被称为add,(不仅仅是更新它),因为一个文件的两个不同版本(快照)被视为两个不同的项目:因此,我们确实向缓存中添加了一个新项目,最终后来犯了。

In light of this, the question is slightly ambiguous:

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

I mistakenly added files using the command...

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

The OP's scenario seems to be the first one (untracked file), we want the "undo" to remove the file (not just the current contents) from the tracked items. Ifthis is the case, then it's ok to run git rm --cached <file>.

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

And we could also run git reset HEAD <file>. This is in general preferable, because it works in both scenarios: it also does the undo when we wrongly added a version of an already tracked item.

我们也可以运行git reset HEAD <file>. 这通常更可取,因为它适用于两种情况:当我们错误地添加了已跟踪项目的版本时,它还可以撤消。

But there are two caveats.

但是有两个警告。

First: There is (as pointed out in the answer) only one scenario in which git reset HEADdoesn't work, but git rm --cacheddoes: a new repository (no commits). But, really, this a practically irrelevant case.

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

Second: Be aware that git reset HEADcan't magically recover the previously cached file contents, it just resynchronises it from the HEAD. If our misguided git addoverwrote a previous staged uncommitted version, we can't recover it. That's why, strictly speaking, we cannot undo [*].

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

Example:

例子:

$ 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

Of course, this is not very critical if we just follow the usual lazy workflow of doing 'git add' only for adding new files (case 1), and we update new contents via the commit, git commit -acommand.

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



* (Edit: the above is practically correct, but still there can be some slightly hackish/convoluted ways for recovering changes that were staged, but not committed and then overwritten - see the comments by Johannes Matokic and iolsmit)

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

回答by Alireza

Undoa file which has already been added is quite easy using Git. For resetting myfile.txt, which have already been added, use:

使用 Git 可以很容易地撤消已添加的文件。对于myfile.txt已经添加的 reset ,请使用:

git reset HEAD myfile.txt

Explanation:

解释:

After you staged unwanted file(s), to undo, you can do git reset. Headis head of your file in the local and the last parameter is the name of your file.

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

I have created the steps in the image below in more details for you, including all steps which may happen in these cases:

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

git reset HEAD file

git重置头文件

回答by braitsch

git rm --cached . -r

will "un-add" everything you've added from your current directory recursively

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

回答by Khaja Minhajuddin

Run

git gui

and remove all the files manually or by selecting all of them and clicking on the unstage from commitbutton.

并手动删除所有文件或通过选择所有文件并单击从提交按钮取消暂存

回答by sjas

Git has commands for every action imaginable, but it needs extensive knowledge to get things right and because of that it is counter-intuitive at best...

Git 对每个可以想象的动作都有命令,但它需要广泛的知识才能把事情做好,因此它充其量是违反直觉的......

What you did before:

你之前做的:

  • Changed a file and used git add ., or git add <file>.
  • 更改文件并使用git add ., 或git add <file>.

What you want:

你想要什么:

  • Remove the file from the index, but keep it versioned and left with uncommitted changes in working copy:

    git reset head <file>
    
  • Reset the file to the last state from HEAD, undoing changes and removing them from the index:

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

    This is needed since git reset --hard HEADwon't work with single files.

  • Remove <file>from index and versioning, keeping the un-versioned file with changes in working copy:

    git rm --cached <file>
    
  • Remove <file>from working copy and versioning completely:

    git rm <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>
    

回答by Michael_Scharf

The question is not clearly posed. The reason is that git addhas two meanings:

这个问题没有明确提出。原因是git add有两层意思:

  1. adding a new fileto the staging area, then undo with git rm --cached file.
  2. adding a modifiedfile to the staging area, then undo with git reset HEAD file.
  1. 新文件添加到暂存区,然后使用 撤消git rm --cached file
  2. 修改后的文件添加到暂存区,然后使用git reset HEAD file.

If in doubt, use

如有疑问,请使用

git reset HEAD file

Because it does the expected thing in both cases.

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

Warning:if you do git rm --cached fileon a file that was modified(a file that existed before in the repository), then the file will be removed on git commit! It will still exist in your file system, but if anybody else pulls your commit, the file will be deleted from their work tree.

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

git statuswill tell you if the file was a new fileor modified:

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