How to Undo the Last Commit in Git (Without Losing Changes)
Made a mistake in your last commit? Don’t panic. Git gives you several ways to undo it depending on whether you want to keep your changes or discard them entirely. Understanding the difference between reset and revert is crucial — the wrong choice can cause problems, especially on shared branches.
Quick Answer
To undo the last commit but keep your changes:
git reset --soft HEAD~1This moves Git’s HEAD back one commit while leaving your files and staged changes intact. You can then fix whatever was wrong and recommit.
When to Use Each Method
| Situation | Command | Effect |
|---|---|---|
| Wrong commit message | git commit --amend | Edit message only |
| Forgot to include a file | git add . && git commit --amend --no-edit | Add changes to last commit |
| Undo commit, keep changes | git reset --soft HEAD~1 | Uncommit, keep working |
| Undo commit, unstage changes | git reset --mixed HEAD~1 | Uncommit + unstage |
| Undo commit, discard all | git reset --hard HEAD~1 | Uncommit + delete changes |
| Already pushed, safe undo | git revert HEAD | Creates a new undo commit |
Detailed Examples
1. Fix the commit message
git commit --amend -m "New and improved commit message"Replaces the last commit’s message without changing its content. This is safe only if the commit hasn’t been pushed yet. If you’ve already pushed, force-pushing an amended commit is risky on shared branches.
2. Add a forgotten file
git add forgotten-file.txt
git commit --amend --no-editThe --no-edit flag keeps the existing commit message. This is the fastest way to fix an incomplete commit — you add the missing file and amend without touching the message.
3. Soft reset (undo but keep everything)
git reset --soft HEAD~1Your files remain unchanged. The changes from the undone commit are staged and ready to recommit. Use this when you committed too early or want to split one large commit into several smaller ones.
4. Mixed reset (undo + unstage)
git reset --mixed HEAD~1Your files remain unchanged. Changes are unstaged — you’ll need to git add them again before recommitting. This is the default mode of git reset (you don’t need the --mixed flag explicitly), and it’s useful when you want to review all your changes before staging them again.
5. Hard reset (completely undo)
git reset --hard HEAD~1⚠️ Dangerous. This permanently removes the commit AND discards all file changes. Only use if you’re sure you don’t need the changes. There’s no built-in undo for this command — though you can sometimes recover lost commits with git reflog.
6. Revert (safe for pushed commits)
If you’ve already pushed the commit, never use git reset. Use git revert instead:
git revert HEADThis creates a NEW commit that undoes the changes. Safe to push because it doesn’t rewrite history. Team members will see the revert commit in their log and know exactly what happened.
Undoing Multiple Commits
To undo the last 3 commits:
git reset --soft HEAD~3Or to revert a specific range:
git revert HEAD~2..HEADThis reverts commits from HEAD~2 to HEAD, creating a new commit for each one. You can also use git revert --no-commit HEAD~2..HEAD to revert them all in a single commit.
Pro Tip: Your Safety Net
If you’re unsure, use git revert instead of git reset. It’s always safe because it doesn’t delete history:
git revert HEAD --no-editFor times when you already did a hard reset and regret it, run git reflog to see a list of every HEAD position you’ve visited. You can restore lost commits with git checkout <commit-hash> or git reset --hard <commit-hash> — as long as you act before Git garbage-collects the unreferenced commits (typically 30-90 days).
Practical Workflow Examples
Scenario: You committed to the wrong branch
# Undo the commit but keep changes
git reset --soft HEAD~1
# Stash the changes
git stash
# Switch to the correct branch
git checkout correct-branch
# Apply the changes
git stash pop
git add .
git commit -m "Correct commit on the right branch"This is a common mistake when you’re working on multiple features. The soft reset undoes the commit without losing any work, and stash temporarily stores your changes while you switch branches.
Scenario: You need to remove a sensitive file
If you accidentally committed a file with passwords or API keys:
# Remove the file and amend
git rm --cached secrets.env
git commit --amend --no-edit
# Also remove from all history (if already pushed)
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch secrets.env' \
--prune-empty --tag-name-filter cat -- --allFor already-pushed secrets, use git filter-branch or the faster git filter-repo to purge the file from your entire Git history. Then notify your team to rotate the exposed credentials. Be aware that rewriting history for already-pushed commits requires force pushing and coordination with everyone who has cloned the repository.
Scenario: Undoing a pushed feature branch
When a full feature branch needs to be rolled back from main after merging, use revert on the merge commit rather than trying to reset the branch pointer:
# Find the merge commit
git log --oneline --merges -1
# abc1234 Merge pull request #456 from feature/new-checkout
# Revert it, keeping mainline
git revert -m 1 abc1234
git push origin mainThis cleanly undoes the entire feature while preserving the audit trail. If you need to re-apply the feature later after fixes, revert the revert commit.
Scenario: Undoing a merge commit
Undoing a merge is trickier because reset removes all commits from both branches:
# Revert a merge commit
git revert -m 1 <merge-commit-hash>The -m 1 flag tells Git to revert to the first parent (the branch you were on when you merged). This is the safest way to undo a merge that’s already been shared.
Scenario: Undoing a pushed commit without revert
If you cannot use revert (your team prefers linear history or you need to edit the commit rather than just undo it), the safest approach is to create a new branch, revert on the new branch, and merge:
# Create a fix branch from the current state
git checkout -b fix/bad-commit
git revert HEAD --no-edit
git push -u origin fix/bad-commit
# Open a PR and merge normallyThis avoids force pushing on shared branches while still achieving the undo. The original bad commit remains in history, but the revert commit neutralizes its effect.
Scenario: Interactive undo with git rebase
For more complex undo scenarios involving several commits in the middle of history, interactive rebase gives you surgical control:
git rebase -i HEAD~5
# In the editor, delete the line of the commit you want to remove
# Save and exit — that commit is gone from historyThis is useful when you need to remove a specific commit from the middle of your history without affecting the commits before or after it. Only use this on branches that haven’t been pushed.
More Git help: Learn how to stash changes, branching strategies, and advanced Git commands.
FAQ
Q: What is the difference between git reset --soft and git reset --mixed?
A: --soft keeps changes staged (ready to recommit). --mixed unstages changes but keeps them in your working directory. Use --soft to fix a commit quickly; use --mixed to review what changed before recommitting.
Q: Can I undo a commit that was pushed days ago?
A: Yes. Use git revert <commit-hash> to create a new commit that undoes the changes. This is safe on shared branches because it preserves history.
Q: What does HEAD~1 mean?
A: It means “one commit before HEAD.” HEAD~3 means three commits before HEAD. You can also use HEAD^ as shorthand for HEAD~1.
Q: Is git commit --amend the same as git reset --soft followed by git commit?
A: Almost. --amend replaces the last commit with a new one that includes the additional changes. reset --soft followed by git commit creates a separate commit with a new hash.
Q: How do I undo a commit that is not the most recent?
A: Use git revert <hash> (safe, creates a new commit) or git rebase -i and delete the commit line (dangerous, rewrites history).
Q: What is the best way to undo changes that are not yet committed?
A: For unstaged changes: git restore <file>. For staged changes: git restore --staged <file> then git restore <file> if you want to discard them entirely.
Related Articles
Developer Tools
Advanced Git Commands Every Developer Should Know
Go beyond basic git with advanced commands: interactive rebase, bisect, reflog, cherry-pick, stash, and submodules. Real-world use cases included.
Git
Git Merge Strategies: Recursive, Octopus & Custom Drivers
Understand Git merge strategies — recursive, octopus, ours, subtree. Learn when to use each strategy with conflict resolution options.
Git
Git Revert vs Reset: Undoing Changes the Right Way
Learn the difference between git revert and git reset. When to use each, how to undo commits, and safe recovery strategies.
Git
Advanced Git Hooks: Automation, Enforcement & CI Integration
Advanced Git hooks — shared hook frameworks, pre-receive server hooks, CI integration, branch policy enforcement, and debugging hook scripts.
Developer Tools
Dotfiles: Managing Developer Configurations
Git-based dotfiles, symlinks, automation — managing and synchronizing developer configurations across machines.