Made a wrong commit? Don't panic. Git gives you multiple ways to undo commits while keeping your code changes intact. Whether you need to fix a commit message, unstage files, or completely reverse a pushed commit, this guide covers all 5 methods with clear explanations and practical examples.
Quick Answer: Undo Last Commit (Keep Changes)
If you just want to undo your last commit and keep all changes staged (ready to commit again), run this single command:
$ git reset --soft HEAD~1This moves HEAD back one commit but leaves all your changes in the staging area. Your code is untouched — you can modify files, fix the commit message, or add more changes before committing again.
What each part means:
git reset— git reset — the command to move HEAD to a different commit--soft— --soft — keep changes staged (in the index)HEAD~1— HEAD~1 — one commit before the current HEAD
# Before: You committed something wrong
$ git log --oneline
a1b2c3d (HEAD -> main) wrong commit message
f4e5d6c previous good commit
# Undo it:
$ git reset --soft HEAD~1
# After: Commit is gone, changes are staged
$ git log --oneline
f4e5d6c (HEAD -> main) previous good commit
$ git status
Changes to be committed:
modified: src/app.tsx
modified: src/utils.tsMethod 1: git reset --soft (Undo Commit, Keep Staged)
git reset --soft HEAD~1 is the gentlest way to undo a commit. It moves HEAD back by one commit, but your changes remain staged. This is perfect when you want to re-do the commit with a different message or add more files.
When to use:
- You want to change the commit message
- You forgot to add a file to the commit
- You want to split one commit into two
- You want to combine this with other changes before committing
# Undo last commit, keep changes staged
$ git reset --soft HEAD~1
# Fix the commit message and recommit
$ git commit -m "correct commit message"
# Or add a forgotten file and recommit
$ git add forgotten-file.ts
$ git commit -m "feature: add login with all files"
# Or split into two commits
$ git reset --soft HEAD~1
$ git reset HEAD src/unrelated-file.ts # unstage one file
$ git commit -m "feature: add login"
$ git add src/unrelated-file.ts
$ git commit -m "fix: update config"After running this command, git status shows your changes as "Changes to be committed" (green). You can immediately commit again.
Method 2: git reset --mixed (Undo Commit, Keep Unstaged)
git reset --mixed HEAD~1 (or simply git reset HEAD~1, since --mixed is the default) undoes the commit AND unstages the changes. Your files are preserved, but they appear as unstaged modifications.
When to use:
- You want to selectively re-stage only some files
- You want to review changes before re-committing
- You committed the wrong files and need to reorganize
- You want to split a large commit into several smaller ones
# Undo last commit, unstage all changes
$ git reset HEAD~1 # --mixed is the default
# or explicitly:
$ git reset --mixed HEAD~1
# Now selectively stage what you need
$ git status
Changes not staged for commit:
modified: src/auth.ts
modified: src/config.ts
modified: src/test.ts
# Stage only what belongs in this commit
$ git add src/auth.ts
$ git commit -m "feature: add authentication"
# Stage the rest separately
$ git add src/config.ts src/test.ts
$ git commit -m "chore: update config and tests"After running this command, git status shows your changes as "Changes not staged for commit" (red). You need to git add files before committing again.
Difference from --soft: With --soft, changes stay staged. With --mixed, changes are unstaged. Both keep your files intact.
Method 3: git commit --amend (Modify Last Commit)
git commit --amend doesn't technically "undo" the commit — it replaces the last commit with a new one. This is the fastest way to fix a commit message or add forgotten files without creating extra commits in your history.
When to use:
- Fix a typo in the commit message
- Add a file you forgot to include
- Remove a file that shouldn't have been committed
- Update the commit with small additional changes
# Fix commit message only
$ git commit --amend -m "fix: correct the typo in error handler"
# Add a forgotten file to the last commit
$ git add forgotten-file.ts
$ git commit --amend --no-edit # keeps the original message
# Remove a file from the last commit
$ git reset HEAD^ -- secrets.env # unstage the file
$ git commit --amend --no-edit
# Change both message and content
$ git add extra-changes.ts
$ git commit --amend -m "feature: complete login with validation"
# View the amended result
$ git log --oneline -1
b2c3d4e (HEAD -> main) feature: complete login with validationWarning: --amend rewrites the commit hash. Do NOT amend commits that have been pushed to a shared branch unless you coordinate with your team.
Method 4: git revert (Safe Undo for Pushed Commits)
git revert creates a NEW commit that undoes the changes from a previous commit. Unlike reset, it does not rewrite history — it adds to it. This is the only safe way to undo commits on shared/public branches.
When to use:
- The commit has already been pushed to a shared branch
- Other developers have pulled the commit
- You need an auditable undo (the revert itself is recorded)
- Working on main/master or any protected branch
# Revert the last commit (creates a new commit)
$ git revert HEAD
# Git opens editor with default message:
# Revert "add broken feature"
# This reverts commit a1b2c3d.
# Skip the editor, use default message
$ git revert HEAD --no-edit
# Revert a specific commit by hash
$ git revert a1b2c3d
# Revert without committing (stage changes only)
$ git revert HEAD --no-commit
$ git status
Changes to be committed:
modified: src/feature.ts # changes are reversed
$ git commit -m "revert: remove broken feature"
# Result: history shows both the original and the revert
$ git log --oneline
e5f6g7h (HEAD -> main) Revert "add broken feature"
a1b2c3d add broken feature
f4e5d6c previous good commitHow it works: If your commit added a line, the revert commit removes that line. If your commit deleted a file, the revert commit restores that file.
Key difference from reset: Reset removes commits from history. Revert adds a new commit that undoes changes. Reset is for local/unpushed commits. Revert is for pushed/shared commits.
Method 5: git reflog (Recover Lost Commits)
git reflog is your safety net. It records every move of HEAD in your local repository — even after destructive operations like reset --hard. If you accidentally lost commits, reflog can help you recover them.
When to use:
- You ran git reset --hard and lost changes
- You need to recover a commit after a bad rebase
- You accidentally deleted a branch with unmerged commits
- You need to find a previous state of your repository
# View the reflog (recent HEAD movements)
$ git reflog
a1b2c3d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
f4e5d6c HEAD@{1}: commit: add important feature
b7c8d9e HEAD@{2}: commit: previous work
...
# Oh no! I accidentally reset --hard and lost my commit!
# Find it in reflog:
$ git reflog
b7c8d9e (HEAD -> main) HEAD@{0}: reset: moving to HEAD~1
f4e5d6c HEAD@{1}: commit: add important feature # <-- there it is!
# Recover it:
$ git reset --hard f4e5d6c
# or create a new branch from it:
$ git branch recover-branch f4e5d6c
# Recover after a bad rebase:
$ git reflog
c1d2e3f (HEAD -> feature) HEAD@{0}: rebase (finish)
a1b2c3d HEAD@{1}: rebase (start)
x9y8z7w HEAD@{2}: commit: my work before rebase # <-- before rebase
$ git reset --hard x9y8z7w # back to pre-rebase state
# Recover a deleted branch:
$ git branch -D feature-branch # oops!
$ git reflog | grep feature
h1i2j3k HEAD@{5}: commit: work on feature-branch
$ git checkout -b feature-branch h1i2j3k # recovered!Important: Reflog is local only. It tracks HEAD movements on YOUR machine. It does not track other people's actions, and entries expire after 90 days by default.
Comparison: All 5 Methods Side by Side
| Method | Command | Changes Kept? | History Rewritten? | Safe for Pushed? | Best For |
|---|---|---|---|---|---|
| reset --soft | git reset --soft HEAD~1 | Yes (staged) | Yes | No | Recommit / fix message |
| reset --mixed | git reset HEAD~1 | Yes (unstaged) | Yes | No | Selective re-staging |
| commit --amend | git commit --amend | Yes (replaced) | Yes | No | Quick fix last commit |
| revert | git revert HEAD | No (new commit) | No | Yes | Pushed / shared commits |
| reflog | git reflog + reset | Recovers lost | - | - | Recovering lost commits |
# Visual: How the 3 reset modes affect your changes
Working Dir Staging Area Repository
─────────── ──────────── ──────────
reset --soft ✓ kept ✓ kept ✗ commit removed
reset --mixed ✓ kept ✗ unstaged ✗ commit removed
reset --hard ✗ DELETED ✗ DELETED ✗ commit removed
commit --amend ✓ kept ✓ merged ✗ commit replaced
revert ✓ kept ✓ new commit ✓ preserved (adds new)Already Pushed? How to Handle Shared Branches
What you do after an undo depends on whether the commit has been pushed and whether others have pulled it.
Scenario 1: Pushed, but nobody pulled yet
You can use reset + force push, but use --force-with-lease to avoid overwriting others' work:
# Undo the commit locally
$ git reset --soft HEAD~1
# Make your fix
$ git add .
$ git commit -m "correct commit"
# Force push (safe version)
$ git push --force-with-lease origin my-branchWarning: Force pushing rewrites remote history. Only do this on branches you own.
Scenario 2: Pushed, and others have pulled
Use git revert. This is the only safe option:
# Create a revert commit
$ git revert HEAD --no-edit
# Push normally (no force needed)
$ git push origin mainScenario 3: Pushed to a protected branch (main/master)
Most teams protect main from force pushes. You must use revert:
# On a protected branch, revert is your only option
$ git revert HEAD --no-edit
$ git push origin main
# Or revert via a pull request:
$ git checkout -b revert-broken-feature
$ git revert HEAD --no-edit
$ git push origin revert-broken-feature
# Then create a PR to merge the revertRule of thumb: If in doubt, use git revert. It is always safe.
Undo Multiple Commits: HEAD~2, HEAD~3
You can undo more than one commit by changing the number after HEAD~. The number specifies how many commits to go back.
# Undo last 2 commits (keep changes staged)
$ git reset --soft HEAD~2
# Undo last 3 commits (keep changes unstaged)
$ git reset HEAD~3
# Undo last 5 commits (DISCARD all changes - dangerous!)
$ git reset --hard HEAD~5HEAD~N means "N commits before HEAD". You can also use commit hashes instead:
# Reset to a specific commit hash
$ git log --oneline
a1b2c3d (HEAD) fourth commit
e4f5g6h third commit
i7j8k9l second commit
m0n1o2p first commit
# Go back to "second commit"
$ git reset --soft i7j8k9l
# Now "third commit" and "fourth commit" changes are stagedWarning: Undoing multiple commits with reset on a shared branch is dangerous. Use revert for each commit instead:
# Safe way to undo multiple pushed commits: revert each one
# Revert in reverse order (newest first)
$ git revert HEAD --no-edit # undo commit 3
$ git revert HEAD~1 --no-edit # undo commit 2
# Or revert a range (Git 1.7.2+)
$ git revert HEAD~2..HEAD --no-edit
# Push all reverts at once
$ git push origin mainNote: When reverting multiple commits, revert them in reverse order (newest first) to avoid conflicts.
Frequently Asked Questions
What is the difference between git reset --soft, --mixed, and --hard?
--soft keeps changes staged (ready to commit). --mixed (default) keeps changes but unstages them. --hard discards all changes permanently. Both --soft and --mixed are safe — your code is preserved. Only --hard is destructive.
Can I undo a git reset --hard?
Yes, if you act quickly. Use git reflog to find the commit hash before the reset, then run git reset --hard <hash> to restore it. However, uncommitted changes that were discarded by --hard cannot be recovered by Git (they were never recorded).
Should I use reset or revert?
Use reset for local/unpushed commits — it gives a cleaner history. Use revert for pushed/shared commits — it is safe and doesn't break other developers' history. When in doubt, use revert.
How do I undo a merge commit?
For an unpushed merge: git reset --hard HEAD~1 (this discards the merge). For a pushed merge: git revert -m 1 HEAD (the -m 1 flag tells Git which parent to revert to, usually the main branch). Be careful: reverting a merge makes it tricky to re-merge the same branch later.
What does HEAD~1 mean vs HEAD^?
For most cases, HEAD~1 and HEAD^ are identical — both refer to the parent of the current commit. The difference matters for merge commits: HEAD^ is the first parent (usually main), HEAD^2 is the second parent (the merged branch). HEAD~2 means "grandparent" (two commits back in the first-parent chain).
How do I undo a commit but keep the files on disk?
Use git reset --soft HEAD~1 (keeps changes staged) or git reset HEAD~1 (keeps changes unstaged). Both preserve your files. Only git reset --hard HEAD~1 deletes your changes. If you already used --hard, check git reflog immediately.