tags:

views:

7762

answers:

4

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.

+12  A: 

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 .
jleedev
Could you elaborate on `git bisect run '[ -e foo.bar ]'`?
avdgaag
You can also use good and bad manually, if it's something that can't be checked automatically. See the bisect man page.
jleedev
It's not the easiest solution, but it is quite impressive. Thanks for the write-up.
avdgaag
+30  A: 
  1. Use git log --diff-filter=D --summary to get all the commits which have deleted files and the files deleted;
  2. Use git revert $commit to revert that particular commit.
Robert Munteanu
The --diff-filter=D bit is genius. Thanks!
avdgaag
You're welcome. Enjoy the resurrections :-)
Robert Munteanu
My solution's much more fun.
jleedev
@jleedev - it's innovative, that's for sure.
Robert Munteanu
A: 

And what if commit where my file was deleted contains several deleted files but I need to restore the only one. All other should stay deleted. As I understand all deleted in commit files will be restored after revert. Is there some way to restore particular file in commit?

if commit_del is the identifier of the commit that deleted fileA then `git checkout commit_del^ -- fileA` will restore fileA to the index and working tree. You can then make a commit re-adding it.
Charles Bailey
+32  A: 

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.

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"
Charles Bailey
This checking out of a given file is awesome, thanks!
avdgaag
Agreed! The accepted answer will reapply the entire commit, but in my case, I just want to bring back one file. Thanks, Charles.
Pistos
The tricky bit is to checkout the commit BEFORE, using the ^ suffix. Thanks.
Christian Oudard
Very nice and clean. Thx!
AndreasT
Great solution, the first bit to find the last commit that affected the path is wonderful.
brad