当前位置 : 首页 » 互动问答 » 正文

Find and restore a deleted file in a Git repository

分类 : 互动问答 | 发布时间 : 2009-06-05 06:40:34 | 评论 : 19 | 浏览 : 817127 | 喜欢 : 2428

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 find I need to restore that file.

I know I can checkout a file using git checkout HEAD^ foo.bar, but I don't really know when that file was deleted.

  1. What would be the quickest way to find the commit that deleted a given filename?
  2. What would be the easiest way to get that file back into my working copy?

I'm hoping I don't have to manually browse my logs, checkout the entire project for a given SHA and then manually copy that file into my original project checkout.

回答(19)

  • 1楼
  • Find the last commit that affected the given path. As the file isn't in the HEAD commit, this 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"
    
  • 2楼
    1. Use git log --diff-filter=D --summary to get all the commits which have deleted files and the files deleted;
    2. Use git checkout $commit~1 filename to restore the deleted file.

    Where $commit is the value of the commit you've found at step 1, e.g. e4cf499627

  • 3楼
  • To restore all those deleted files in a folder enter the following command.

    git ls-files -d | xargs git checkout --
    
  • 4楼
  • I came to this question looking to restore a file I just deleted but I hadn't yet committed the change. Just in case you find yourself in this situation, all you need to do is the following:

    git checkout HEAD -- path/to/file.ext

  • 5楼
  • If you’re insane, use git-bisect. Here's what to do:

    git bisect start
    git bisect bad
    git bisect good <some commit where you know the file existed>
    

    Now it's time to run the automated test. The shell command '[ -e foo.bar ]' will return 0 if foo.bar exists, and 1 otherwise. The "run" command of git-bisect will use binary search to automatically find the first commit where the test fails. It starts halfway through the range given (from good to bad) and cuts it in half based on the result of the specified test.

    git bisect run '[ -e foo.bar ]'
    

    Now you're at the commit which deleted it. From here, you can jump back to the future and use git-revert to undo the change,

    git bisect reset
    git revert <the offending commit>
    

    or you could go back one commit and manually inspect the damage:

    git checkout HEAD^
    cp foo.bar /tmp
    git bisect reset
    cp /tmp/foo.bar .
    
  • 6楼
  • My new favorite alias, based on bonyiii's answer (upvoted), and my own answer about "Pass an argument to a Git alias command":

    git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'
    

    I have lost a file, deleted by mistake a few commits ago?
    Quick:

    git restore my_deleted_file
    

    Crisis averted.


    Robert Dailey proposes in the comments the following alias:

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

    And jegan adds in the comments:

    For setting the alias from the command line, I used this command:

    git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 
    
  • 7楼
  • If you know the filename, this is an easy way with basic commands:

    List all the commits for that file.

    git log -- path/to/file
    

    The last commit (topmost) is the one that deleted the file. So you need to restore the second to last commit.

    git checkout {second to last commit} -- path/to/file
    
  • 8楼
  • To restore a deleted and commited file:

    git reset HEAD some/path
    git checkout -- some/path
    

    It was tested on Git version 1.7.5.4.

  • 9楼
  • If you only made changes and deleted a file, but not commit it, and now you broke up with your changes

    git checkout -- .
    

    but your deleted files did not return, you simply do the following command:

    git checkout <file_path>
    

    And presto, your file is back.

  • 10楼
  • I've got this solution.

    1. Get the id of the commit where the file was deleted using one of the ways below.

      • git log --grep=*word*
      • git log -Sword
      • git log | grep --context=5 *word*
      • git log --stat | grep --context=5 *word* # recommended if you hardly remember anything
    2. You should get something like:

    commit bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Author: Alexander Orlov Date: Thu May 12 23:44:27 2011 +0200

    replaced deprecated GWT class
    - gwtI18nKeySync.sh, an outdated (?, replaced by a Maven goal) I18n generation script
    

    commit 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Author: Alexander Orlov Date: Thu May 12 22:10:22 2011 +0200

    3. Now using the commit id bfe68bd117e1091c96d2976c99b3bcc8310bebe7 do:

    git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java
    

    As the commit id references the commit where the file was already deleted you need to reference the commit just before bfe68b which you can do by appending ^1. This means: give me the commit just before bfe68b.

  • 11楼
  • git checkout /path/to/deleted.file
    
  • 12楼
  • In many cases, it can be useful to use coreutils (grep, sed, etc.) in conjunction with Git. I already know these tools quite well, but Git less so. If I wanted to do a search for a deleted file, I would do the following:

    git log --raw | grep -B 30 $'D\t.*deleted_file.c'
    

    When I find the revision/commit:

    git checkout <rev>^ -- path/to/refound/deleted_file.c
    

    Just like others have stated before me.

    The file will now be restored to the state it had before removal. Remember to re-commit it to the working tree if you want to keep it around.

  • 13楼
  • git undelete path/to/file.ext

    1. Put this in your .bash_profile (or other relevant file that loads when you open a command shell):

      git config --global alias.undelete '!sh -c "git checkout $(git rev-list -n 1 HEAD -- $1)^ -- $1" -'
      
    2. Then use:

      git undelete path/to/file.ext
      

    This alias first checks to find the last commit where this file existed, then does a git checkout of that file path from that last commit where this file existed. source

  • 14楼
  • So I had to restore a bunch of deleted files from a specific commit and I managed it with two commands:

    git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git checkout <rev>^ -- 
    git show <rev> --diff-filter=D --summary --name-only --no-commit-id | xargs git reset HEAD 
    

    (Note the trailing space on the end of each command.)

    The files had been added to the .gitignore file and then cleared with git rm, I needed to restore the files but then unstage them. I had hundreds of files to restore, typing things manually for each file as in the other examples was going to be far too slow.

  • 15楼
  • user@bsd:~/work/git$ rm slides.tex
    user@bsd:~/work/git$ git pull 
    Already up-to-date.
    user@bsd:~/work/git$ ls slides.tex
    ls: slides.tex: No such file or directory
    

    Restore the deleted file:

    user@bsd:~/work/git$ git checkout
    D       .slides.tex.swp
    D       slides.tex
    user@bsd:~/work/git$ git checkout slides.tex 
    user@bsd:~/work/git$ ls slides.tex
    slides.tex
    
  • 16楼
  • I had the same question. Without knowing it, I had created a dangling commit.

    List dangling commits

    git fsck --lost-found

    Inspect each dangling commit

    git reset --hard <commit id>

    My files reappeared when I moved to the dangling commit.

    git status for the reason:

    “HEAD detached from <commit id where it detached>”

  • 17楼
  • In our case we accidentally deleted files in a commit and some commit later we realized our mistake and wanted to get back all the files that were deleted but not those that were modified.

    Based on Charles Bailey's excellent answer here is my one liner:

    git co $(git rev-list -n 1 HEAD -- <file_path>)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- <file_path>)~1 head | grep '^D' | cut -f 2)
    
  • 18楼
  • If you know the commit that deleted the file(s), run this command where <SHA1_deletion> is the commit that deleted the file:

    git diff --diff-filter=D --name-only <SHA1_deletion>~1 <SHA1_deletion> | xargs git checkout <SHA1_deletion>~1 --
    

    The part before the pipe lists all the files that were deleted in the commit; they are all checkout from the previous commit to restore them.

  • 19楼
  • $ git log --diff-filter=D --summary  | grep "delete" | sort
    

相关阅读:

Why should I use core.autocrlf=true in Git?

Force "git push" to overwrite remote files

Update Git branches from master

Testing IO performance in Linux

Undo a Git merge that hasn't been pushed yet

How can I delete a file from git repo?

Correct way to write line to file?

Git fetch remote branch

How do you clone a Git repository into a specific folder?

Reset or revert a specific file to a specific revision using Git?