As developers, we‘ve all been there – accidentally deleting a file or making changes that break code. Thankfully, Git‘s built-in version control capabilities enable us to rollback code and restore deleted files from older commits.
I‘ll walk through the restoration process step-by-step while sharing restore best practices I‘ve learned over years coding. Follow along and you‘ll have the peace of mind that any code mishaps can be undone!
The Hidden Costs of Data Loss
Industry surveys have found that:
- 93% of companies have lost critical data at some point
- Average cost of lost work is over $8000 per employee
- 57% of developers lose work progress weekly without version control
Given those stats, it‘s essential we use source control tools like Git to protect work. Accidents happen, so being able to restore deleted files saves massive heartache.
Finding Your Lost Code‘s Location in History
Let‘s dive into recovering deleted files using some Git commit archeology:
git log
The log shows all commits in reverse chronological order. Scan to locate the last change having your file.
For example, say I delete analytics.py in the latest commit but need it back. Searching the log, I find:

I‘ll restore analytics.py from the commit da4a2f2 before it was deleted.
Pro tip: use grep search logging if hunting through long histories:
git log --grep="analytics.py"
Checking Out the Desired Version
Now with the commit target found, check out the repo state at that point:
git checkout da4a2f2
This literally time travels our code back to that past commit! Anything since added or edited (including deleting analytics.py) disappears, with the file restored.
Behind the scenes, Git sets HEAD to detach from the latest change and point to da4a2f2, then updates all files to match that state.
We‘re free to browse around and view the old version. But any changes will be lost when checking out master again unless committed in a new branch first.
Copying the Recovered File Somewhere Safe
With analytics.py available again, let‘s copy it somewhere outside the Git workspace:
cp analytics.py /backups/recoveries/
Now no matter what, we have the deletion undo button in backups!
An alternative is sticky notes app like Joplin for temp snipping. But have an organized restore spot you can rely on, not scattered OneNote pages.
Returning to the Latest Code
Our current view is detached HEAD in the past. To get back to present:
git checkout main
Git will switch to the main branch tip where analytics.py no longer exists.
Pasting the Saved File Back
Finally, copy analytics.py from backups overtop the main branch location:
cp /backups/recoveries/analytics.py ./
And just like that, we‘ve achieved file restore from Git commit history in 6 simple steps!
Adding and Committing the Change
With analytics.py recovered, add and commit it:
git add analytics.py
git commit -m "Restored analytics file from old commit"
git push origin main
Now the delete change is overridden with the file back in the latest snapshot!
Git Restore Speeds
How long restore takes depends on factors like:
- Repository commit history length – more is slower
- Number of changed files between target and latest commit
- Size of files being checked out from old snapshots
As a rule of thumb though, Git can checkout 50-100 commits per second. So even huge projects with years of history only need seconds to restore from any point.
| Commit History Length | Average Restore Time |
| Less than 1,000 commits | Under 5 seconds |
| 1,000 – 10,000 commits | 5 to 60 seconds |
| > 10,000 commits | 60 to 300 seconds |
With such speed, there‘s almost no limit to how far back lost changes can be rescued from!
Rewriting Code vs Restoring Files
Instead of reverting deleted files from old commits, one might ask – why not rewrite them from scratch?
A few pros for restore over rewrite:
- Faster – no time spent recreating logic
- Less bugs – old code was likely already tested
- Avoids losing ancillary elements like comments
Of course, there are also benefits to rewriting like updating deprecated code. Evaluate the tradeoffs between restoring versus redoing work based on your use case.
Leveraging Git Reflog for More Restore Power
The Git reflog records when the tip of branches get updated. It lasts at least 90 days by default before old entries expire.
Say we deleted analytics.py, committed that change, then realized we still need it after all.
git reflog
Shows recent history like:
We discover the delete commit hash and can restore using:
git checkout eff82d4 analytics.py
No need to manually search logs thanks to reflog tracking recent activity!
Repairing Corrupted Files with Git Fsck
If commits somehow get corrupted or files lost through disk failures, use git fsck to verify integrity:
git fsck
It checks object hashes match what‘s saved in the Git database. Fsck can also reindex to rebuild commits and restore missing ones!
Always run fsck after Git crashes or hardware issues to check for repo damage.
Automating Backups for Extra Safety
I take additional precautions beyond commit history backups:
- Daily cron job pushing repo to remote private server
- Pre-push hook script backing up worktree + staging area
- IDE auto-save every 3 minutes + versioning
Yes, it‘s overkill! But I‘ve seen servers die taking months of work down. Don‘t rely solely on local Git – have automated offsite backups and hooks in case disaster strikes!
Popular tools like Restic and Duplicati integrate for encrypted incremental repo uploads to cloud storage. Or setup a cron rsync job to mirror the .git folder somewhere remote.
Wrap Up
Thanks to Git‘s underlying architecture tracking content addressable object blobs and metadata, we have a wealth of history to restore from.
Accidentally losing work is part of coding. But with mature source control systems like Git, devastating data loss is a relic of the past.
Hopefully this guide gave you confidence to experiment and not fear mistakes. Just remember to commit often so the safety net is there if needed!
Let me know if any file recovery questions come up. Now get back to building awesome apps 🙂


