32

How to restore an accidentally deleted file and folder in git after push. Below is the scenario.

  1. Accidentally deleted a folder which has a single file and made a bunch of changes to other files.
  2. Now, pushed all these changes. So this push has the deleted folder along with other changed files.
  3. On top of this, there were a few more pushes.

Now, can I restore the deleted file, along with history and then push back to repo.

4
  • 1
    Git doesn't have "file history". Git has commit history. Restore the file(s) and your history says "commit K deleted some files, later commit R produced new files with the same name and the same contents as before." That's as close as you get. Use Ryan's answer below to get the file(s) back. Commented Feb 17, 2017 at 1:21
  • If you didn't use git to remove them you could reset your workspace to get them back. Are you sure they are deleted? Commented Feb 17, 2017 at 5:46
  • @osowskit what do you mean by "if you did not use git to remove" . My question itself is "how to restore if I deleted the file and pushed the deleted file?" Commented Feb 20, 2017 at 7:49
  • Thx. I'd recommend including the git commands you used to clarify that this wasn't deleting on the client-side only. I'd revert the commit to avoid rewriting history. Commented Feb 20, 2017 at 16:02

6 Answers 6

25

It is possible, with help of Git copy file preserving history you need to copy the file before it was deleted.

I will write here a complete example. First I will prepare some test repository:

git init echo "test content" > file.txt git add file.txt git commit -m "start" echo "test content 2" >> file.txt git commit -am "next commit" rm file.txt git commit -am "Delete file, lose history" 

Now we have a test repository without the file. To restore the file with its commit history the process is following: create a branch where the file did exist. Then make two copies of this file, each with history. Then merge back to master and only one of the two copies will get deleted during the merge. Be sure to use your commit hash instead of 190112b in example below.

git checkout 190112b -b before-deletion git mv file.txt file1.txt git commit -m "Move file (1)" SAVED=`git rev-parse HEAD` git reset --hard "HEAD^" git mv file.txt file2.txt git commit -m "Move file (2)" git merge $SAVED git commit -a -n 

OK, so now in this branch we have two copies of this file. Each copy preserves history. Now when we merge this back into master, one file will disappear (or will have wrong history), the other one will keep the history.

git checkout master git merge before-deletion git commit -a -n git rm file1.txt git mv file2.txt file.txt git commit -m "recovery finished" 

And that's it.

Sign up to request clarification or add additional context in comments.

9 Comments

Worked. Restored the file with its history. Thanks!
@RodrigoElias great to hear! :)
I followed your steps above, but had errors with git merge. When I ignored the errors and carried on with the next command, it all just worked. Amazing!
Was it line git commit -m "next commit" ? I will remove it, it's unnecessary, the next command fixes it.
Can you add comments before each git command? I don't know what is specific of this example and what should be changed. For example, what if the commit that deleted the file is not the one before HEAD?
|
12

Sure, just check it out from a commit where it existed. If the commit that deleted the file is the tip of whatever you have currently checked out, that’s HEAD^ (the commit before last):

git checkout HEAD^ -- path/to/file 

Then you can commit and push it.

9 Comments

You can replace HEAD^ with a commit SHA and that will also work.
I tried that but it does not restore the history for me.
@Ry-: I did get the file back, but the history was lost. But when I execute git blame on the file I only get my name and not the name of the original committer.
This did not preserve history for me; I see only the latest commit in the file's history. FWIW I checked out using the commit hash.
This did not retain the history for me.
|
12

Just encountered a similar scenario: A file was previously deleted, now I need to restore it.

Step 1: Locate the commit which deletes the file (say the file name is test.log):

$ git log --all --stat --diff-filter=D -- test.log commit aec72339081df3bc621a6c0cd3687257778466e4 (Author, Date, commit message etc appear here) 

Step 2: Restore the file from that commit (aec723390 from above), note the ^ for the prior commit containing the not-yet-deleted file:

$ git restore --source aec723390^ -- test.log 

Step 3: The file will be restored to your working tree (but not the repo until you actually commit it), you may add it via git add test.log then make a commit:

$ git status On branch main Your branch is up-to-date with 'origin/main'. Untracked files: (use "git add <file>..." to include in what will be committed) test.log nothing added to commit but untracked files present (use "git add" to track) $ 

6 Comments

Is this any different than simply recreating the exact file manually? According to this description, after git restore the file is still untracked, presumably the same as if I would have copied the file to the directory from a backup, for example. Where does the extra history tracking come in? Does git restore provide something extra to history tracking that my simply recreating the file from scratch would not?
@GarretWilson I do not quite understand what you meant by "my simply recreating the file from scratch". The scenario in my case is, I'm pretty sure there is such a file in my git repository a long time ago, and the file may gets deleted by a commit made by someone else. I cannot simply recreate the file since I'm not fully sure what is in the file.
@GarretWilson That's why I have to get the file from git commit history.
@MadHatter Simply restoring a file is not a rocket science. The tricky part is when you want this file to be tracked like it was never gone (i.e. with git blame showing all the authors of each line) - this is what the topic starter asked. Your solution does not achieve this, unfortunately.
@JBM git blame: git-blame - Show what revision and author last modified each line of a file. For this topic, I thought we were actually talking about restoring a deleted file in git.
we are talking about both together; that's why the question says "… restore the deleted file, along with history …". The question is whether the technique you mention, when you bring back a file from history, also allows git blame to follow the previous edits of the file you brought back, along the history for the changes to the deleted file. Does your technique allow that, or is it equivalent to my simply typing in a new few with the old content?
2

Assuming you're currently on the master branch, an easy solution is to find the GIT SHA1 from the last commit (using something like gitk helps) with the deleted file you want.

Run the command

git checkout <GIT SHA1 value> 

Copy the contents of the file into notepad (or any text editor)

Run the command

git checkout master 

Recreate the file with the contents from notepad

3 Comments

Will this bring back the history of the file also?
Yes it should, assuming the file is in the same location with the same name
This is the most simple and most clearly explained solution and it worked for me!
1

My current one-liner, for a given deleted file I know the exact name and path, from a Windows CMD:

bash -c "git restore -SW -s $(git log -n 1 --diff-filter=D --pretty=%H -- :/path/to/file)^ -- :/path/to/file"

By using the pathspec :/path/to/file, I reference the deleted file with a relative path from the root of the working tree, even when I am running the command from inside a subdirectory.
See more at "How does git interpret a colon followed by a path?".

The git restore -SW part directly restores the file both in the working tree and the index, ready to be committed.

Comments

0
  1. check out the commit before the deletion (I'll call this b for 'before')

  2. now merge the deletion commit (d), but use --no-ff to ensure you get a proper merge and not a fast-forward merge. do not commit the merge yet!

  3. unstage the deletion

  4. commit the merge. this gives you a new commit (I'll call it r for resurrection) that you can merge into your branch to "resurrect" the file with the history

  5. merge r into your target branch

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.