Step-by-step
-
1
Understand your situation first
Before running anything, check whether the commit has been pushed. If it exists only in your local repo, you can rewrite history freely. If it has already been pushed to a shared branch, rewriting history will force-conflict with everyone else's clone.
bash# See if your local branch is ahead of the remote git status # or inspect the log git log --oneline -5 -
2
Undo unpushed commit, keep your changes
Use
git reset --soft HEAD~1. This moves the branch pointer back one commit but leaves all changes staged and ready to re-commit. Nothing is lost — the diff still sits in the index.bashgit reset --soft HEAD~1 # Your changes are still staged git status -
3
Undo unpushed commit, keep changes unstaged
The default
--mixedflag moves the branch pointer back and unstages the changes, but keeps them in your working tree. Good when you want to re-stage only part of the diff.bashgit reset HEAD~1 # equivalent to: git reset --mixed HEAD~1 # Changes are unstaged but present in working tree -
4
Undo unpushed commit and discard changes entirely
Warning: this is destructive.
git reset --hard HEAD~1moves the branch pointer back and deletes all changes in the index and working tree. There is no Ctrl-Z afterward — unless you usegit reflog(covered below).bash# DESTRUCTIVE — the changes are gone from your working tree git reset --hard HEAD~1 -
5
Undo an already-pushed commit safely
Never rewrite history that has already been pushed to a shared branch. Instead, use
git revert, which creates a new commit that undoes the changes. History stays intact, teammates are not disrupted.bash# Creates a new commit that is the inverse of HEAD git revert HEAD # For a specific commit SHA: git revert a3f9c12 -
6
Fix a typo in the last commit message
If the commit has not been pushed yet,
git commit --amendrewrites the most recent commit — message, author, or even staged files. It rewrites history (a new SHA is created), so never amend after pushing.bash# Change only the message git commit --amend -m "Fix login redirect on expired session" # Open your editor to also change the message body git commit --amend -
7
Recover from an accidental --hard reset
Git does not immediately garbage-collect unreachable commits. Use
git reflogto find the dangling commit SHA and reset back to it within the default 90-day grace period.bash# List recent HEAD positions git reflog # Example output: # a3f9c12 HEAD@{0}: reset: moving to HEAD~1 # 8b1d3e7 HEAD@{1}: commit: Add user profile page # Restore to the commit you lost git reset --hard 8b1d3e7
Tips & gotchas
- Use <code>git reset --soft</code> when you committed too early and want to add more changes to the same commit.
- Never use <code>git reset --hard</code> or <code>git push --force</code> on a branch that teammates are working on.
- <code>git reflog</code> is your safety net — it keeps a log of every HEAD movement for 90 days by default.
- If you only want to un-stage a single file without touching the commit, use <code>git restore --staged <file></code>.
Wrapping up
The golden rule: if it's local, reset freely; if it's pushed, revert instead. Keep git reflog in your back pocket for the rare moments a --hard reset goes wrong. With these three commands — reset --soft, reset --hard, and revert — you can handle every undo scenario Git throws at you.