“Undo the last commit” means different things depending on what you want to keep and whether you’ve already pushed. Pick the situation that matches yours.

Quick decision table

Situation Command
Fix the message or add a forgotten file (not pushed) git commit --amend
Undo commit, keep changes staged git reset --soft HEAD~1
Undo commit, keep changes in working files git reset HEAD~1
Undo commit and throw away the changes git reset --hard HEAD~1
Commit is already pushed/shared git revert HEAD
Committed to the wrong branch branch + reset (below)
“I reset too far!” git reflog

HEAD~1 means “the commit before the current one”. HEAD~3 goes back three.

Change the last commit: amend

Wrong message:

git commit --amend -m "Correct message"

Forgot a file:

git add forgotten-file.txt
git commit --amend --no-edit

Amend creates a new commit replacing the old one. Fine before pushing; after pushing, you’d have to force-push (see below).

Undo the commit, keep the work

git reset --soft HEAD~1

The commit disappears; its changes remain staged, ready to recommit — e.g. after splitting it differently or fixing something.

git reset HEAD~1          # same as --mixed

The changes remain in your files but unstaged. Use this when you want to re-pick what goes into the commit (git add -p).

Undo the commit and discard the changes

git reset --hard HEAD~1

The commit and its changes are gone from your working tree. Any uncommitted work is discarded too — check git status first. (The commit itself can still be recovered via reflog for a while.)

The commit is already pushed

Rewriting shared history causes problems for everyone who pulled it. Use revert, which adds a new commit that reverses the old one:

git revert HEAD
git push

To revert an older commit: git revert a1b2c3d. For a merge commit: git revert -m 1 <merge-sha>.

If you really must rewrite pushed history

Only on branches nobody else uses (your own feature branch):

git reset --hard HEAD~1         # or --soft, then recommit
git push --force-with-lease

--force-with-lease refuses to overwrite commits someone else pushed in the meantime. Never force-push to shared branches like main without agreement — many platforms protect them anyway.

Pushed a secret (password, API key)? Reverting doesn’t remove it from history. Rotate the secret immediately — consider it compromised — then clean history with git filter-repo if needed.

Committed to the wrong branch

You meant to start feature/x but committed on main:

git branch feature/x            # new branch pointing at your commit(s)
git reset --hard origin/main    # move main back to the remote state
git switch feature/x

Or move just one commit to another existing branch:

git switch other-branch
git cherry-pick a1b2c3d
git switch main
git reset --hard HEAD~1

Undo several commits

git reset --soft HEAD~3          # combine the last 3 commits into one new commit
git commit -m "One clean commit"

Or use interactive rebase to squash, reorder or drop: git rebase -i HEAD~3.

Recover from a mistake with reflog

Git keeps a log of where HEAD has been — including commits you “deleted”:

git reflog
a1b2c3d HEAD@{0}: reset: moving to HEAD~1
9f8e7d6 HEAD@{1}: commit: Add payment form

Restore:

git reset --hard 9f8e7d6
# or keep current state and create a branch at the lost commit:
git branch rescue 9f8e7d6

Reflog entries expire after a while (90 days by default for reachable entries, 30 for unreachable), and they’re local — they don’t exist on other clones.

Undo changes that aren’t committed yet

git restore file.txt            # discard changes in a file
git restore --staged file.txt   # unstage, keep changes
git stash                       # set everything aside temporarily

Summary

  • Not pushed → amend or reset.
  • Pushed → revert.
  • Panic → reflog.

More in the Git commands cheat sheet.

  • Git
  • Version Control
  • Troubleshooting