Recovering a commit from Githubs Reflog

Gits reflog is a powerful feature that allows a user to recover from almost any error. Github doesnt offer direct access to the reflog of the backing repository but using a couple of Githubs APIs, we can recover a commit from the remote reflog..

Object Partners

This morning when I arrived to work I was presented with a very interesting Git/Github problem by a co-worker. He had pushed a commit from his home computer last night and this morning he had force pushed over the top of it without realizing it. Normally, this isn’t such a big problem since the commit would still exist in Git’s reflog and you could recover it from there. The problem was that he never did a fetch from Github before doing the force push. This effectively erased the commit and made it unrecoverable without his home computer. Or so we initially thought…

First, use Github’s Events API to retrieve the commit SHA.$ curl https://api.github.com/repos/<user>/<repo>/eventsThis will return a JSON response which you can read through to find the commit you lost. Use the commit message and approximate times to narrow your search. Here is an example portion of the response (I’ve deleted some info, but you should get the point):

{
    "id": "1970551769",
    "type": "PushEvent",
    "actor": {
      "id": 563541,
      "login": "<user>",
      "gravatar_id": "<id>",
      "url": "https://api.github.com/users/<user>",
      "avatar_url": "<url>"
    },
    "repo": {
      "id": 9652839,
      "name": "<user>/<repo>",
      "url": "https://api.github.com/repos/<user>/<repo>"
    },
    "payload": {
      "push_id": 303837533,
      "size": 1,
      "distinct_size": 1,
      "ref": "refs/heads/<branch>",
      "head": "a973ddd28d599c9ba128de56182f8769d2b9843b",
      "before": "4ef3d74316c04c892d17250f0ba251b328274e5f",
      "commits": [
        {
          "sha": "384f275933d5b762cdb27175aeff1263a8a7b7f7",
          "author": {
            "email": "<email>",
            "name": "<author>"
          },
          "message": "<commit message>",
          "distinct": true,
          "url": "https://api.github.com/repos/<user>/<repo>/commits/384f275933d5b762cdb27175aeff1263a8a7b7f7"
        }
      ]
    },
    "public": false,
    "created_at": "2014-02-06T14:05:17Z"
}

Next, use Github’s Refs API to create a new branch pointing to that commit:$ curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d '{"ref":"refs/heads/D-commit", "sha":"384f275933d5b762cdb27175aeff1263a8a7b7f7"}' https://api.github.com/repos/<user>/<repo>/git/refsBreaking down the JSON in this request we have this:

{
  "ref": "refs/heads/D-commit",
  "sha": "384f275933d5b762cdb27175aeff1263a8a7b7f7"
}

The “ref” in the JSON is Git ref we want to create, it MUST be of the form: refs/heads/, where is the name of the branch we want to create in Git. The “sha” is the commit that we want this new branch to point to.

If successful Github will respond back with something like this:

HTTP/1.1 201 Created
Server: GitHub.com
Date: Thu, 06 Feb 2014 14:47:02 GMT
Content-Type: application/json; charset=utf-8
Status: 201 Created
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4991
X-RateLimit-Reset: 1391700034
Cache-Control: private, max-age=60, s-maxage=60
ETag: "43e54fee5cf29edd01fa8bfce094ed1b"
Location: https://api.github.com/repos/<user>/<repo>/git/refs/heads/D-commit
Vary: Accept, Authorization, Cookie, X-GitHub-OTP
X-GitHub-Media-Type: github.beta
X-Content-Type-Options: nosniff
Content-Length: 339
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
Access-Control-Allow-Origin: *
X-GitHub-Request-Id: 4389B15A:3E60:1D2506F:52F3A066  

{
  "ref": "refs/heads/D-commit",
  "url": "https://api.github.com/repos/<user>/<repo>/git/refs/heads/D-commit",
  "object": {
    "sha": "384f275933d5b762cdb27175aeff1263a8a7b7f7",
    "type": "commit",
    "url": "https://api.github.com/repos/<user>/<repo>/git/commits/384f275933d5b762cdb27175aeff1263a8a7b7f7"
  }
}

Now if you pull up your repository in Github, you will see the new branch and doing a ‘git fetch’ will retrieve that new branch to your local repository. From there you can cherry-pick or merge the commits back into your work.

Share this Post

Related Blog Posts

Unknown

Getting started with git svn

February 4th, 2014

Getting started with git svn

Object Partners
Unknown

The Creatives on Agile and QA teams

January 10th, 2014

Agile and QA teams both have their compliment of Creatives who don’t fit the mold yet provide tremendous value to the team through the very traits that set them apart.

Object Partners
Unknown

Alternative Test Data Strategies for Automation

January 7th, 2014

The test data you have available creates a major design constraint for your automated tests, impacting both test coverage and validity. You can address this challenge in several ways.

Object Partners

About the author