644

Usually, to discard changes to a file you would do:

git checkout -- <file>

What if the change I want to discard is deleting the file? The above line would give an error:

error: pathspec '<file>' did not match any file(s) known to git.

What command will restore that single file without undoing other changes?

bonus point: Also, what if the change I want to discard is adding a file? I would like to know how to unstage that change as well.

lurscher
  • 25,930
  • 29
  • 122
  • 185

8 Answers8

1007

Assuming you're wanting to undo the effects of git rm <file> or rm <file> followed by git add -A or something similar:

# this restores the file status in the index
git reset -- <file>
# then check out a copy from the index
git checkout -- <file>

To undo git add <file>, the first line above suffices, assuming you haven't committed yet.

Patrick M
  • 10,547
  • 9
  • 68
  • 101
twalberg
  • 59,951
  • 11
  • 89
  • 84
  • 110
    The `--` is the key. `git reset ` doesn't work, which is what brought me here. –  Oct 18 '16 at 11:12
  • But this will also restore all the modifications. what to do if I want to restore just all removed files. And keep all modifications untouched. – Dainius Kreivys Sep 05 '17 at 17:26
  • 8
    Why is `end-of-options-marker` required in the deleted file case only? – haridsv Nov 13 '17 at 05:44
  • 9
    @handsv It's not strictly required (you could alternately do `git reset HEAD `, which is equivalent), but `git reset` treats its first argument before `end-of-options-marker` as a ref name, not a file name. Could it be written a bit more flexibly? Probably. Why wasn't it? Probably only the developers know for sure. – twalberg Nov 13 '17 at 15:08
  • @DuncanJones probably not quite what you're after but you could do `git reset -- && git checkout -- `. Maybe also check [this](https://stackoverflow.com/questions/7534184/git-alias-multiple-commands-and-parameters?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) out – lachy Apr 15 '18 at 06:56
  • 4
    @twalberg `git reset filename` works fine for non-deleted files. – Rag May 15 '18 at 00:24
  • 1
    @AaronMahan - can you please explain the difference between `git reset ` and `git reset -- `. I'm having a hard time finding an answer to that on google. – Neeraj B. Mar 13 '19 at 15:06
  • 4
    @NeerajB. Manual page synopsis says `git reset [-q] [] [--] ...`. If your `` argument matches the name of a `` (a branch, or `HEAD` or a tag or ...), then it's going to do something other than what you might think, if you don't use `--`. That's the reason it's there. – twalberg Mar 13 '19 at 15:32
  • This is the best answer because it actually deciphers the results from a program evidently not written by English-speaking people: After `git reset -- `, `git status` says "Changed but not updated", which evidently means "not staged" in the native language of the git authors. – personal_cloud May 08 '19 at 16:30
  • Both "git checkout HEAD " and "git reset --hard " command will do the same thing as this commands. Am I missing anything ? – Siva Sankaran Sep 03 '20 at 20:17
  • now git has the `restore` command which I recommend using instead of `checkout` since using checkout in this way overlooks how powerful this command actually is. Before the `--` in the command there is an optional parameter which is a commit or branch that you can overwrite the specified file with, essentially saying "overwrite this file with the content it has in this commit/branch". Since that parameter wasn't specified, it is simply restoring that file to the last commit, which is desired, but if you don't understand git then it is overlooking what is actually going on. – Jake Jul 06 '22 at 19:46
70

Both questions are answered in git status.

To unstage adding a new file use git rm --cached filename.ext

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

To unstage deleting a file use git reset HEAD filename.ext

# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   deleted:    test

In the other hand, git checkout -- never unstage, it just discards non-staged changes.

starbeamrainbowlabs
  • 5,692
  • 8
  • 42
  • 73
seppo0010
  • 15,184
  • 5
  • 31
  • 30
  • 6
    I don't see the hint for a deleted file in git 1.7.2.5 on Debian. – tripleee Sep 21 '15 at 10:55
  • Nice to see `git status` cited; shows users a way to self-help now and next time, and in case information is added or updated in future git versions. – Will Feb 28 '19 at 16:39
  • 1
    This is wrong. "Changes to be committed" is what you see *before* the `git reset`. *After* the `git reset`, you see "Changed but not updated" which means "Changes not staged" in the native language of the git authors, apparently. More importantly, the whole dogma about "git status tells you everything you know" is a lie. (Managers who say that are wasting people's time and should be fired.) – personal_cloud May 08 '19 at 16:25
  • 2
    Generally speaking this is now `git restore --staged deleted-file` as of at least git 2.24.3 (but could be earlier versions too) – Jasdeep Khalsa Dec 31 '21 at 00:27
17

If it has been staged and committed, then the following will reset the file:

git reset COMMIT_HASH file_path
git checkout COMMIT_HASH file_path
git add file_path

This will work for a deletion that occurred several commits previous.

michaeldever
  • 359
  • 4
  • 14
12

As of git v2.23, you have another option:

git restore --staged -- <file>

Kreempuff
  • 643
  • 9
  • 16
12

The answers to your two questions are related. I'll start with the second:

Once you have staged a file (often with git add, though some other commands implicitly stage the changes as well, like git rm) you can back out that change with git reset -- <file>.

In your case you must have used git rm to remove the file, which is equivalent to simply removing it with rm and then staging that change. If you first unstage it with git reset -- <file> you can then recover it with git checkout -- <file>.

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
4

I tried the above solutions and I was still having difficulties. I had other files staged with two files that were deleted accidentally.

To undo the two deleted files I had to unstage all of the files:

git reset HEAD .

At that point I was able to do the checkout of the deleted items:

git checkout -- WorkingFolder/FileName.ext

Finally I was able to restage the rest of the files and continue with my commit.

Matt Ke
  • 3,599
  • 12
  • 30
  • 49
0

From manual page,

git-reset - Reset current HEAD to the specified state
git reset [-q] [<tree-ish>] [--] <paths>...
In the first and second form, copy entries from <tree-ish> to the index.

for example, when we use git reset HEAD~1 it reset our current HEAD to HEAD~1

so when we use git reset 'some-deleted-file-path'

git assume 'some-deleted-file-path' as some commit point and try to reset out current HEAD to there.

And it ends up fail

fatal: ambiguous argument 'some-deleted-file-path': unknown revision or path not in the working tree.
KyoungWan
  • 1
  • 1
0

If you accidentally staged the deletion of many files which are mixed in with changes you want to keep, you can use this pipeline of commands:

git diff --staged --name-status | awk '$1=="D"{print $2}' | xargs git restore --staged -- 

The first portion, git diff --staged --name-status, shows all the staged changes with a letter to identify the type of change. Deleted files have a "D".

The second portion prints the file name if the status letter (first column) is "D". This effectively filters the list to just the staged deletions.

The last line calls the git restore --staged command (that the other answers have so helpfuly pointed out) for each deleted file.

irowe
  • 638
  • 11
  • 21
  • does not work: `git: 'restore' is not a git command.` – Eric Aug 25 '23 at 09:18
  • @Eric, you will need to update your version of git. `git-restore` was introduced in Git 2.23. See https://stackoverflow.com/questions/66120447/git-restore-command-not-found and https://git-scm.com/docs/git-restore – irowe Aug 25 '23 at 12:50