How To: Recover From a Git Hard Reset

(Or, This is Why We Probably Shouldn’t but Totally Can Have Nice Things)

Carrie Guss
12 min readAug 4, 2014

Here is the logistical information about this post.

Target Audience: Github beginners working via command line who also have ADHD, insomnia, or a fever.

Question Addressed: I did a hard reset. I did a hard reset after adding files to be committed but BEFORE committing them. Git deleted all the files on my actual working directory. (Or, Git replaced all my new fancy clean files with old ugly bad files.) Can I recover from this?

Answers According to the Internet:

This is the first git command I’ve come across so far that’s irreversible… added a warning so others don’t get too trigger happy… — jmort253 Apr 1 at 21:41

Erm… no you cannot get the index back from before a reset —hard. You can only get back what has been commited/stashed — sehe Sep 10 ‘11 at 19:32

“This tells Git to replace the files in your working copy with the “HEAD” revision (which is the last committed version), discarding all local changes. Discarding uncommitted changes cannot be undone. This is because they have never been saved in your repository. Therefore, Git has no chance to restore this kind of changes.” — git-tower.com

Actual Answer:

YES, YOU CAN RECOVER FROM THIS. There’s no undo button, and it’s a tedious process, but you haven’t just irrevocably lost hours or days or weeks of work. There seem to be a few solutions that have worked for other people, which I’ll link to at the bottom of this post, but here I’ll lead you step-by-step through the one that worked for me.

