87

I added a new file F1 and made changes to another file F2 but then did a git reset --hard HEAD^ and I have lost all the changes to the files.

Is there some way, I can get them back?

I did look at a related question here: How can I undo git reset --hard HEAD~1? but, that question assumes that the one has done a Git commit.

Yuri
  • 4,254
  • 1
  • 29
  • 46
Susheel Javadi
  • 3,034
  • 3
  • 32
  • 34
  • 1
    possible duplicate of [Undo git reset --hard with uncommitted files in the staging area](http://stackoverflow.com/questions/7374069/undo-git-reset-hard-with-uncommitted-files-in-the-staging-area) – Ajedi32 Jan 15 '15 at 18:49
  • Now there is a git script that does this out of the box: https://github.com/pendashteh/git-recover-index – Alexar Jan 31 '16 at 07:18

6 Answers6

170

You can (with some work) recover state of file at the last "git add <file>". You can use

$ git fsck --cache --no-reflogs --lost-found --dangling HEAD

and then examine files in '.git/lost-found/other' directory.

Please read git fsck manpage.

Flow
  • 23,572
  • 15
  • 99
  • 156
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
42

(I'm assuming that the missing file is not part of any commit. Otherwise, git log --all -g --diff-filter=D --stat is your friend.)

  1. Get list of unreachable files that git knows a file name:

    git fsck --unreachable --no-reflogs --no-cache HEAD | fgrep " tree " \
    | cut -d " " -f3 | xargs -r -n1 git ls-tree \
    | fgrep " blob " | cut -d " " -f 3- | sort -k2 -u
    
  2. If you see something interesting, git cat-file blob SHA-1-of-interesting-file will output the file to standard output. (Example: git cat-file blob b8f0bdf56 > recovered-logo.png)

Unfortunately, if the missing file is not part of the any commit, git does not have a timestamp and as such, you cannot print various versions of files ordered by time.

If the missing file has never been staged (git stage or git add) or stashed (git stash), you're pretty much out of luck because as far as git knows, the file never did exist. (You may still try doing a git fsck --no-reflogs --lost-found and looking around in directory .git/lost-found/other to see if you have anything worth keeping in case git indeed has a copy of your missing file by some lucky accident. You do not have file names to help you in this case, only file contents.)

In case you just lost some commits (instead of just files), you'll probably want to run something like this:

gitk --all $( git fsck | awk '/dangling commit/ {print $3}'; git log -g --pretty='format:%H' )

That will run gitk with all the branches, all the reflog and all the dangling commits. You may want to add -n 10000 or some other limit in case your repo has really many commits (say linux kernel). If you do not have gitk, you may instead run lesser version using only command line like this:

git log --all --decorate --stat --graph --date-order $( git fsck | awk '/dangling commit/ {print $3}'; git log -g --pretty='format:%H' )

or a version with less verbose output

git log --all --decorate --oneline --graph --date-order $( git fsck | awk '/dangling commit/ {print $3}'; git log -g --pretty='format:%H' )

If you see some commit you want to save as branch recovered1, simply do git checkout -b recovered1 <sha1-of-the-commit>.

Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112
  • 2
    Wow! just found an untracked file, which got accidentally discarded by me (without even committing it), using `git fsck --no-reflogs --lost-found` and then searching it inside the directory `.git/lost-found/other` (with _Notepade++_'s _Find in Files_ search). Thank you! – Ofir Nov 25 '18 at 11:48
  • I have to add that I seriously recommend doing automated versioned backups over trying to recover files from git history. – Mikko Rantalainen Mar 11 '21 at 12:07
  • omg thank you! @Ofir, the same worked for me!! "$ git fsck --no-reflogs --lost-found" then search through files in ".git/lost-found/other" – greenhouse Jun 12 '21 at 00:27
7

Try this http://gitready.com/advanced/2009/01/17/restoring-lost-commits.html

I got a heart attack for the changes I lost. But after following this post. I got my changes back

Nav
  • 19,885
  • 27
  • 92
  • 135
Ravi Krishna P
  • 139
  • 1
  • 8
6

There is a git plugin that does this out of the box:

https://github.com/pendashteh/git-recover-index

$ cd /path/to/disatered/repo
$ git clone git@github.com:pendashteh/git-recover-index.git $HOME/.git-recover-index
$ $HOME/.git-recover-index/git-recover-index.sh
Alexar
  • 1,858
  • 5
  • 24
  • 34
4

Actually, if you've added the object to the index (by using git add), there is a blob created for that state of the object - but there is no tree (and thus, commit) object that is referring to it. This is how one gets a 'dangling' loose object file, and if you run git fsck it will show you the unreferenced blob (git gc will delete these types of objects if it is run).

Because of this, you can use the reflog, if you have it enabled, to try and restore the index state for your file F1 that has been added. If you haven't added F2 at all, then as Greg said, git doesn't know anything about it and you're out of luck there.

Matt Enright
  • 7,245
  • 4
  • 33
  • 32
0

If you use the VSCode, you can use the Timelime feature (by clicking right button mouse on changed file and choose "Open Timeline")