Eyes, JAPAN Blog > Git literacy : Recovery

Git literacy : Recovery

denvazh

この記事は1年以上前に書かれたもので、内容が古い可能性がありますのでご注意ください。

Getting started

Freedom is a nice thing. Really. However lack of borders and regulations often means that you should maintain them yourself. Git is a very flexible and indeed a tool of those who like freedom along with flexibility in development. But, as any other tool it is a masterpiece in the hands of master, and burden for those who uses tool without thinking about consequences. In case of problems, however, certain ready solutions might not work. Let’s see some small examples how to find list commits ( usually deleted by mistake ) and recover them back in the development tree.

The problem

As soon as it is usually not desired to deliberately make mistakes in the current development tree of active project. Let’s make some testbed and try to mess it up, so that we have a chance to study how to recover it later. First, we create repository and add some file, then we move to different branch and do actual stuff there:

$: mkdir -p /tmp/testbed
$: cd /tmp/testbed
$: git init
$: touch test.sh
$: git add test.sh
$: git commit -a -m "Initial commit"
$: git checkout -b scripting
$: git branch
  master
* scripting

Now we have repository with one empty file and the first commit. Now we can add some content to the file:

#!/usr/bin/env bash

function greeting()
{
        echo "My name is `basename $SHELL`. Nice to meet you."
}

and commit

$: git commit -a -m "greetings now possible"

Let’s append some more content:

function user()
{
        echo "Please introduce yourself"
        read NAME
        echo "Welcome, $NAME"
}

and commit

$: git commit -a -m "user introduction added"

Now let’s add part that actually uses it:

function init()
{
greeting
user
}

init

commit and check the log

$: git commit -a -m "Init part added"
$: git log --oneline
fb4c2b4 Init part added
e71b90d user introduction added
c49d349 greetings now possible
7d4fc7f Initial commit

Now everything is ready and even code launches correctly:

$: ./test.sh 
My name is bash. Nice to meet you.
Please introduce yourself
Denis
Welcome, Denis

From riches to rags and the way back

Suppose you didn’t like what you’ve did in the last commit and decided to get rid of it hard way.

$: git reset --hard HEAD^
HEAD is now at e71b90d user introduction added

Now if we check the commit log, we can clarify that commit is gone.

$: git log --oneline     
e71b90d user introduction added
c49d349 greetings now possible
7d4fc7f Initial commit

Now few words about reset. In fact, this command do not actually delete certain commit, but only removes its reference from commit tree. In other words, this is dangling commit, as soon as it has no reference to it, thus its possible to access it with it SHA1 key.
NOTE1: if you run garbage collector( $: git gc ) right after reset, it will be not possible to recover dangling commits.
NOTE2: if you have remote repository as well, you can fix everything with $: git pull ( or $: git pull –all if you not in the master branch )

To recover we have to check git with its filesystem check tool and get necessary SHA1 key of dangling commit.

$: git fsck  --lost-found
dangling commit fb4c2b4bc4c77c59bf5c232a8ce06ddc385aa3b5

From this point we can directly checkout and check the contents of lost commit:

$: git checkout fb4c2b4bc4c77c59bf5c232a8ce06ddc385aa3b5
Note: checking out 'fb4c2b4bc4c77c59bf5c232a8ce06ddc385aa3b5'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at fb4c2b4... Init part added

Let’s verify, that we now actually in the lost commit:

$: git log --oneline
fb4c2b4 Init part added
e71b90d user introduction added
c49d349 greetings now possible
7d4fc7f Initial commit

$: git branch
* (no branch)
  master
  scripting

The most safest and easy way to recover, would be to merge dangling commit using its SHA1 key. Let’s do it.

$: git merge fb4c2b4bc4c77c59bf5c232a8ce06ddc385aa3b5
Updating e71b90d..fb4c2b4
Fast-forward
 test.sh |   11 +++++++++--
 1 files changed, 9 insertions(+), 2 deletions(-)

Checking.

$: git branch
  master
* scripting

$: git log --oneline
fb4c2b4 Init part added
e71b90d user introduction added
c49d349 greetings now possible
7d4fc7f Initial commit

Everything is okay so far. Let’s try another way of doing it. This time we create separate branch out of the dangling commit and then merge this branch back to one we’ve used so far.

$: git branch recovery fb4c2b4bc4c77c59bf5c232a8ce06ddc385aa3b5

We can compare to actually check the difference between branches:

$: git log scripting --oneline
e71b90d user introduction added
c49d349 greetings now possible
7d4fc7f Initial commit

$: git log recovery --oneline
fb4c2b4 Init part added
e71b90d user introduction added
c49d349 greetings now possible
7d4fc7f Initial commit

Now we can merge recovery branch to our current ( scripting ) and then delete it, because we no longer need it.

$: git merge recovery
Updating e71b90d..fb4c2b4
Fast-forward
 test.sh |   11 +++++++++--
 1 files changed, 9 insertions(+), 2 deletions(-)

$: git branch -d recovery
Deleted branch recovery (was fb4c2b4).

Conclusion

There are many ways how to recover the information. However, it is crucial to know what recovery tools is available by default and general recovery pattern: investigate the problem, develop solution, apply solution to the actual problem.

More information about the topic

Recovery: http://gitready.com/advanced/2009/01/17/restoring-lost-commits.html
Maintenance: http://progit.org/book/ch9-7.html

Comments are closed.