How do I undo 'git add' before commit?

Solution 1

To unstage a specific file

git reset <file>

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

To unstage all files from the current change set:

git reset

In old versions of Git, the above commands are equivalent to git reset HEAD <file> and git reset HEAD respectively, and will fail if HEAD is 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 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).

Documentation: git reset


Solution 2

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:

  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.

It further turns out that there's a bug logged against 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)

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

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

Note the warnings elsewhere here - git rm deletes your local working copy of the file, but not if you use --cached. Here's the result of 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.

I proceed to use

git rm --cached .

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

git rm -r --cached .

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

git add -n .

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


Solution 3

If you type:

git status

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

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.

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

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

Solution 4

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

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

git stage

git add is just an easier-to-type alias for git stage

Pity there is no git unstage nor git unadd commands. The relevant one is harder to guess or remember, but it is pretty obvious:

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

Solution 5

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 .git directory.

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 undo of git add?

git reset HEAD <file> ?

or

git rm --cached <file>?

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

git add cannot be undone - safely, in general.

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

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

  2. If <file> was already tracked, git add saves the current content (snapshot, version) to the cache. In Git, this action is still called add, (not mere update it), 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.

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. If this is the case, then it's ok to run 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.

But there are two caveats.

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

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

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 -a command.


* (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)