I was knee deep in work the other day and accidentally overwrote a file that I had spent some time working on a couple of days ago.
I was not happy. However, since I commit my work often with git, I knew all my previous work was not lost. I just needed to recover that one file.
I was surprised to find that posts on recovering just a file in Git were a little harder to find on the Internet than I expected. Everyone was very helpful when it came to resetting all your files from a previous commit, but not just one file. And when this happens, I usually write a post about it. We'll limit ourselves to recovering one file, and if you "grok" that, you'll know by extrapolation how to get any files you want from previous commits in Git.
First, if your good file, that you committed previously, is still in your index (the files in your last commit), and you haven't committed the new bad file yet, all you need to do overwrite the bad file in your working directory with the good file in your index using git checkout. Like so:
git checkout ( path/good file name )
For example, to recover a records_db.php file in my models subdirectory, I would type:
git checkout models/records_db.php
Easy no. Your previously committed file will overwrite your bad new file in your working directory, and your all set. Here's an example using the file test.txt.
Be careful, if you just do a "git checkout" without being specific about the file, you'll get all the previously committed files back, which will overwrite your current work, that you haven't committed yet, which you may not want to do.
Second, if you've committed the bad new file before you discovered your error, you need to tell git which committed file you want to retrieve, and its just a tad more complicated. Git can't guess which file you want from which commit.
What your trying to do is bring into your current working directory, an older file. "git checkout" alone brings your last committed file, which is, after you committed, now the bad file.
We need to find the good file in your commits. There's a couple of ways to do this. Let's digress a bit, and talk about how Git labels commits. There's the absolute method, and the relative method.
The absolute method is the SHA1 encryption number. You can see all your commits with the absolute SHA1 number by running:
The number opposite the word commit is that commits absolute address. If you get a ":" after git log runs, you can quit out of git log by typing a "q". The SHA1 numbers are long, but you only need the first three or four characters and Git will know which commit you mean.
Most folks, however, like to use the commit relative names. You can see the relative names by typing:
git show-branch --more=10
Now that we know how to label our commits, let's get back to the subject at hand. We need to find the good file. By looking at the "git log" or the "git show-branch" you may not be able to determine which commit has the good file. We may want to see the contents of the file. For that, let's use the gitk history gui, like so:
That's with two dashes - -, as is, the more above in the show-branch command.
This is a graphical record of all your commits, and the files that were committed. If you click on the file in the lower right window, you'll see the contents on the left. This is the quick way to look at the file contents in each commit to find the commit you need.
OK, you know the commit you need. You can double check the relative name of the commit with the "git show-branch --more=10" command and we're all set.
Back to what we started with "git checkout." We will tell git to checkout the "four.txt" file from the "master~2" commit, a previous commit, like so:
git checkout master~2 four.txt
Here's an actual example of the command.
Now that wasn't bad. You should now feel confident you can recover any file you want in Git, that is, as long as you use Git the way it was designed to be used, commit your work often, and let Git take care of all the remembering. Enjoy!