Recover an (accidental) git force push with the GitHub API

Imagine somebody force pushed to a branch. That person is unreachable now, and some critical code is gone. You never had that repo cloned or branch checked out. Can you do something?

It turns out, yes. At least if you are using GitHub (GH).

With the GH API, there are things you can do that are not otherwise available in the web UI.

For example, you can get a list of all things that happened to a repo using the list repository events endpoint.

curl \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR-TOKEN>" \
https://api.github.com/repos/OWNER/REPO/events

This includes all kinds of activities, like somebody leaving a comment on a PR, somebody starring the repo, or somebody pushing a commit.

[
{
"id": "22237752260",
"type": "WatchEvent",
"actor": {},
"repo": {},
"payload": {
"action": "started"
},
"public": true,
"created_at": "2022-06-08T23:29:25Z"
},
{
"id": "22249084964",
"type": "PushEvent",
"actor": {},
"repo": {},
"payload": {
"push_id": 10115855396,
"size": 1,
"distinct_size": 1,
"ref": "refs/heads/master",
"head": "7a8f3ac80e2ad2f6842cb86f576d4bfe2c03e300",
"before": "883efe034920928c47fe18598c01249d1a9fdabd",
"commits": [
{
"sha": "7a8f3ac80e2ad2f6842cb86f576d4bfe2c03e300",
"author": {},
"message": "commit",
"distinct": true,
"url": "https://api.github.com/repos/octocat/Hello-World/commits/7a8f3ac80e2ad2f6842cb86f576d4bfe2c03e300"
}
]
},
"public": true,
"created_at": "2022-06-09T12:47:28Z"
}
]

The PushEvent includes the before, which is the "The SHA of the most recent commit on ref before the push". To put it otherwise, the "state that we want to recover".

Now we can take advantage of the create a reference endpoint. This would allow us to create a new branch with the state right before the force push. The SHA identifies the state. This is what we pass to the endpoint, together with the branch name we want.

curl \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR-TOKEN>" \
https://api.github.com/repos/OWNER/REPO/git/refs \
-d '{"ref":"refs/heads/featureA","sha":"aa218f56b14c9653891f9e74264a383fa43fefbd"}'

This is it. "We saved the world."


I found the solution on Stack Overflow. This is just a more narrative explanation with updated links to the API.