In a recent blog post I had to recover a deleted file from a public git repository to share with you. Today I will show you how it’s done.

In this particular case, I needed a file with old UDEV rules. I knew the name and location of the file before it was deleted. And of course, the way git works, a file is never truly deleted as it’s all in the git commit history.

The file in question is 70-old-u2f.rules from the repository https://github.com/Yubico/libu2f-host/

Let’s recover the file, starting by cloning the repository (which by default includes the full history):

git clone https://github.com/Yubico/libu2f-host/
cd libu2f-host

Now that we’re in the git repository, let’s search for commits that involved the file we’re looking for:

git log --all --full-history -- 70-old-u2f.rules

If we don’t know the path or exact file name, we can also use asterisks for wildcard search like:

git log --all --full-history -- *old-u2f*

Either way, we will see a result like this:

commit 17d67ff89fb98d4148b63b96a397ea6a648c4ecc
Merge: 5a941ed 2892b9c
Author: Klas Lindfors <[email protected]>
Date:   Wed Feb 13 15:14:24 2019 +0100

    Merge branch 'pr-114'

commit 5a941ed0726f7efd23ebd613b4207ac335820dc5
Author: Klas Lindfors <[email protected]>
Date:   Wed Feb 13 15:12:12 2019 +0100

    udev: drop 70-old-u2f.rules

As you can see, the second commit looks like the one where the file was deleted (dropped). This means we want to restore the file from the commit before this one, to get the latest version before deletion. So now we copy the hash of the commit where the file was deleted and do:

git checkout 5a941ed0726f7efd23ebd613b4207ac335820dc5^ -- 70-old-u2f.rules

And ta-da! The file is now restored. The reason we could use the commit hash from the moment it was deleted, is because we added a caret (^) at the end of the hash, which means the commit right before this hash is the one to be restored. We could instead, of course, specify the commit hash of the previous commit directly.

Now, if you don’t want to restore the file to the filesystem and instead just display the diff in the terminal, you could instead do:

git show 5a941ed0726f7efd23ebd613b4207ac335820dc5 -- 70-old-u2f.rules

Note that this time we did not add a caret (^) to the end of the hash. Because we really do want the commit from when the file was deleted, to see the diff of what was deleted.

In all those commands, you can specify the file with asterisks if you’re not sure about the exact path. But be careful! If two files with the same name exist but in a different path, and you omit the exact path, you might end up reverting all files with that name to their previous version. You have been warned. ;)

And that’s all there is to it!