Someone deleted a file three commits ago - or three months ago - and now it turns out it was load-bearing. If the file was ever committed, getting it back is a two-command job.
Find where it disappeared#
git log --oneline --follow -- database/migrations/2026_09_14_100000_add_invoice_notes.php
This lists every commit that touched the file, ending with the one that deleted it. When you are not sure of the exact path, widen the search:
git log --all --oneline --diff-filter=D -- '*invoice*'
--diff-filter=D shows only commits where matching files were deleted.
Restore from before the deletion#
Bring the file back from the commit just before the one that removed it:
git restore --source=HEAD~3 -- database/migrations/2026_09_14_100000_add_invoice_notes.php
The file lands in your working tree as an untracked-by-that-name restoration - review it, then commit it like any new change. Restoring a deleted migration this way, before anyone runs it in production, is exactly the Laravel-shaped case: the file matters, the history of how it vanished does not.
For a file deleted long ago, grab the hash of the last commit that had it and use that as the source instead of counting back from HEAD.
The honest limits#
- The file must have been committed at least once. Git only versions what it was told about.
- Staged-but-never-committed work sometimes survives as a dangling blob:
git fsck --lost-foundcan surface it. It is a long shot, but a real one. - Unstaged, uncommitted work is gone for good - the same rule as
git cleanandgit restore.
The pattern worth keeping: deletions in Git are just commits too, and everything committed is recoverable until the day it is not - which is the same reason the server itself still needs real backups, not just a long history.
Share this post
Keep reading
Annotating Commits with git notes: Metadata Without Rewriting History
Some things belong on a commit but not in it: review links, benchmark results, the ticket nobody remembered to reference. git notes adds them after the fact, no rebase required.
Backing Up Your Git Server So It Actually Restores
Every clone is a partial backup - but hooks, config and server state live outside the repo. Mirror, bundle, copy off-site, and rehearse the restore.
Cherry-Picking: One Commit Without the Branch
Cherry-pick copies a single commit onto your branch and leaves the rest behind. Right for hotfixes across release branches, wrong as a sync strategy.