Before you start
- Git installed and a repository with at least two commits
- A clean working tree, or changes you are happy to lose or keep (check with git status)
Everyone commits the wrong thing sooner or later: a typo in the message, a file that should not be there, a change that belongs on another branch. Git gives you several ways out. Which one is right depends on a single question:
Has this commit left your machine? If you have pushed it, or anyone has pulled it, do not rewrite it. Use
git revert. If it only exists locally,git resetis fine.
The example
A history with three commits, the last one a mistake:
c2c1377 Add third line (wrong)
a7bcf76 Add second line
dfea50b Add notes
Local commit: keep the changes, redo the commit
git reset --soft HEAD~1 moves the branch back one commit and keeps everything staged, so you can fix the problem and commit again.
git reset --soft HEAD~1
git status --short
M notes.txt
The commit is gone from the log and the change is still staged (M in the left column). Fix what you need and run git commit again. This is the usual answer to "wrong message" or "forgot a file". For just the message of the last commit, git commit --amend is shorter.
Local commit: keep the changes, but unstage them
git reset HEAD~1 (the default is --mixed) also moves the branch back, but leaves the changes in your files, not staged.
git reset HEAD~1
git status --short
M notes.txt
Now the leading space shows an unstaged change. Use this when you want to split one commit into two: stage and commit the parts separately.
Local commit: throw the changes away
git reset --hard HEAD~1 moves the branch back and discards the changes from your files.
git reset --hard HEAD~1
HEAD is now at a7bcf76 Add second line
This is the one that destroys work, and it also discards any uncommitted edits. Check git status first. If you regret it, the reflog usually saves you:
git reflog
a7bcf76 HEAD@{0}: reset: moving to HEAD~1
821d0e7 HEAD@{1}: commit: again
Each line is a place HEAD has been. Find the hash from before the reset and run git reset --hard 821d0e7 to return to it. Git keeps reflog entries for a limited time (by default 30 days for commits that are no longer on any branch, and 90 for the rest), so act soon.
Shared commit: cancel it with a new one
If the commit has been pushed, use git revert. It creates a new commit that applies the opposite change, so nobody's history is rewritten.
git revert --no-edit HEAD
git log --oneline
56419b3 Revert "work on a detached head"
fb58245 work on a detached head
a7bcf76 Add second line
dfea50b Add notes
Both the original and its reversal stay in the log. That is the point: everyone who pulled the original gets the reversal as an ordinary update. To revert an older commit, give its hash: git revert <hash>.
Just one file, not the whole commit
git restore puts files back without touching history.
git restore notes.txt # discard uncommitted edits to the file
git restore --staged notes.txt # unstage the file, keep the edits
git restore --source=HEAD~1 notes.txt # take the file as it was one commit ago
The first command throws away unsaved edits to that file, so use it deliberately.
Choosing quickly
| Situation | Command |
|---|---|
| Wrong message on the last local commit | git commit --amend |
| Redo the last local commit with changes | git reset --soft HEAD~1 |
| Split the last local commit | git reset HEAD~1 |
| Discard the last local commit and its changes | git reset --hard HEAD~1 |
| Cancel a commit that is already shared | git revert <hash> |
| Undo edits or staging of one file | git restore [--staged] <file> |
When in doubt, make a safety copy first: git branch backup leaves a pointer to where you are, and costs nothing.
Expected result
The unwanted commit is gone from your history (reset) or cancelled by a new commit (revert), and your files are in the state you intended.
Frequently asked questions
I ran git reset --hard and lost work. Can I get it back?
Often yes, if it was committed. Run git reflog, find the commit's hash from before the reset, and run git reset --hard <hash>. Work that was never committed cannot be recovered this way.
What is the difference between reset and revert?
Reset moves the branch pointer back and rewrites history. Revert adds a new commit that cancels an earlier one and leaves history intact, which is why it is the safe choice for anything shared.
References
From Grokem: https://www.grokem.com/programming/version-control/git-undo-last-commit



