To undo the last commit in Git you have three options depending on what you want to keep: git reset --soft HEAD~1 removes the commit but keeps the changes staged; git reset --hard HEAD~1 removes the commit and the changes; and git revert HEAD creates a new commit that cancels the previous one without rewriting history, which makes it the safe option if you have already pushed.
Below you will see when to use each one, how to fix a commit instead of deleting it (--amend) and how to recover a commit you thought was lost with git reflog.
First: have you pushed?
This question decides everything else.
- You have not pushed (the commit only exists on your machine): you can rewrite history with
git resetor--amendwithout bothering anyone. - You have pushed to a shared branch: do not rewrite history. Use
git revert, which adds a new commit instead of deleting the existing one. If you force-push after a reset, the rest of the team ends up with a different history from yours and conflicts are guaranteed.
Option 1: git reset (undo locally)
git reset moves the branch reference (HEAD) to an earlier commit. HEAD~1 means “one commit before the current one”. What happens to that commit’s changes depends on the mode:
# Removes the commit; changes stay in the staging area
git reset --soft HEAD~1
# Removes the commit; changes stay in the working directory, unstaged (the default mode)
git reset --mixed HEAD~1
git reset HEAD~1
# Removes the commit AND discards the changes. Irreversible except through reflog.
git reset --hard HEAD~1
| Mode | Removes the commit? | Keeps the changes? | Where do they end up? |
|---|---|---|---|
--soft |
Yes | Yes | Staged, ready for a new commit |
--mixed (default) |
Yes | Yes | In the working directory, unstaged |
--hard |
Yes | No | Discarded |
A typical case: you committed too early and want to add something else. git reset --soft HEAD~1, add the missing files and commit again.
To undo several commits, change the number: HEAD~3 goes back three.
Option 2: git commit –amend (fix, don’t delete)
If you only want to change the last commit’s message or add a forgotten file, you do not need to undo it:
# Change only the message
git commit --amend -m "Corrected message"
# Add a forgotten file to the last commit, keeping the message
git add forgotten-file.js
git commit --amend --no-edit
--amend replaces the last commit with a new one (with a different hash). It is therefore a history rewrite: the same rule applies about not using it if you have already pushed to a shared branch.
Option 3: git revert (undo without rewriting)
git revert creates a new commit that applies the inverse changes of the specified commit. History keeps both the original commit and its reversal, which makes it safe on shared branches.
# Cancel the last commit and open the editor for the message
git revert HEAD
# Same, without opening the editor
git revert --no-edit HEAD
# Cancel a specific commit by its hash
git revert a1b2c3d
If the commit to revert is a merge, you will need to say which parent to keep with -m 1; the git-revert documentation explains it in detail.
If you have pushed and still need to rewrite
There are situations where a reset after a push is unavoidable (for example, you pushed a password by mistake and need to remove it from history). In that case:
git reset --hard HEAD~1
git push --force-with-lease
--force-with-lease is preferable to --force: it only overwrites the remote branch if nobody has pushed since your last fetch, which avoids wiping out someone else’s work by accident. Warn the team, because they will have to update their copies. And remember that a published secret must be considered compromised and rotated even if you delete it from history.
I did a reset –hard and need the commit back
git reflog keeps a record of everywhere HEAD has pointed over the last few weeks, including commits that are no longer on any branch:
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# e4f5g6h HEAD@{1}: commit: Add form validation
# ...
# Go back to the state before the reset
git reset --hard e4f5g6h
As long as Git has not run its garbage collection (by default, unreferenced objects stay in the reflog for at least 30 days), the commit is still there.
Which command to use: summary
| I want to… | Command |
|---|---|
| Redo the commit with more changes (not pushed) | git reset --soft HEAD~1 |
| Edit the files again before committing (not pushed) | git reset HEAD~1 |
| Throw away the commit and its changes (not pushed) | git reset --hard HEAD~1 |
| Change the message or add a file (not pushed) | git commit --amend |
| Undo an already published commit | git revert HEAD |
| Recover a commit after a wrong reset | git reflog + git reset --hard <hash> |
Conclusion
Before undoing a commit, ask yourself whether you have already shared it. If not, git reset (with the right mode) or --amend give you full control. If you have, git revert keeps history consistent for the whole team. And whatever happens, git reflog is the safety net: in Git it is very hard to truly lose a commit.