How to Undo the Last Git Commit (reset, amend, revert)

To undo the last commit but keep its changes, run git reset --soft HEAD~1; to throw the changes away as well, run git reset --hard HEAD~1. If the commit is already pushed to a branch other people use, run git revert HEAD instead, which adds a new commit that cancels it without rewriting history.

If you only want to fix the commit message or add a forgotten file, you don't need to undo anything: git commit --amend replaces the last commit in one step.

Which command to use

CommandCommitStaging area (index)Your filesSafe after push?
git reset --soft HEAD~1Removed from branchChanges stay stagedUnchangedNo
git reset HEAD~1 (same as --mixed)Removed from branchUnstagedUnchangedNo
git reset --hard HEAD~1Removed from branchResetChanges deletedNo
git commit --amendReplaced with a new oneStaged changes added to itUnchangedNo
git revert HEADStays; a new "undo" commit is addedMust be clean firstChange reversedYes

HEAD~1 means "the commit before the one you are on". HEAD~3 means three commits back, so git reset --soft HEAD~3 undoes the last three commits at once.

git reset --soft: undo the commit, keep the changes staged

Use this when the commit was premature: wrong message, missing a file, or you want to split it differently. The Git docs describe --soft as leaving "your working tree files and the index unchanged". Only the branch pointer moves back.

git reset --soft HEAD~1
git status --short

Example output, from a test repository where the last commit had changed f.txt:

M  f.txt

The M in the first column means the change is staged. Edit what you need and run git commit again.

git reset --mixed: undo the commit and unstage

This is the default mode, so git reset HEAD~1 does the same thing. Your files keep their edits, but nothing is staged, which is useful when you want to re-pick which changes go into which commit with git add.

git reset HEAD~1

Example output:

Unstaged changes after reset:
M	f.txt

git status --short now shows M f.txt, with the M in the second column: modified, not staged.

git reset --hard: delete the commit and its changes

--hard makes your working tree match the earlier commit exactly. The docs say it will "overwrite all files and directories" with that commit's version, and tracked files that were not in it are removed.

git reset --hard HEAD~1

Example output:

HEAD is now at 5361315 first

Run git status first if you have uncommitted edits you care about: --hard discards those too, and uncommitted work is not recorded anywhere Git can recover it from.

How to get a commit back after git reset --hard

The commit itself is not deleted right away. Git keeps a log of where HEAD has pointed:

git reflog

Example output:

5361315 HEAD@{0}: reset: moving to HEAD~1
fb40b96 HEAD@{1}: commit: second

The commit you removed is the entry just before the reset. Restore it with either of these:

git reset --hard ORIG_HEAD     # straight after the reset
git reset --hard fb40b96       # any time, using the hash from the reflog

ORIG_HEAD works because, as the reset docs note, Git sets it to the tip of the branch before the reset runs. Other commands (merge, rebase) also overwrite it, so use the reflog hash if you have done anything since.

git commit --amend: fix the last commit instead of undoing it

To change only the message:

git commit --amend -m "Correct message"

To add a file you forgot, keeping the existing message:

git add forgotten_file.py
git commit --amend --no-edit

An amend does not edit the old commit. It creates a new commit with a new hash and points the branch at it. The Git docs call it a rough equivalent of git reset --soft HEAD^ followed by a new commit, which is why the same push rules apply to both.

The commit is already pushed: use git revert

Reset and amend rewrite history. If other people have already pulled the commit, their copies still contain it, and the Pro Git book warns that amending pushed commits and force pushing "will cause problems for your collaborators". On a shared branch, revert instead:

git revert HEAD

Git opens an editor for the message; add --no-edit to accept the default. Example output with --no-edit:

[main 8f69b64] Revert "third"
 1 file changed, 1 deletion(-)

The history now reads third followed by Revert "third", and you push it normally. Revert needs a clean working tree, so commit or stash other edits first. To revert an older commit, give its hash: git revert 1a2b3c4. Reverting a merge commit needs -m 1 to tell Git which parent is the main line.

What happens if you reset or amend after pushing

Your next git push is rejected because the remote has a commit your branch no longer contains. This is the real message, from a test repository after amending a pushed commit:

 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to '...'
hint: Updates were rejected because the tip of your current branch is behind

On your own feature branch that nobody else uses, overwriting the remote is acceptable:

git push --force-with-lease

--force-with-lease only overwrites the remote branch if it still matches your remote-tracking copy of it (what you last fetched), so it refuses if someone else pushed in the meantime. Plain --force does not check. The git-push docs add that a background fetch (some editors do this) can update that copy and defeat the check. On main or any branch others work on, don't force push; revert. The same reasoning applies to rebasing, covered in Git merge vs rebase.

If it still looks wrong

Run git log --oneline -5 to see where your branch points now, and git status to see what is staged. If you ended up on a commit rather than a branch (the prompt or git status says HEAD detached at), see Git detached HEAD explained. To compare a file before and after the undo, git diff ORIG_HEAD -- path/to/file shows exactly what changed.

Frequently asked questions

What is the difference between git reset and git revert?
git reset moves your branch pointer back, so the commit disappears from the branch history. git revert leaves the history alone and adds a new commit that applies the opposite change. Use reset for commits that only exist on your machine and revert for commits other people may already have pulled.
Can I get a commit back after git reset --hard?
Usually, yes, if it was committed. Run git reflog to find the commit's hash (it appears as the entry just before 'reset: moving to HEAD~1'), then run git reset --hard with that hash, or git reset --hard ORIG_HEAD straight after the reset. Changes that were never committed are not in the reflog and cannot be recovered this way.
How do I undo the last commit but keep my changes?
Run git reset --soft HEAD~1 to keep the changes staged, or git reset HEAD~1 (the default --mixed mode) to keep them as unstaged edits in your files. Neither command touches the content of your working tree.
How do I undo the last two or three commits?
Use a larger number after the tilde: git reset --soft HEAD~3 undoes the last three commits and keeps all of their changes staged. The same rule about pushed commits applies: if those commits are on a shared branch, revert them instead.

Sources