578

I accidentally added a lot of temporary files using git add -A

I managed to unstage the files using the following commands and managed to remove the dirty index.

git ls-files -z | xargs -0 rm -f
git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached

The above commands are listed in the git help rm. But sadly, my files were also deleted on execution, even though I had given cache option. How can I clear the index without losing the content?

Also it would be helpful if someone can explain the way this pipe operation works.

James Skemp
  • 8,018
  • 9
  • 64
  • 107
sarat
  • 10,512
  • 7
  • 43
  • 74
  • 8
    `rm -f` is not a git command and doesn't have a `--cached` option. Your local files were deleted before you executed `git rm` so I don't think you can legitimately blame `git rm` for anything. – CB Bailey Aug 18 '11 at 07:12
  • 9
    @sarat, please consider changing the correct answer to the highly upvoted answer from **Ian Maddox**, as the `git reset --hard` is **not the correct answer** and will in fact delete the content. This will confuse users - as it did me. – m3o Mar 24 '15 at 18:38
  • 2
    @sarat as Marco says, go on. This page gets a lot of traffic. – Ross Mar 26 '15 at 08:26

9 Answers9

1218

git reset

If all you want is to undo an overzealous "git add" run:

git reset

Your changes will be unstaged and ready for you to re-add as you please.


DO NOT RUN git reset --hard.

It will not only unstage your added files, but will revert any changes you made in your working directory. If you created any new files in working directory, it will not delete them though.

mas
  • 1,155
  • 1
  • 11
  • 28
tiz.io
  • 12,510
  • 1
  • 18
  • 9
39

If you have a pristine repo (or HEAD isn't set)[1] you could simply

rm .git/index

Of course, this will require you to re-add the files that you did want to be added.


[1] Note (as explained in the comments) this would usually only happen when the repo is brand-new ("pristine") or if no commits have been made. More technically, whenever there is no checkout or work-tree.

Just making it more clear :)

Simon
  • 3,667
  • 1
  • 35
  • 49
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Yea it's more like deleting the created index itself. Good thing is that, I don't require to re-initialize git. Thanks! – sarat Aug 18 '11 at 10:11
  • Are sure it's a safe operation? I just did this (actually moved index out of way) and got all other files staged for deletion. – inger Oct 10 '13 at 08:52
  • @inger "If you have a pristine repo". You clearly didn't have that. – sehe Oct 10 '13 at 08:53
  • Actually, I was wondering what you meant by "pristine repo" (I don't feel lucky with google either).. So you meant an empty repo actually? – inger Oct 10 '13 at 11:46
  • @inger Pristine would be the case where you didn't commit anything yet, this is common: doing the initial `git add .` tends to surprise me at the very amount of files I _didn't_ want to add :) In this, very specific, case, I would indeed just nuke the index. – sehe Oct 10 '13 at 17:18
  • Ah, so you did mean an empty repo. Quite a specific case indeed:) the question doesn't suggest this scenario, can't see how this became the selected answer for @sarat's case. Thanks anyway. – inger Oct 10 '13 at 18:48
  • 1
    @inger Agree. I had to assume that the OP had happened to have this exact situation - he doesn't specify it, but his description leaves the possibility. Anyways, I'm just sharing information, and can't influence the voting :(. Have added a word of warning to the answer text, in case it helps others in the future. – sehe Oct 10 '13 at 20:44
19

Use git reset HEAD to reset the index without removing files. (If you only want to reset a particular file in the index, you can use git reset HEAD -- /path/to/file to do so.)

The pipe operator, in a shell, takes the stdout of the process on the left and passes it as stdin to the process on the right. It's essentially the equivalent of:

$ proc1 > proc1.out
$ proc2 < proc1.out
$ rm proc1.out

but instead it's $ proc1 | proc2, the second process can start getting data before the first is done outputting it, and there's no actual file involved.

Amber
  • 507,862
  • 82
  • 626
  • 550
  • but to how to use it with multiple files. I never have commited these files before. – sarat Aug 18 '11 at 07:10
  • 5
    Just type `git reset HEAD` without specifying anything else, and it will reset the entire index. You can then just re-add only the files you want. – Amber Aug 18 '11 at 07:13
  • I got the following error. I never committed these items before. `$ git reset HEAD fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree. Use '--' to separate paths from revisions` – sarat Aug 18 '11 at 07:17
  • 1
    Try just `git reset` then, without the `HEAD`. – Amber Aug 18 '11 at 07:20
  • I already tried that I ended up in the following error. `$ git reset fatal: Failed to resolve 'HEAD' as a valid ref.` – sarat Aug 18 '11 at 10:05
17

2019 update

As pointed out by others in related questions (see here, here, here, here, here, here, and here), you can now unstage a file with git restore --staged <file>.

To unstage all the files in your project, run the following from the root of the repository (the command is recursive):

git restore --staged .

If you only want to unstage the files in a directory, navigate to it before running the above or run:

git restore --staged <directory-path>

Notes

  • git restore was introduced in July 2019 and released in version 2.23.
    With the --staged flag, it restores the content of the working tree from HEAD (so it does the opposite of git add and does not delete any change).

  • This is a new command, but the behaviour of the old commands remains unchanged. So the older answers with git reset or git reset HEAD are still perfectly valid.

  • When running git status with staged uncommitted file(s), this is now what Git suggests to use to unstage file(s) (instead of git reset HEAD <file> as it used to prior to v2.23).

