Introduction
It was late, the weather was foul, and my Wi-Fi decided to have an opinion. Something went wrong during a sync between two machines, and the next morning I discovered that an entire folder of images had quietly vanished from my Git repository. Not overwritten — deleted. Gone from the working tree and staged as deletions in the last commit.
If this has happened to you, take a breath. Git keeps the entire history of your project, including every file that was ever committed. Nothing is truly lost as long as the deletion was committed (and not just a git rm you haven’t committed yet — that is even easier to undo). Let me walk you through exactly what I did.
Restoring the deleted files
Step 1 — Find the commit that deleted your files
Git’s --diff-filter option lets you filter the log to show only commits that match a specific kind of change. The flag D means “deleted”:
git log --diff-filter=D --summary
The --summary flag prints a brief list of files added, deleted, or renamed in each commit — which is exactly what we need. You will see output like this:
commit 45a2d299ef3d....
Author: Elena Daehnhardt <email@gmail.com>
Date: Wed Nov 29 12:05:06 2023 +0100
create_references
delete mode 100644 images/ai_art/dalle/elena/dall.e.22.18.39.png
delete mode 100644 images/ai_art/dalle/elena/dall.e.22.18.45.png
delete mode 100644 images/ai_art/dalle/elena/dall.e.22.18.50.png
There it is. Copy the commit hash — you will need it in the next step.
Step 2 — Restore from the parent commit
The commit I just found is the one that deleted the files. The files still existed one commit before that. In Git, ~1 refers to the first parent of a commit — in plain English, the commit that came just before it.
So COMMIT_ID~1 means: “go back one step from the guilty commit.”
To restore a single file:
git checkout COMMIT_ID~1 -- images/ai_art/dalle/elena/dall.e.22.18.39.png
You should see:
Updated 1 path from ae8c184
Your file is back in the working tree, staged and ready to commit.
To restore an entire folder at once, just use the folder path:
git checkout COMMIT_ID~1 -- images/ai_art/dalle/elena/
Updated 3 paths from ae8c184
A note on ~ versus ^
These two look similar but mean different things:
COMMIT_ID~1— the first ancestor going straight back in history (one step up the chain).~2goes back two steps.COMMIT_ID^1— the first parent of a merge commit.^2is the second parent. For a regular (non-merge) commit,~1and^1are equivalent.
For restoring deleted files, ~1 is almost always what you want. See the Git gitrevisions documentation for the full details.
Modern alternative: git restore (Git ≥ 2.23)
Since Git 2.23, git restore is the preferred way to restore working tree files. It does the same job but with a clearer, more explicit interface:
git restore --source=COMMIT_ID~1 -- images/ai_art/dalle/elena/
The --source flag tells Git where to pull the content from. This command leaves the files staged (ready to commit), just like git checkout does. Both work — git restore just makes the intent more obvious.
Step 3 — Commit the restored files
Once you have verified the files are back and looking correct, commit them:
git add images/ai_art/dalle/elena/
git commit -m "Restore accidentally deleted images"
Quick reference
# 1. Find the commit that deleted your files
git log --diff-filter=D --summary
# 2. Restore a single file (classic)
git checkout COMMIT_ID~1 -- path/to/deleted_file
# 2a. Restore a folder (classic)
git checkout COMMIT_ID~1 -- path/to/deleted_folder/
# 2b. Modern equivalent (Git ≥ 2.23)
git restore --source=COMMIT_ID~1 -- path/to/deleted_folder/
# 3. Commit back to your branch
git add path/to/deleted_file
git commit -m "Restore deleted file"
Inspecting what changed at a commit
While I was recovering my files, I found two git show options genuinely useful. They let you peek inside any commit without checking anything out.
To list only the file names that changed:
git show --name-only COMMIT_ID
images/ai_art/midjourney/computer/laptop_fantastic.jpg
To see the status (Added, Deleted, Modified, Renamed) alongside the file names:
git show --name-status COMMIT_ID
D images/ai_art/computer/laptop_fantastic.jpg
A images/ai_art/midjourney/computer/laptop_fantastic.jpg
D is deleted, A is added, M is modified, R is renamed. This is a quick way to confirm you have the right commit before restoring anything.
Conclusion
Git’s history is the closest thing to a time machine that I use daily. The trick is knowing which commands to reach for when something goes wrong — because something always goes wrong eventually, usually late at night.
The core workflow is simple: find the deletion commit with --diff-filter=D, step back to its parent with ~1, restore the file with git checkout or git restore, and commit. That is it.
If you want to go deeper on navigating Git history, the Pro Git book chapter on revision selection is well worth an hour of your time. I come back to it more often than I expected.
References
Git Documentation: git-log, –diff-filter
Git Documentation: gitrevisions — specifying ~ and ^
Git Documentation: git-restore (Git ≥ 2.23)
Pro Git Book: Revision Selection
Did you like this post? Please let me know if you have any comments or suggestions.
Git posts that might be interesting for you