Git has three undo commands and they are not interchangeable. restore touches uncommitted work, reset moves your own branch, and revert undoes a commit that other people may already have. Picking by what you are undoing - and who has seen it - keeps undo boring.
Restore: uncommitted work#
You edited a file and the change is wrong. It is not staged, not committed:
git restore app/Models/Invoice.php
Staged it by accident but still want to edit? Unstage without losing the edit:
git restore --staged app/Models/Invoice.php
Restore only reaches work that exists in some committed form. An edit to a file that was never committed anywhere is gone for good once restored away.
Reset: commits nobody else has#
You committed on your branch, have not pushed, and want those commits back as working changes:
git reset --soft HEAD~1 # undo the commit, keep everything staged
git reset HEAD~1 # undo the commit, keep the changes unstaged
git reset --hard HEAD~1 # undo the commit and throw the changes away
Reset rewrites the branch, so it belongs to history only you have seen. If a commit is already on the shared remote, reset is how you end up force-pushing over a teammate’s work.
Revert: commits everyone has#
A commit is on main, the deploy from it broke invoices, and the team has pulled it:
git revert 3e39e4f
Revert adds a new commit that applies the inverse diff. History keeps growing forward, nobody re-pulls a rewritten branch, and git revert of the revert brings the change back when the fix is ready. On a Laravel app this pairs cleanly with rollback: revert the commit, run php artisan migrate:rollback if it carried a migration, deploy again.
The rule of thumb#
- Not committed: restore.
- Committed, not pushed: reset.
- Pushed: revert.
When the mistake is bigger than one commit - a wrong rebase, a reset too far - the reflog remembers where the branch was before any of it. And when you are about to undo something you are not sure about, git branch rescue first: a branch you delete after inspecting costs nothing.
Share this post
Keep reading
Git Bisect: Find the Commit That Broke It
The tests passed last week and fail now. Bisect turns 'somewhere in these 40 commits' into six test runs.
The Reflog: Undoing Almost Anything in Git
Reset too far, deleted the wrong branch, botched a rebase? The reflog remembers where HEAD has been, and that is usually enough.
What Not to Commit in a Laravel Project
vendor, .env, caches and build output: the short list of what stays out of the repo, and the .gitignore that keeps it out.