aelmosalamy
aelmosalamy

Reputation: 120

How to recover from "git rm -rf ." and still retain uncomitted changes?

I had a bunch of changes added to the staging index by git add *, but not yet commited, I wanted to remove them, undo the add, so I did git rm -rf . to my surprise it removed them from my hard drive, I want a way to get them back; however, I have made huge changes and I would like to get those changes back.

TLDR: I deleted my whole project by git rm -rf ., I need some way to reset the deletion, so that I keep my uncommited changes and get my files back. please I am too scared that I might lose my whole project.

ANSWER: My repository is basically a website with a lot of content, Based on the answers below, I made 2 copies of my repository, let's call them copy A and copy B, for A I did git reset --hard to go back to the latest commit, I got my files back, but lost the changes I made to them. so for copy B I did git fsck --lost-found and went into the .git/lost-found/other/ directory which contained multiple hash-named versions of my files, I kept opening each of them, they were more than 60 files btw, each file I recognize I rename it to it's actual name and then place it instead of the older versions inside of copy A, at the end I deleted my original repo and I am using copy A now as my website. it is as thought nothing have happened now. One Little Stupid Mistake => 5 Hours of Pain. Don't repeat what I did, never ever.

right now git status shows all my files as "deleted: " and its in green.

Upvotes: 2

Views: 1387

Answers (5)

joran
joran

Reputation: 2883

git status gives you some hints on what commands you could use to restore the files:

$ git rm  -rf .
$ git status

On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

  deleted: file1
  deleted: file2

To unstage the deleted files:

$ git reset HEAD file1 file2
$ git status

On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    deleted:    file1
    deleted:    file2

no changes added to commit (use "git add" and/or "git commit -a")

Checkout the deleted files:

$ git checkout -- file1 file2
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

Upvotes: 1

Calum Halpin
Calum Halpin

Reputation: 2095

The first thing you should do, as the commenters said, is backup your project directory.

You can get your committed files back with git reset --hard HEAD. Since you cleared your staging tree your uncommitted changes aren't in git anymore (as several other answers point out, this may not be true) so your only chance of recovering them is any backups you or your OS might have made.

I'm sorry it doesn't help with the immediate situation but for future reference you can unstage your changes with git reset HEAD.

Upvotes: 0

Thomas
Thomas

Reputation: 181745

There may be hope: the objects that you previously added to the index may still exist, although they are no longer referenced. I did the following to test:

$ git init test
$ cd test
$ echo hello > README.md
$ git add README.md
$ git commit -m"Add README.md"
$ echo world >> README.md
$ cat README.md
hello
world
$ git add README.md
$ git rm -f README.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    README.md

$ git fsck
Checking object directories: 100% (256/256), done.
unreachable blob 94954abda49de8615a048f8d2e64b5de848e27a1
$ git show 94954abda49de8615a048f8d2e64b5de848e27a1
hello
world

So even after git rm -f, the file's contents as added to the index still exist. You can dump them all into .git/lost-found/other by running git fsck --lost-found.

The index itself isn't stored as a Git object, but resides directly in .git/index, so I think this has been irreversibly overwritten. That means the paths to the files, as well as any metadata such as permissions, have been lost.

Upvotes: 3

Khoyo
Khoyo

Reputation: 1247

Using

find .git/objects/ -type f -printf "%T+\t%p\n" \
  | sort \
  | sed 's:.*git/objects/\(.*\)/\(.*\):\1\2:' \
  | while read object; do
       echo -n "$object "; 
       git cat-file -t "$object";
    done

You should get a list of objects and type, ordered by time.

For example, on a test repo:

908553f63e9126f933b690970d41adc3377e3360 blob
31e0d0e213c9976308fbb91c542ced9218fa8f6a tree
5b181f91c49d287b4670fcf3545656dc0c0ef5f4 commit
de683137e0b2d0a40f766307e81999986e4b31c2 blob
086cb344a4fd8a9d671006b8e5844f2437faa3ab tree
930ba9809b5d50410b72c3fbff111d948f1027fa commit
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 blob
40cda8e010466dc7a4a0eb107e7e45804290685e blob

The last two blobs were created after the last commit, and if you print them with eg. git cat-file -p 40cda8e010466dc7a4a0eb107e7e45804290685e, you should be able to recover the file contents.

You may also get some trees that way (use git ls-tree <tree-id> to print them), but not the top-level one, since it was stored directly in the index.

Upvotes: 4

ElpieKay
ElpieKay

Reputation: 30868

For the files that have been committed, git reset --hard would recover them all.

For those added but not committed, try git fsck --lost-found. It will print dangling blobs and make their copies in .git/lost-found/other. You can either run git cat-file -p $blob or cat .git/lost-found/other/$blob to see the content. The blobs do not record the file paths, so you need to map the content and the file path by memory, or by a keyword which is not newly modified with git grep $keyword. After you find out a path for a blob, run cp .git/lost-found/other/$blob $path to recover it.

Upvotes: 3

Related Questions