Everyone makes mistakes in Git: committing to the wrong branch, adding a file that holds a secret, writing a bad commit message, or running a command you did not fully understand. The fear of breaking something keeps many developers from using Git confidently. But Git is designed around the idea that almost nothing is truly lost, and there is a tool for nearly every kind of undo.
The tricky part is choosing the right one. This guide explains what each command does, when to use it, and how to rescue work that appears to have vanished.
First, understand the three areas
Git moves your changes through three places. The working directory holds the files you are editing. The staging area (or index) holds changes you have marked for the next commit with git add. The repository holds committed history. Most undo commands operate on one or more of these areas, so knowing where your mistake lives tells you which tool to reach for.
Always start by looking at the situation:
git status
git log --oneline -5
Discard changes in a file: git restore
You edited a file and want to throw away the changes and return to the last committed version.
git restore path/to/file.js
This permanently discards uncommitted edits in that file, so be sure you do not need them. If you only want to remove a file from the staging area, but keep your edits, use:
git restore --staged path/to/file.js
That is the right fix when you ran git add on something you did not mean to include.
Fix the last commit: git commit --amend
You just committed and noticed a typo in the message, or forgot to include a file. If you have not pushed yet, you can amend the commit.
git add forgotten-file.js
git commit --amend -m "Add password reset email"
This replaces the last commit with a new one. Because it rewrites history, do not amend commits that other people have already pulled.
Undo commits locally: git reset
git reset moves your branch pointer back to an earlier commit. It has three common modes that differ in what happens to your changes:
--softmoves the branch back but keeps your changes staged. Handy for combining several commits into one.--mixed(the default) moves the branch back and unstages the changes, but leaves them in your files.--hardmoves the branch back and throws away the changes entirely.
git reset --soft HEAD~1 # undo last commit, keep changes staged
git reset HEAD~1 # undo last commit, keep changes unstaged
git reset --hard HEAD~1 # undo last commit and DELETE its changes
--hard is the dangerous one, since it discards uncommitted work. Use --soft or --mixed when you are unsure. Like amending, reset rewrites history, so it is meant for commits you have not shared.
Undo shared commits safely: git revert
When a bad commit has already been pushed and others may have pulled it, rewriting history causes chaos. git revert is the safe alternative. Instead of removing the commit, it creates a new commit that applies the exact opposite changes.
git revert a1b2c3d
History stays intact, the mistake and its correction are both visible, and nobody's copy of the repository breaks. A simple rule of thumb: use reset for private, local history, and revert for anything that is already public.
Rescue lost work: git reflog
This is the command that turns panic into relief. Git keeps a private log of everywhere your HEAD has pointed, even after you reset, rebase, or delete a branch. That log is the reflog.
git reflog
You will see a list of entries such as HEAD@{3}: commit: Add login form. If you accidentally ran git reset --hard and lost commits, find the entry from before the mistake and return to it:
git reset --hard HEAD@{3}
Or create a new branch at that point so you can inspect it safely: git switch -c rescue HEAD@{3}. Reflog entries expire after a period, typically at least thirty days, so act reasonably soon. Note that it only recovers committed work. Changes that were never staged or committed cannot be rescued by Git.
Undo a merge or a bad rebase
If a merge has just gone wrong and you have not committed the result, git merge --abort returns you to the state before the merge. After a rebase that went badly, find the pre-rebase commit in the reflog and reset to it. Remember that the reflog remembers where your branch was, which makes even scary operations recoverable.
Remove a secret you committed
If you accidentally committed a password or API key, treat the secret as compromised, whether or not you remove it from history. Rotate or revoke it first. Removing it from history requires rewriting every commit that contains it, using tools such as git filter-repo, and force-pushing, which affects every collaborator. The safest rule is prevention: add sensitive files to .gitignore before the first commit, and keep secrets in environment variables.
A quick decision guide
- Discard uncommitted edits in a file:
git restore file - Unstage a file:
git restore --staged file - Fix the last unpushed commit:
git commit --amend - Undo local commits:
git reset --softor--mixed - Undo a pushed commit:
git revert - Recover "lost" commits:
git reflog
Build safety habits
Commit often, even if the commit is messy, since committed work is recoverable. Before a risky operation, create a backup branch with git branch backup. Run git status before and after destructive commands. And use git diff to see what you are about to lose.
The takeaway
Git is far more forgiving than it looks. Learn the difference between restoring files, resetting history, reverting shared commits, and consulting the reflog, and you can undo almost any mistake. The next time something goes wrong, do not panic and do not delete the folder. Run git status, read the options above, and choose the gentlest tool that solves the problem.