Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
325 views
in Technique[技术] by (71.8m points)

git commit - Recover files that were added to the index but then removed by a git reset

I added some files to the index but then by mistake I deleted them with git reset --hard. How do I recover them? Here's what happened:

  1. I added all files using git add .
  2. I then committed
  3. When I checked the status, there were still files that weren't included in the commit from the add, which was strange
  4. I added the untracked files again and it worked this time
  5. But I wanted everything to be in 1 single commit so I looked up how to unstage what I just committed
  6. I used git reset --hard HEAD^ — bad idea obviously, all files were deleted
  7. so then I used git reflog to find where I left off
  8. then I used git reflog ______ to go back to my last commit.
  9. then I used git reset HEAD to unstage the commit (what I should have originally done) but the files I added (see above) after the commit were still gone.

How do I get those files back?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First, make a full backup of your Git repository!

When you git add a file, git will create a blob out of this file's content and add it to its object database (.git/objects/??/*).

Let's look at your commands, one by one:

I added all files using git add .

$ git add .

This will add all files contained in the current directory and its subdirectories to Git's object database. Untracked files matching patterns from .gitignore files will not be added. Tree files will also be written. Please see the end of my answer.

I then committed

$ git commit -m'added all files'

This will write a new commit object to the object database. This commit will reference a single tree. The tree references blobs (files) and other trees (subdirectories).

When I checked the status, there were still files that weren't included in the commit from the add, which was strange

$ git status

I can think of two scenarios where this happens: something modified your files or new files were added behind your back.

I added the untracked files again and it worked this time

$ git add .

I assume you used the same add command again, as in step 1.

But I wanted everything to be in 1 single commit so I looked up how to unstage what I just committed

I will tell you a better way at the end of this answer, which does not require the user to issue a potentially dangerous reset

I used git reset --hard HEAD^ — bad idea obviously, all files were deleted

$ git reset --hard HEAD^

This command will set your current working tree and index to be exactly at the commit HEAD^ (the second-last commit). In other words, it will discard any local uncommitted changes and move the branch pointer back one commit. It does not touch untracked files.

so then I used git reflog to find where I left off

$ git reflog

This shows the last commits that were recently checked out (identical to git reflog HEAD). If you specify a branch name, it will show you the last commits that this branch pointed to recently.

then I used git reflog __ to go back to my last commit.

Not sure about this one. git reflog is (mostly) a read-only command and cannot be used to "get back" to commits. You can only use it, to find commits a branch (or HEAD) pointed to.

then I used git reset HEAD to unstage the commit (what I should have originally done) but the files I added (see above) after the commit were still gone. $ git reset HEAD

This will not unstage this commit, but it will unstage all staged (but uncommitted) changes from the index. Originally (1st step), you wanted to say git reset HEAD^ (or git reset --mixed HEAD^) – this will leave your working tree untouched, but set the index to match the tree pointed to by the commit named by HEAD^.


Now, to get back your files, you have to use git fsck --full --unreachable --no-reflog. It will scan all objects in Git's object database and perform a reachability analysis. You want to look for blob objects. There should also be a tree object, describing the state after your second git add .

git cat-file -p <object hash> will print the files content, so you can verify that you have the right objects. For blobs, you can use IO redirection to write the content to the correct file name. For trees, you have to use git commands (git read-tree). If it's only a few files, you are better off writing them directly to files.


A few notes here:

If you want to add files to the last commit (or edit its commit message), you can simply use git commit --amend. It's basically a wrapper around git reset --soft HEAD^ && git commit -c HEAD@{1}.

Also, it's almost never a good idea to use git add .. Usually, you only want to use it the first time, when you are creating a new repository. Better alternatives are git add -u, git commit -a, which will stage all changes to tracked files. To track new files, better specify them explicitely.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...