2

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.

aelmosalamy
  • 100
  • 9
  • 2
    I think you are out of luck. If you had typed `rm -rf .` then your changes would still have been in Git's index, but by using `git rm` both the index and the working copy on disk were updated. But just in case I'm wrong, MAKE A COPY of the entire project directory including the `.git` directory before you experiment any more. – Thomas Jun 14 '19 at 12:03
  • Yes, I learned the hard way, If you mess with git, git will mess you. Rule No. 1 that must be taught in any of the git courses I took is MAKE A COPY IF YOU ARE NOT SURE WHAT YOU ARE DOING. – aelmosalamy Jun 14 '19 at 12:07
  • Thinking further, it may be that the previous version of the index, or at least the files that were contained in it, are still around as blobs in your repository. Hopefully someone with more knowledge about this will chime in. – Thomas Jun 14 '19 at 12:08
  • *Commits* are saved forever (or at least, for as long as you can still find the hash ID—see [Think Like (a) Git](http://think-like-a-git.net/) for details). Stuff you're actively messing with, in the index and work-tree, are *not* saved forever. – torek Jun 14 '19 at 15:03

5 Answers5

4

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.

Khoyo
  • 1,253
  • 11
  • 20
3

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.

ElpieKay
  • 27,194
  • 6
  • 32
  • 53
3

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.

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • I used `git fsck --lost-found` but the jumble of files there are a mixture of my previous commits and my uncommitted changes, I believe there is way of just *undoing* any git commands you do, it isn't called *version control* for nothing. – aelmosalamy Jun 14 '19 at 12:48
  • The way you recreated the problem at hand is ingenious, salutes to you! – aelmosalamy Jun 14 '19 at 14:40
1

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
joran
  • 2,815
  • 16
  • 18
0

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.

Calum Halpin
  • 1,945
  • 1
  • 10
  • 20
  • AFAIK, `git add *` moves changes from working directory to staging index, `git rm --cached` does the opposite, while `git commit` moves changes from staging index to .git repository, what does `git reset` do? what am I missing? – aelmosalamy Jun 14 '19 at 12:27
  • `git reset --hard HEAD` will set the tip of both your staging tree to the tip of your current branch and also checkout all files from your current branch. It will reset everything to your last commit. – Calum Halpin Jun 14 '19 at 12:30
  • 2
    `git rm --cached` doesn't do what you think it does. Use `git reset ` to remove a file from the index but not from your working directory. – Khoyo Jun 14 '19 at 12:30
  • Thanks that cleared some info, or more appropriately: "demystified". – aelmosalamy Jun 14 '19 at 12:58