I am trying to recover my work. I stupidly did git reset --hard
, but before that I've done only get add .
and didn't do git commit
. Please help! Here is my log:
MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# modified: .gitignore
...
MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api
Is it possible to undo git reset --hard
in this situation?
We can use the command git fsck to recover the files after a hard reset.
The reset command actually will never delete a commit or change the content of a branch that you do not have currently checked out. It is just simply moving your HEAD pointer around. A very useful tool that is worth mentioning is the reflog.
I just did a git reset --hard
and lost one commit. But I knew the commit hash, so I was able to do git cherry-pick COMMIT_HASH
to restore it.
I did this within a few minutes of losing the commit, so it may work for some of you.
You should be able to recover any files back that you added to the index (e.g, as in your situation, with git add .
) although it might be a bit of work. In order to add a file to the index, git adds it to the object database, which means it can be recovered so long as garbage collection hasn't happened yet. There's an example of how to do this given in Jakub Narębski's answer here:
However, I tried that out on a test repository, and there were a couple of problems - --cached
should be --cache
, and I found that it didn't actually create the .git/lost-found
directory. However, the following steps worked for me:
git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")
That should output all objects in the object database that aren't reachable by any ref, in the index, or via the reflog. The output will look something like this:
unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03
... and for each of those blobs, you can do:
git show 907b308
To output the contents of the file.
Update in response to sehe's comment below:
If you find that you have many commits and trees listed in the output from that command, you may want to remove from the output any objects which are referenced from unreferenced commits. (Typically you can get back to these commits via the reflog anyway - we're just interested in objects that have been added to the index but can never be found via a commit.)
First, save the output of the command, with:
git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all
Now the object names of those unreachable commits can be found with:
egrep commit all | cut -d ' ' -f 3
So you can find just the trees and objects that have been added to the index, but not committed at any point, with:
git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
$(egrep commit all | cut -d ' ' -f 3)
That enormously cuts down the number of objects you'll have to consider.
Update: Philip Oakley below suggests another way of cutting down the number of objects to consider, which is to just consider the most recently modified files under .git/objects
. You can find these with:
find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort
(I found that find
invocation here.) The end of that list might look like:
2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a
In which case you can see those objects with:
git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a
(Note that you have to remove the /
at the end of the path to get the object name.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With