Another tool exists in Git that can help you find and recover lost commits and even blobs (files), which is git fsck
. The fsck
command tests the object database and verifies the SHA-1 ID of the objects and the connections they make. The command can also be used to find objects that are not reachable from any named reference, as it tests all the objects found in the database, which are under the .git/objects
folder.
Again, we'll use the hello world
repository. If you make a fresh clone, make sure to run the scripts for this chapter (04_undo_dirty.sh
), so there will be some objects for git fsck
to consider. The scripts can be found on the book's homepage. If you just reset the master
branch after performing the other recipes in the chapter, everything is ready.
We can create the fresh clone as follows:
$ git clone https://github.com/dvaske/hello_world_cookbook.git $ cd hello_world_cookbook
We can reset an existing clone as follows:
$ cd hello_world_cookbook $ git checkout master $ git reset --hard origin master HEAD is now at 3061dc6 Adds Java version of 'hello world'
$ git fsck --unreachable Checking object directories: 100% (256/256), done. unreachable commit 147240ad0297f85c9ca3ed513906d4b75209e83d unreachable blob b16cf63ab66605f9505c17c5affd88b34c9150ce unreachable commit 4c3b1e10d8876cd507bcf2072c85cc474f7fb93b
git show b16cf63ab66605f9505c17c5affd88b34c9150ce #include <stdio.h> void say_hello(void) { printf("hello, world "); } int main(void){ say_hello(); return 0; }
So the blob is the hello_world.c
file from the example which stashing away your changes before resetting a commit. Here we stashed away the file, performed a reset, and resurrected the file from the stash, but we never actually performed a commit. The stash
command, however, did add the file to the database, so it could find it again, and the file will continue to be there until the garbage collection kicks in or forever if it is referenced by a commit in the general history.
$ git show 147240ad0297f85c9ca3ed513906d4b75209e83d commit 147240ad0297f85c9ca3ed513906d4b75209e83d Merge: 3061dc6 4c3b1e1 Author: Aske Olsson <[email protected]> Date: Thu Mar 13 23:19:37 2014 +0100 WIP on master: 3061dc6 Adds Java version of 'hello world' diff --cc hello_world.c index 881ef55,881ef55..b16cf63 --- a/hello_world.c +++ b/hello_world.c @@@ -1,7 -1,7 +1,10 @@@ #include <stdio.h> --int main(void){ ++void say_hello(void) { printf("hello, world "); ++} ++int main(void){ ++ say_hello(); return 0; --} ++} $ git show 4c3b1e10d8876cd507bcf2072c85cc474f7fb93b commit 4c3b1e10d8876cd507bcf2072c85cc474f7fb93b Author: Aske Olsson <[email protected]> Date: Thu Mar 13 23:19:37 2014 +0100 index on master: 3061dc6 Adds Java version of 'hello world'
Both the commits are actually commits we made when we stashed away our changes in the previous example. The stash
command creates a commit object with the contents of the staging area, and a merge commit merging HEAD
and the commit with the index with the content of the working directory (tracked files only). As we resurrected our stashed changes in the previous example, we no longer have any reference pointing at the preceding commits; therefore, they are found by git fsck
.
The git fsck
command will test all the objects found under the .git/objects
folder. When the --unreachable
option is given, it will report the objects found that can't be reached from another reference; the reference can be a branch, a tag, a commit, a tree, the reflog, or stashed away changes.
git stash
command