9

Normally, you can do:

$ echo "Stanley, you must beware of the Drive bee" > file-a
$ echo "What's a Drive bee?" > file-b
$ git init .
$ git add file-b
$ git commit file-b -m "We don't know, but whatever error you make with it could be fatal."
$ git reset --hard HEAD
$ ls
file-a file-b

I think I did something really bad:

$ echo "What are you doing, you darn ?" > file-a
$ echo "Can't you see I'm trying to drive?" > file-
$ git init .
$ git add -A
$ git commit file- -m "Oh, my God! [It's] the Drive !"
$ git reset --hard HEAD
$ ls
file-

Result: all staged, but uncommitted, files deleted 0_o

git reset --hard HEAD\^
fatal: ambiguous argument 'HEAD^': unknown revision or path not in the working tree.

Is there anything I can do to recover the file I just deleted? In other words, is it possible to restore a git repository to the condition it was before (or when) the git add -A command was issued?

dcow
  • 7,765
  • 3
  • 45
  • 65

3 Answers3

20

Yes, you are actually really lucky. What you added to Git’s index is there, in some way. In fact, Git does create blob objects for each file already when it is added to the index. The index itself only stores the tree objects.

So yes, there were blob objects created for your staged files. All you lose are the tree information, i.e. path and filename, but you can recover the content.

Try running git fsck and you should get a list of dangling blobs:

Checking object directories: 100% (256/256), done.
dangling blob ac28af8d84fc71eb247ccf665c6d0f4bf1822520
dangling blob 2d152ff9f09cb08ebc495f453da63eddaa9e249f
dangling blob cd9567427762cd8066b4e802e5c170a31a026100

You can then recover the contents by doing git cat-file -p ac28af8d. For example you can pipe that to a file:

git cat-file -p ac28af8d > recovered-file

Do that for all of them and you have them back.

poke
  • 369,085
  • 72
  • 557
  • 602
  • 2
    Just a mostly irrelevant note that you can use `git show` as an alternative to `git cat-file -p`. Great answer. I didn't realize this myself. – Michael Mior Jul 23 '12 at 22:39
  • @MichaelMior True, I usually use `cat-file -p` though as it gives me the same output for any object type ;) – poke Jul 23 '12 at 22:42
  • Thanks! I didn't know this either. The somewhat sad reality is that it will be easier to just recreate all the files since this was a relatively new Android project and a lot of files got deleted meaning a relatively large tree is lost. But, I should be able to get the code I wrote from the blobs using your solution and just add it back in. (= – dcow Jul 23 '12 at 22:47
  • Yeah, unfortunately the index contains all the staged tree objects, so when the index is lost, the trees are lost too. And `reset --hard` is one of the many ways to get rid of your index… ;) – poke Jul 23 '12 at 22:57
  • To add to this, if you have many dangling objects and want to narrow it down, use the file system: `ls -lrt .git/objects` to show you the newest prefixes, then do it again in an object dir, eg `ls -lrt .git/objects/f8`. Finally `git cat-file -p f8`. – mik3y Jan 27 '15 at 17:17
  • Another option if you have a lot of dangling objects is `git fsck --lost-found`. This will write all dangling blobs into `.git/lost-found/other`. Then you can do e.g. `less .git/lost-found/other/*` and cycle through files with `:n` (next) and `:p` (previous), or grep through the files to find the one you were looking for. – user149408 Nov 21 '17 at 22:58
2

The following script restore all dangling blobs from git fsck output into files:

i=0; for x in `git fsck | grep "dangling blob [0-9a-f]" | cut -d ' ' -f 3`; do git cat-file -p $x > /tmp/test${i}; i=$((i+1)) ; done

As you can see, the output of each original file is restored into generic filename /tmp/test${i} ... I haven't found a way to restore the file name yet... the command I've tried is git ls-tree:

First I've took possible objects :

./.git/objects/37
./.git/objects/37/5187f5882593f7e52c8efef602d98f1f049c5d
./.git/objects/37/98be05fcbd7c79e08703aead800529b362332b

Then, I tried to do the ls-tree trick, but it failed to identify this as a valid object.

/<myproj>/.git/objects/37 ]$ git ls-tree 5187f5882593f7e52c8efef602d98f1f049c5d 
fatal: Not a valid object name 5187f5882593f7e52c8efef602d98f1f049c5d
Zohar81
  • 4,554
  • 5
  • 29
  • 82
0

I really like the following solution from this answer to a related Stack Overflow question:

  1. Use git log --diff-filter=D --summary to get all the commits which have deleted files and the files deleted
  2. Use git checkout $commit~1 filename to restore the deleted file.

It is concise and intuitive.

Community
  • 1
  • 1
red-goblin
  • 41
  • 3
  • Please don't post link only answers to other Stack Overflow questions. Instead, vote/flag to close as duplicate, or, if the question is not a duplicate, *tailor the answer to this specific question.* – Rizier123 Mar 06 '16 at 17:04
  • @red-goblin the commit was toast. You can't checkout from an index that doesn't exist S= (i.e. this is not a duplicate of the question you linked, and thus its answers don't apply correctly here, thanks though) – dcow Mar 07 '16 at 05:35