prosoitos
  • 6,679
  • 5
  • 27
  • 41
11
git stash && git stash pop
Bijan
  • 6,675
  • 6
  • 33
  • 29
  • 2
    I would do 'git stash && git stash pop' so that the stash also gets deleted. 'apply' would leave the stash in the stash list. – chitti Nov 24 '13 at 09:21
8

If HEAD isn't set (i.e., you have no commits yet, but you don't want to just blow away .git because you've already set up other repo config you want to keep), you can also do

git rm -rf --cached .

to unstage everything. This is effectively the same as sehe's solution, but avoids mucking with Git internals.

jjlin
  • 4,462
  • 1
  • 30
  • 23
  • this actually tells Git to delete everything in the cached area. – Visionary Software Solutions Jul 01 '13 at 01:08
  • 1
    The cache is also known as the staging area, so I'm not sure what you're getting at. – jjlin Jul 01 '13 at 05:27
  • From sehe, rm .git/index is very dangerous - be sure to read the NOTE he has about a brand new repo! This is much safer for the other 99.999% of the time. I didn't read carefully and had to blow away my working copy and re-clone after doing rm .git/index on my working copy. – phpguru Feb 08 '17 at 18:55
  • DO NOT use this command! It will change ALL project to the untracked state (including ones that were not staged). This is not a solution to the original question. The correct solution using the 'git rm' command is to ONLY specify the files you want unstaged: git rm -rf --cached . – Monte Creasor Jan 20 '20 at 22:01
  • You should only use this command when you have a new repo **with no commits yet** (this is what "HEAD isn't set" means), but you don't want to just blow away `.git` because you've set up other repo config you want to keep. I've edited to clarify this. – jjlin Apr 18 '20 at 00:45
6

Warning: do not use the following command unless you want to lose uncommitted work!

Using git reset has been explained, but you asked for an explanation of the piped commands as well, so here goes:

git ls-files -z | xargs -0 rm -f
git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached

The command git ls-files lists all files git knows about. The option -z imposes a specific format on them, the format expected by xargs -0, which then invokes rm -f on them, which means to remove them without checking for your approval.

In other words, "list all files git knows about and remove your local copy".

Then we get to git diff, which shows changes between different versions of items git knows about. Those can be changes between different trees, differences between local copies and remote copies, and so on.
As used here, it shows the unstaged changes; the files you have changed but haven't committed yet. The option --name-only means you want the (full) file names only and --diff-filter=D means you're interested in deleted files only. (Hey, didn't we just delete a bunch of stuff?) This then gets piped into the xargs -0 we saw before, which invokes git rm --cached on them, meaning that they get removed from the cache, while the working tree should be left alone — except that you've just removed all files from your working tree. Now they're removed from your index as well.

In other words, all changes, staged or unstaged, are gone, and your working tree is empty. Have a cry, checkout your files fresh from origin or remote, and redo your work. Curse the sadist who wrote these infernal lines; I have no clue whatsoever why anybody would want to do this.


TL;DR: you just hosed everything; start over and use git reset from now on.

SQB
  • 3,926
  • 2
  • 28
  • 49
2

If you want to unstage all the changes use below command,

git reset --soft HEAD

In the case you want to unstage changes and revert them from the working directory,

git reset --hard HEAD
027
  • 1,553
  • 13
  • 23
2

I'm afraid that the first of those command lines unconditionally deleted from the working copy all the files that are in git's staging area. The second one unstaged all the files that were tracked but have now been deleted. Unfortunately this means that you will have lost any uncommitted modifications to those files.

If you want to get your working copy and index back to how they were at the last commit, you can (carefully) use the following command:

git reset --hard

I say "carefully" since git reset --hard will obliterate uncommitted changes in your working copy and index. However, in this situation it sounds as if you just want to go back to the state at your last commit, and the uncommitted changes have been lost anyway.

Update: it sounds from your comments on Amber's answer that you haven't yet created any commits (since HEAD cannot be resolved), so this won't help, I'm afraid.

As for how those pipes work: git ls-files -z and git diff --name-only --diff-filter=D -z both output a list of file names separated with the byte 0. (This is useful, since, unlike newlines, 0 bytes are guaranteed not to occur in filenames on Unix-like systems.) The program xargs essentially builds command lines from its standard input, by default by taking lines from standard input and adding them to the end of the command line. The -0 option says to expect standard input to by separated by 0 bytes. xargs may invoke the command several times to use up all the parameters from standard input, making sure that the command line never becomes too long.

As a simple example, if you have a file called test.txt, with the following contents:

hello
goodbye
hello again

... then the command xargs echo whatever < test.txt will invoke the command:

echo whatever hello goodbye hello again
Nick T
  • 25,754
  • 12
  • 83
  • 121
Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • I never made any commits so it will say like can't resolve HEAD. What we do in such situations. Thanks a lot for explaining pipe in detail. – sarat Aug 18 '11 at 10:09
  • 8
    If you have just changed your git ignore and did git add --all to include a lot of files, DO NOT run git reset --hard to unstage them. THEY WILL BE DELETED!! – Ajoy May 30 '14 at 06:05
  • 6
    Uwaaahh!! Maybe you need to highlight the word **"carefully"**. I just saw these three words "git reset --hard" and all my unstaged files are...fufff!! gone!!!!! – Vineeth Chitteti Dec 16 '14 at 06:56