"Undoing" a commit in git can mean very different things depending on whether the commit has been pushed, whether you want to keep the changes, and whether anyone else has pulled from your branch. Picking the wrong tool is how people accidentally lose work or rewrite history other people are already building on.
This guide walks through each case and the safe answer for it.
First, decide which case you're in
Ask three questions:
- Has the commit been pushed? If yes, anything that rewrites history (
reset,commit --amend, force-push) will affect collaborators. Prefergit revert. - Do you want to keep the changes in your working tree? That decides between
--soft,--mixed, and--hardresets. - How many commits back do you need to undo? One, several, or "back to a known good commit".
The right command falls out of the answers.
Undo the last commit, keep the changes
The most common case: you committed too early, want to rework the changes, and nothing has been pushed.
git reset --soft HEAD~1
The commit is gone, but every file change is still staged. Edit, re-stage, re-commit.
If you want the changes unstaged but kept in your working tree:
git reset --mixed HEAD~1 # --mixed is the default
Undo the last commit, throw the changes away
git reset --hard HEAD~1
This deletes the commit and the changes. Use only when you're sure you don't want them — and remember that a hard reset can usually still be recovered with git reflog for a couple of weeks.
Fix the last commit instead of removing it
If the previous commit is fine but you want to:
- adjust the message, or
- include one more file change,
then --amend is what you want, not a reset.
# fix the message only
git commit --amend -m "better message"
# add more changes to the same commit
git add path/to/file
git commit --amend --no-edit
--amend rewrites the commit. Don't amend a commit that has already been pushed and pulled by others.
Undo a commit that has already been pushed
The safe answer is git revert. It creates a new commit that undoes the changes of an earlier commit, leaving history intact.
git revert <sha>
git push
Revert the most recent commit:
git revert HEAD
Revert a merge commit (you must tell git which parent to keep — usually 1, the branch you merged into):
git revert -m 1 <merge-sha>
If you really must rewrite pushed history (e.g., you accidentally pushed secrets), see "Force-pushing safely" below — but revert is the default answer.
Undo several commits at once
Reset back to a specific commit
git log --oneline # find the SHA you want to land on
git reset --soft <sha> # or --mixed, or --hard
Everything after that commit becomes uncommitted (or vanishes, with --hard).
Revert a range of pushed commits
git revert --no-commit <oldest>^..<newest>
git commit -m "Revert feature X"
--no-commit collapses the reverts into the index so you produce one revert commit instead of one per commit.
Undo just one file from a commit
If you committed too much and only want to back one file out:
git checkout HEAD~1 -- path/to/file
git commit -m "Revert path/to/file"
Or, equivalently, with git restore:
git restore --source=HEAD~1 -- path/to/file
git commit -am "Revert path/to/file"
Force-pushing safely
After a reset or --amend on a branch you've already pushed, your local and remote diverge. git push will be rejected. You then have a choice:
git push --force— overwrites the remote with your local. Dangerous: if a teammate pushed in the meantime, their work is gone.git push --force-with-lease— refuses the push if the remote has commits you haven't seen. Use this. Always.
git push --force-with-lease
Even with --force-with-lease, do not force-push to shared branches like main or master without coordinating with the team.
Recover a commit you "accidentally" undid
git reset --hard and similar destructive operations don't actually delete the commit immediately — git's reflog remembers where every branch and HEAD pointed for a while.
git reflog
# find the SHA from before your reset
git reset --hard <sha>
This works for at least 30 days by default (controlled by gc.reflogExpire). If the reflog has rotated out, git fsck --lost-found may still surface the dangling commit.
Quick reference
| Situation | Command |
|---|---|
| Last commit, keep staged changes | git reset --soft HEAD~1 |
| Last commit, keep unstaged changes | git reset HEAD~1 |
| Last commit, discard everything | git reset --hard HEAD~1 |
| Tweak last commit's message | git commit --amend |
| Add files to last commit | git add . && git commit --amend --no-edit |
| Undo a pushed commit | git revert <sha> |
| Undo a pushed merge | git revert -m 1 <merge-sha> |
| Undo a range of pushed commits | git revert --no-commit A^..B then commit |
| Recover after a destructive command | git reflog then git reset --hard <sha> |
Summary
- Local commit →
reset(soft / mixed / hard depending on what you want to keep). - Pushed commit →
revertis almost always the safe answer. - Want to fix the last commit, not remove it →
--amend. - After rewriting history, always push with
--force-with-lease, never plain--force. - If you do destroy something,
git reflogis your friend for the next 30 days.