Skip to main content

How To Recover Lost Git Branches

Daniel, at work, ran into a problem of accidentally removing a branch he had just created and made a commit to, thus loosing the days work. This was actually the fault of our internal scripts to manage the branching and merging policy we've set up. By "internal" I mean that I wrote them and it was my fault his whole day of work was gone, so that left it up to me to figure out how to repair the situation and salvage the current commit back from the ether. I thought it might be good to document, in the case that anyone else needs to do this.

This works in the case of branch A existing and branch B being removed after a single commit on it and branch B being from A. This means we know commit A and we need to find an unnamed commit, what was B, to recover it.

I can demonstrate the recovery process with a simple transcript.

ironfroggy:/tmp ironfroggy$ mkdir A
ironfroggy:/tmp ironfroggy$ cd A
ironfroggy:/tmp/A ironfroggy$ git init
Initialized empty Git repository in .git/
ironfroggy:/tmp/A ironfroggy$ cat >> test
a b c
ironfroggy:/tmp/A ironfroggy$ git add test
ironfroggy:/tmp/A ironfroggy$ echo "commit 1" | git commit -m test
Created initial commit 5b2401e: test
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 test
ironfroggy:/tmp/A ironfroggy$ cat >> test
1 2 3
ironfroggy:/tmp/A ironfroggy$ git add test
ironfroggy:/tmp/A ironfroggy$ echo "commit 2" | git commit -m test
Created commit 08217dc: test
1 files changed, 1 insertions(+), 0 deletions(-)
ironfroggy:/tmp/A ironfroggy$ git reset --hard HEAD^
HEAD is now at 5b2401e... test
ironfroggy:/tmp/A ironfroggy$ git reflog show master
5b2401e... master@{0}: reset --hard HEAD^
08217dc... master@{1}: commit: test
ironfroggy:/tmp/A ironfroggy$ git log master
commit 5b2401e38d400c3039a53a036f2d98f75d544056
Author: Calvin Spealman
Date: Sun Dec 2 13:27:25 2007 -0500

test
ironfroggy:/tmp/A ironfroggy$ git log 08217dc
commit 08217dc6ef8f8117d6c9e4bca6fe7f18f78559b6
Author: Calvin Spealman
Date: Sun Dec 2 13:28:01 2007 -0500

test

commit 5b2401e38d400c3039a53a036f2d98f75d544056
Author: Calvin Spealman
Date: Sun Dec 2 13:27:25 2007 -0500

test
ironfroggy:/tmp/A ironfroggy$ git merge 08217dc6ef8f8117d6c9e4bca6fe7f18f78559b6
Updating 5b2401e..08217dc
Fast forward
test | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
ironfroggy:/tmp/A ironfroggy$ cat test
a b c
1 2 3


The red line is the dangerous move that lost your commit. Now, nothing is actually removed from the repository, except for when you perform a garbage collection, so I knew we could recover this somehow. After a short time looking through the docs and asking a question or two on IRC, I arrived at the solution. There are logs kept on the changes that happen to refs (or branches), so I could use that to see the log of when that commit happened. You can see that and the commit the follows when the reset occurs and undoes our commit. The commit id is in the short version, but if we look at the log of that commit, we get the full ID and can simply merge it back into our branch. A very clean solution, indeed.

For Daniel This Means...

He didn't loose his whole days work, but he should be doing local commits more often so that any problems with our scripts can't show up in the first place. At least the work was recovered, and in short time. He is saved, for one more day at least, from being a disgruntled co-worker.

For Git This Means...

I'd love to contribute, so I'm thinking the git-fsck command needs something that can do this for you. Some operation to locate child commits would be really useful in these situations. Once again, I'm very happy with the move to Git.

Comments

Jakub Narebski said…
Actually the post is not how to recover lost git branches (you would lost branches together with reflog after doing 'git branch -D [branchname]'), but how to recover lost history (like for example 'git reset --hard HEAD^'). To recover lost branches you would need 'git fsck --lost-found'...
Anonymous said…
Thanks man, This post saved me 3 days of hard work !
Anonymous said…
Even better, if you don't know the commit's hash code, you can access it via HEAD@{1}, or even master@{yesterday}.
Anonymous said…
I owe you a beer
Holy crap, thank you thank you!
Anonymous said…
@jnareb: woah, thank you for sharing, it saved my night!

Popular posts from this blog

CARDIAC: The Cardboard Computer

I am just so excited about this. CARDIAC. The Cardboard Computer. How cool is that? This piece of history is amazing and better than that: it is extremely accessible. This fantastic design was built in 1969 by David Hagelbarger at Bell Labs to explain what computers were to those who would otherwise have no exposure to them. Miraculously, the CARDIAC (CARDboard Interactive Aid to Computation) was able to actually function as a slow and rudimentary computer.  One of the most fascinating aspects of this gem is that at the time of its publication the scope it was able to demonstrate was actually useful in explaining what a computer was. Could you imagine trying to explain computers today with anything close to the CARDIAC? It had 100 memory locations and only ten instructions. The memory held signed 3-digit numbers (-999 through 999) and instructions could be encoded such that the first digit was the instruction and the second two digits were the address of memory to operate on

Statement Functions

At a small suggestion in #python, I wrote up a simple module that allows the use of many python statements in places requiring statements. This post serves as the announcement and documentation. You can find the release here . The pattern is the statement's keyword appended with a single underscore, so the first, of course, is print_. The example writes 'some+text' to an IOString for a URL query string. This mostly follows what it seems the print function will be in py3k. print_("some", "text", outfile=query_iostring, sep="+", end="") An obvious second choice was to wrap if statements. They take a condition value, and expect a truth value or callback an an optional else value or callback. Values and callbacks are named if_true, cb_true, if_false, and cb_false. if_(raw_input("Continue?")=="Y", cb_true=play_game, cb_false=quit) Of course, often your else might be an error case, so raising an exception could be useful

How To Teach Software Development

How To Teach Software Development Introduction Developers Quality Control Motivation Execution Businesses Students Schools Education is broken. Education about software development is even more broken. It is a sad observation of the industry from my eyes. I come to see good developers from what should be great educations as survivors, more than anything. Do they get a headstart from their education or do they overcome it? This is the first part in a series on software education. I want to open a discussion here. Please comment if you have thoughts. Blog about it, yourself. Write about how you disagree with me. Write more if you don't. We have a troubled industry. We care enough to do something about it. We hark on the bad developers the way people used to point at freak shows, but we only hurt ourselves but not improving the situation. We have to deal with their bad code. We are the twenty percent and we can't talk to the eighty percent, by definition, so we need to impro