Categories
file-io git git-checkout

How do I find and restore a deleted file in a Git repository?

3091

Say I’m in a Git repository. I delete a file and commit that change. I continue working and make some more commits. Then, I discover that I need to restore that file after deleting it.

I know I can checkout a file using git checkout <commit> -- filename.txt, but I don’t know when that file was deleted.

  1. How do I find the commit that deleted a given filename?
  2. How do I restore that file back into my working copy?

7

  • 42

    note that the previous comment answers the question in the title, not in the body — that includes finding out when the file was deleted.

    – avdgaag

    Dec 16, 2011 at 16:02

  • 14

    To find the commit a file was deleted in: git log --diff-filter=D -- path/to/file

    Mar 16, 2012 at 21:28

  • 2

    Related: How do you discard unstaged changes in git?.

    – user456814

    Apr 28, 2014 at 5:16

  • 61

    @hhh git checkout deletedFile will undelete deletedFile if it’s been deleted but that deletion has not yet been staged or committed. That’s not what the question here is asking for; this question is about how to restore a file whose deletion was committed many commits ago.

    Apr 15, 2017 at 10:33

3413

Find the last commit that affected the given path. As the file isn’t in the HEAD commit, that previous commit must have deleted it.

git rev-list -n 1 HEAD -- <file_path>

Then checkout the version at the commit before, using the caret (^) symbol:

git checkout <deleting_commit>^ -- <file_path>

Or in one command, if $file is the file in question.

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

If you are using zsh and have the EXTENDED_GLOB option enabled, the caret symbol won’t work. You can use ~1 instead.

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"

20

  • 107

    The tricky bit is to checkout the commit BEFORE, using the ^ suffix. Thanks.

    Apr 26, 2010 at 14:40

  • 5

    For some reason, this won’t work in zsh. ± git checkout $(git rev-list -n 1 HEAD "spec/Sporkfile_example.rb")^ -- "spec/Sporkfile_example.rb" zsh: no matches found: b71c152d8f38dcd23ad7600a93f261a7252c59e9^ I switched to bash & it worked fine though.

    – zoras

    Feb 28, 2012 at 3:45


  • 23

    From the windows command line I got an error. error: pathspec <filename> did not match any file(s) known to git.. The solution was to use git bash.

    – donturner

    Jul 26, 2012 at 18:07

  • 61

    @zoras zsh has it’s own expansion on ‘^’ I believe, but you can use the alternative syntax of ‘~1’: git checkout <deleting-commit>~1 -- <file-path> ~X allows you to specify X commits before the specified commit, so ~1 is the commit before, ~2 is two commits before, etc

    Sep 10, 2012 at 15:07


  • 31

    On windows cmd prompt, the ^ character is the escape character! Therefore, on cmd, you have to type ^^ to tell cmd you want a single literal ^ and that your not escaping something else after it. What’s happening to many people is that the ^ is followed by a space. So cmd thinks you’re escaping the space — which yields simply a space character. Thus, by the time git gets the cli arguments, it sees SHA1 and not SHA1^. It’s really annoying. ~ isn’t an escape character, so that’s why that still works. (PS. if you think googlers will want this info, please upvote this comment)

    Sep 18, 2015 at 14:48

985

  1. Get all the commits which have deleted files, as well as the files that were deleted:

    git log --diff-filter=D --summary
    

    Make note of the desired commit hash, e.g. e4e6d4d5e5c59c69f3bd7be2.

  2. Restore the deleted file from one commit prior (~1) to the commit that was determined above (e4e6d4d5e5c59c69f3bd7be2):

    git checkout e4e6d4d5e5c59c69f3bd7be2~1 path/to/file.ext
    

    Note the ~1.

16

  • 13

    curious, what does the ~1 refer to?

    Jul 22, 2011 at 17:41

  • 8

    @tommy – the tilde spec will give you the nth grandchild of the named commit . See book.git-scm.com/4_git_treeishes.html for more details .

    Jul 23, 2011 at 12:00

  • 10

    this is by far the easiest and intuitive approach. git log -- *PartOfMyFileName*. Thanks for the $commit~1

    – bgs

    Apr 10, 2013 at 23:25

  • 3

    the git checkout $commit~1 filename syntax works perfect for individual files, and also works for whole directories. ie: to restore all deleted images in ./images from sha 12345: git checkout 12345~1 images. thanks for this answer!

    – noinput

    Jun 3, 2014 at 4:59

  • 38

    @Alexar $commit~1 means you should add the name of the commit. Something like 1d0c9ef6eb4e39488490543570c31c2ff594426c where $commit is.

    – Eugene

    Apr 7, 2015 at 6:27

335

To restore all deleted files in a folder:

git ls-files -d | xargs git checkout --

10

  • 1

    Where do the files get piped to? I see no change.

    Sep 13, 2013 at 14:36

  • 31

    This is probably the easiest method. Its perverted how difficult git has made even the simplest task.

    – jww

    Oct 19, 2013 at 4:12

  • 10

    The ls-files sub-command is handy, but doesn’t seem to work for files that had been removed with git rm i.e. staged, let alone committed, which is what the OP asked.

    – MarkHu

    Nov 17, 2017 at 8:47

  • 3

    @RomainValeri – It is 2019. The tools work for me. I do not work for the tools. If learing is required then the design is broken.

    – jww

    Dec 5, 2019 at 10:24


  • 3

    @RomainValeri , @jww you’re both right. git is nearly unparalleled in its usefulness, while also imfamously complicated to learn. An appreciable proportion of gits learning curve is due to an inconsistent / unintuitive UI. Personally, when I’m learning something difficult, one of the things that gives me reassurance is seeing that other (competent) people also struggled to figure it out

    Mar 17, 2021 at 23:21