DEV Community

Alan North
Alan North

Posted on

How to Recover Locally Deleted Files From Github

So let's just say, hypothetically, you rewrote your git history or created a fixup commit that deleted a file, force pushed to github, deleted the repo locally, then realized you accidentally deleted one file too many.

I know, is it possible to commit so many sins at once?

This did not happen with code for me, it was just some dotfiles stuff. I was moving things around and removed a sensitive file from the commit history, then accidentally deleted the repo, only to realize I hadn't backed up the sensitive file I still needed.

First Step, Don't Panic

If you did not delete the repo, you're good. You can search how to use git reflog to recover it.

Otherwise github should keep the commits for 90 days.

Now I found the partial answer to how to do this here.

The gist of it is we can use the github api to find the commit and extract a patch. Problem is with a private repo this answer won't work. Additionally if we committed multiple files we don't want to have to extract each patch as suggested by the answer. We just want the full patch.

Get the Github CLI

If you don't have it already you will need the github cli. Authenticate with gh auth, and now we can use the gh api function.

Find Commit to Recover

First, if we need to find which commit we need to recover we can search through the events:

gh api repos/{OWNER}/{REPO}/events
Enter fullscreen mode Exit fullscreen mode

We can stick the result in a temp file for easier searching.

gh api repos/{OWNER}/{REPO}/events > temp
Enter fullscreen mode Exit fullscreen mode

And we can then make it easier to find the commit by filtering the response with jq.

cat temp | jq ".[].payload.commits.[] | {url: .url, author: .author.name, message: .message}"
Enter fullscreen mode Exit fullscreen mode

Once we find our commit we can query the url (just the end bit see here):

gh api repos/{OWNER}/{REPO}/commits/{SHA}
Enter fullscreen mode Exit fullscreen mode

This will give us a json response which is not what we want but we can use it if we want to further find if it contains the correct changes we're looking for.

gh api repos/{OWNER}/{REPO}/commits/{SHA} | jq ".files[] | {status:.status, filename:.filename, patch: .patch}"
Enter fullscreen mode Exit fullscreen mode

Get the Patch

To actually get the patch we will need to change a header.

# save the patch somewhere not in the repo
gh api -H "Accept: application/vnd.github.patch" repos/{OWNER}/{REPO}/commits/{SHA} > patch
Enter fullscreen mode Exit fullscreen mode

Apply the patch.

We can then apply it (be sure to have committed any changes beforehand). How to apply it can depend, but the following should work even if the repo doesn't have the files anymore, you can then recover what you need:

git apply /path/to/patch --3way --ignore-space-change --ignore-whitespace
Enter fullscreen mode Exit fullscreen mode

Further Notes

Note that if the patch contains binary file changes, applying them might bork your terminal. You can try sending reset one or two times, or try with a different terminal (Konsole worked for me).

Top comments (0)