One other relevant detail: Medium automatically displays two side-by-side hyphens as an em-dash. (One loooong hyphen.) I’ve tried to go through and reformat all of these, but in case I’ve missed a couple, if you see what looks like an em-dash in any of the Git commands, translate that in your brain eyeball to two hyphens. (This: “—” should be this: “--").

Off we go! First, some background:

What “git reset --hard” actually does:

Basically it tells Git to replace the files in your working directory (AKA on your actual computer, not on Github) with the last version of the project you committed and pushed to Github. Evidently there are times when this is the #1 most useful option for people who know what they are doing. In the hands of a beginner, this command is basically just saying DELETE ALL THE THINGS, HIDE THEM WHERE THEY MAY NEVER AGAIN BE FOUND.

Allie Brosh of Hyperbole and a Half demonstrates the power of git reset —hard.

Why you may have found yourself in this situation (Or, how I ended up in this situation). This meanders. Feel free to skip ahead to the actual how-to:

I was working on redesigning and rebuilding my website. I was not committing early and often. Making this mistake is definitely a good way to learn why committing early and often is important. When I went to do my first commit, I already had pretty much a full working version of my site ready to go.

Things going well. Grunt watching my .scss files and auto-compiling my .css files. Hurray for Grunt!

It was a new project, so there were many new files that I wanted to push. Instead of individually adding each file, I added all the files in my project folder (“git add .”)

I’d forgotten to create a .gitignore file (Google this if you don’t know what it is — super useful and easy to use) so this added everything everything, not just everything, and there were a lot of things that I did not want to commit. Most problematically, this added infinite “node_modules” folders and all the files within them because I’d been running Grunt and using fun extensions.

Sample of what it looked like when I accidentally added all of the things. This went on for a while. What you’re seeing here is a screenshot of my terminal, which is semi-transparent, revealing my clearly appropriate desktop background.

So I was like, OH NO, this is exactly what you are not supposed to do. This is why you do $ git status, and hand select the stuff you want to add, commit, and push. I do not want to commit all of these files that I have added to be committed.

There are many, many things, or at least two very easy things you can do at this point—the point at which you have added files to the staging area and want to unstage them. If you are at this point, and wondering what to do, just go ask the internet, and do ANYTHING, I repeat ANYTHING other than git reset —hard. (Unless you know how to use that properly and are just here reading this to laugh and laugh and laugh at the pain of other, lesser beings. In which case ugh go do whatever you want.) Some other form of git reset may be appropriate, just not $ git reset --hard. Read more about what git reset does, and how to use it properly, here.

The first thing I tried was “git rm --cached .” This tells git to unstage the changes without deleting them from the working directory. (The keyword here is “--cached”, as without it you’ll also be removing the files from your local working directory.) The “.” refers to all files. Since I’ve added like 700 of them to the staging area and unstaging each one individually would be a nightmare (although less of a nightmare than I ultimately created for myself), I use “.”

I then get the following message: “fatal: not removing ‘.’ recursively without –r”

Here’s what that means: I had added all of my files to the staging area, including files within folders. “Recursive” refers to files within folders (within files within folders etc.) Git adding “.” will automatically add all your files recursively to the staging area, but when you remove “.” you need to specify that you mean all recursive files or git won’t listen. You know, FOR SAFETY. Thanks for having our backs, Git.

In order to unstage all of these files, including the ones within folders, while leaving them intact in my working directory, I needed to say “git rm –r --cached .”

Unfortunately, I read “fatal: not removing ‘.’ recursively without –r” as “FATAL, HERE ARE SOME NONSENSE WORDS, BOOP BOOP BOOP. TRY TYPING SOME OTHER THINGS THAT YOU FIND ON THE INTERNET UNTIL YOU GET A MESSAGE THAT DOES NOT INCLUDE THE WORD FATAL.”

So I was like, I guess I broke Git, I just added literally hundreds of .js and .md and .json files that I definitely did not intend to add. Then when I tried to undo the add, I did something that was apparently “fatal.” I will go ask ibrokegit.com what to do.

Under “I broke git while trying to remove changes from staging,” it said:

Did you do the following steps?> git reset (files changed)

An excellent suggestion that would not have screwed me over. To which my brain replied, “(files changed)”? I don’t know what that means! Do I actually type “(files changed)”? Do I do this with each individual file name? Do I [etc., etc., etc.]? NO TIME FOR FURTHER ANALYSIS! Moving on.

So I looked at the next option:

Reset all changes and revert to the last commit> git reset --hard

I should have recognized the giant Do Not Enter sign, but instead I was like oh, cool, I bet that means it will be like a big fancy undo button for the adds I just did since those are the “changes” I have made via command line. I will mindlessly type it in and press enter without actually processing what “Reset all changes and revert to the last commit” really means.

I misunderstood.

Trial and error is frequently a great way to learn when you are coding, but trial and error is NOT a good way to learn how to use command line.

So, Git did exactly what I asked it to do, and exactly what ibrokegit.com said it would do: It reset all changes I had made on my local directory and reverted to the last commit I had pushed to Github.

(I’m phrasing it this way to place emphasis on the relationship [and distinction] between the stuff on your hard drive and the stuff in your online repository. Pushing might be irrelevant in this situation. It’s possible if I had added and committed but not pushed I would have been OK. I haven’t looked into this.)

Regardless, I had never committed or pushed anything to this new Github repository. So “all changes” was the existence of every single file in my local project folder, and “last commit” was nothing. Entering “git reset --hard” turned my site into an empty folder. (With the exception of a few Grunt-related folders that Git left as empty shells inside my empty shell of a site folder.)

Regard the fatal error.

At that point, I realized a few things. I did not have my files backed up anywhere. Not on Dropbox, not on an external hard drive, and obviously not on Github. I had not made a copy of anything at any point while playing around, as I usually do, because thanks to everything I’d been learning and practicing, things had actually been working the way I wanted them to (hurray…) and I never needed to, for example, turn my index.html file into an indexperiment.html file or my stylesheet.css file into a stylecheat.css file (cute, ammiright?) to mess around. There was no such thing as a git/command line undo button, and because my files had been essentially written over, not deleted, it’s not like they were just hanging out in the trash folder.

It was one of those moments where the main thought you keep coming back to is ok, come on, I can just not have done that, right? Let’s just go back in time 30 seconds and I’ll not do that.

Since that’s not possible, here’s how to (mostly) fix it:

So. You’ve created new files. You’ve staged them. You haven’t committed them. For whatever reason, you’ve run “git reset --hard”.

It’s time to find your lost blobs.

Quick but important note moving forward: Within this post, any punctuation inside the quotation marks is punctuation you should actually type into command line. Any punctuation outside of the quotation marks is just punctuation I have to use so you know when I am ending my sentences. The quotation marks themselves I’m just using to distinguish the commands from the rest of the content. Unless otherwise instructed, don’t actually enter the quotation marks into your command line. And, to repeat what I mentioned above, if you see one very long dash in a Git command, type it as two short dashes.

To recover your files, you’re going to be using “git fsck”. This stands for File System Check. When you do a hard reset, all your stuff goes out and dangles nebulously in the git dataverse. It’s not gone yet, but it’s like if you threw your retainer in the garbage and now it’s in a dumpster out back. You’re going to have to wade through a lot of garbage to find the items you’re looking for, and when you do find them, they may not be totally recognizable, and they’ll need some serious cleaning up.

STEPS:

1. If you type “git fsck” and press enter, you’ll get a list of dangling blobs. There might be other things in there, like missing trees. It will look something like this:

Hidden in these dangling blobs are your html files, your CSS files, your Sass files, your images, everything. Every dangling blob is essentially a file without its corresponding name or extension.

2. Enter “git fsck --lost-found”. (That’s two dashes up front there in front of lost-found, if my edit didn’t work.) Running this saves the blobs to an invisible path: .git/lost-found/other

Your path may not be invisible. Mine was super invisible.

3. Now copy and paste the following and press enter. This will take all the blobs in your possibly invisible new path and save them as 100% visible .txt files in your project folder:

for blob in $(git fsck --lost-found | awk ‘$2 == “blob” { print $3 }’); do git cat-file -p $blob > $blob.txt; done

4. Your files are in there somewhere, but maybe you now have a list of 1517 .txt files and no way to figure out what you need and what you don’t. For example, somewhere in there, I don’t know why, will be this file. It belongs to Git:

Not one of my project images, but yes one of my dangling blobs.

5. Recover your HTML files by searching for key words within the content of the files. (On a Mac, open your finder window, go to File > Find, change the search to target only your project folder, and change the dropdown menu field from “Kind” to “Contents.” Now you’ll have a little search bar.) Maybe there’s a menu bar on every page that includes “bio” and “contact.” Maybe there’s a page about rocket fuel, and you can filter that list of 1517 .txt files for only the ones that contain the words “rocket fuel.” Or just search for “doctype.” For me, “doctype” narrowed the search for my HTML files from 1517 .txt files to just 23 .txt files.

6. Your .css, .scss, .js and other files can be found the same way. Use your imagination. (Or, use your newly recovered HTML files to find class and id tag names to search for.)

7. Images and PDFs are trickier, but not impossible. An image saved as a .txt file will just look like pages and pages of this:

What a beautiful full-page cover image you’ve chosen for your website!

So, there are two things you can do right away:

(i) Change all the file extensions to, for example, .jpg — the ones that really are images will turn back into images, like in Cinderella, and they’ll show up as thumbnail images instead of that blank jpeg file icon. For the love of all that is holy, do not do this by hand. I used Renamer, which has a free fourteen day trial.

(ii) Sort the files by size. For the most part, your image files will be larger than the other files, and drift up towards the top of the list, making the thumbnails easier to spot.

Make sure you’ve renamed everything correctly and recreated all your folders. Use your HTML files to figure out what your CSS files were called, what your image extensions were, etc.

The end.

LINKS:

Here’s a really useful thread on Stack Overflow where Mark Longair saves a bunch of people from hellfire. This technique didn’t work for me, but it seems to have worked for most of the other people who tried it. All the comments/answers on the page are useful, and there’s one that tells you what to do if you’ve only lost one file. (You cherry-pick!): http://stackoverflow.com/questions/7374069/undo-git-reset-hard/7376959#7376959

Helpful summary of the above Stack Overflow thread: http://www.freshleafmedia.co.uk/blog/undo-git-reset-hard/2013/02/

What got me back on track: http://blog.ctp.com/2013/11/21/git-recovering-from-mistakes/

Good luck!!! Hope this helped. If it didn’t, keep digging. I tried probably every single thing suggested on Stack Overflow before I found the Cambridge Technology Partners’ solution that finally worked.

Also, you can rest easy knowing that you’ve just screwed yourself over in one of the most thorough possible ways. You’ve pretty much hit rock bottom. Welcome! This is a safe space. Everything will be hilarious and nothing will be stressful for at least two weeks. (At least nothing related to coding. I make no promises about life outside coding, if you happen to have one of those.)

Lastly: If, after doing a git hard reset, you find yourself never wanting to use command line again, remember that with git patching you get to work with hunks.

Kate Beaton explains Git hunks.

--

--

Carrie Guss

Writer, illustrator, and front-end web developer. Sometimes scared of ghosts.