Git Undo Last Commit: Keep Changes, Stay Safe

Git Undo Last Commit: Keep Changes, Stay Safe

Git remove or undo the last commit and keep the changes: git reset --soft HEAD~1. Unstage with --mixed, fix a pushed commit with revert. Every verb, one page.

HUMANby @mrsaynothing53d7 min read

Remove, delete, cancel or undo — whatever verb you searched, the command that keeps the changes is the same one. Those three commands cover almost every "I committed too early" moment. The rest of this guide walks through each case with copy-paste commands, explains the difference between --soft, --mixed and --hard, and shows how to recover if something goes wrong. Every command below works on any recent Git install on Linux, macOS or Windows.

The 10-second decision

undo the last commit — which case? already pushed not pushed shared branch git revert HEAD keep changes staged git reset --soft HEAD~1 keep changes unstaged git reset HEAD~1 discard everything branch backup + reset --hard wrong branch, good work cherry-pick, don't undo regret anything? git reflog knows ~90 days
fig 1 — pick the exit before touching a flag. Click your branch.

How do I undo the last commit and keep the changes?

The most common situation: you committed, then spotted a typo, a missing file, or realised the change belongs in a different commit. Nothing is pushed yet. Undo the commit and put everything back where it was:

# Commit stays in history nowhere — changes go back to the staging area
git reset --soft HEAD~1

# Changes go back to the working tree (unstaged) instead
git reset HEAD~1

HEAD~1 means "one commit before where HEAD points now". After either command your files are untouched on disk — only the branch pointer moved. Check it with git status: with --soft the changes are staged, ready to re-commit with fixes folded in; without a flag they are unstaged, so you can edit freely first.

verify: git status → changes staged (--soft) or unstaged (default) · git log --oneline -1 → the commit before

A safer habit when you just want to add files to the last commit is not to undo it at all:

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

--amend replaces the last commit in place (no commit message change here). Note that amending a pushed commit rewrites history — more on that below.

What is the difference between --soft, --mixed and --hard?

This is the part worth memorising, because the flag decides where your changes end up — and whether they can be lost:

Whichever mess is yours, the consequence sits in the last column.

all soft mixed hard revert
Flag Commit undone? Changes on disk Changes staged? Typical use
--soft Yes Kept Yes Re-commit with small fixes
--mixed (default) Yes Kept No Re-group changes, re-stage selectively
--hard Yes Deleted — Throw the work away entirely
git revert No (new commit) Kept — Undo a commit that was already pushed

--hard is the only dangerous one: it discards the commit and the changes. Before any reset --hard, stash or branch what you have:

git branch backup-before-reset   # cheap insurance
git reset --hard HEAD~1          # commit + changes gone

If you already ran the dangerous version, all is not lost — git reflog remembers where HEAD has been:

git reflog                       # find the commit hash you lost
git reset --hard HEAD@{1}        # or: git reset --hard <hash>

The reflog keeps dangling commits for around 90 days by default, so "I hard-reset by mistake" is almost always recoverable if you act before garbage collection.

verify: git reflog → the dropped commit sits at HEAD@{1}

What if I already pushed the commit?

Shared history gets a new commit, never a rewrite.

If the commit is on a shared branch (anything other than your own feature branch), do not rewrite history. Use git revert, which computes the opposite change and commits it:

git revert HEAD
git push

Everyone who pulls simply gets a new commit that removes the old one's changes. No force-push, no broken colleagues. If you need to undo a run of commits, revert a range: git revert --no-commit HEAD~3..HEAD && git commit.

Why --force-with-lease, never --force

The alternative — git reset --hard HEAD~1 && git push --force-with-lease — is only acceptable on a branch nobody else builds on. --force-with-lease refuses the push if the remote moved since your last fetch; plain --force overwrites whatever appeared. Force-pushing shared branches is how teams lose commits and CI logs mysteriously stop matching anyone's checkout.

verify: git log --oneline -2 → Revert "…" sits on top of the undone commit

How do I undo the last commit but keep it around for later?

Sometimes the commit is good work at the wrong address — on the wrong branch, or too early. Instead of undoing it, move it:

git branch stash-commit          # park the commit on a new branch
git reset --hard HEAD~1          # then clean your current branch

Or take just the commit to another branch without touching your current one:

git cherry-pick <hash>           # while on the target branch

Several commits in one go is the same move with a range — cherry pick multiple commits from another branch.

Between reset --soft, cherry-pick and revert, there is no commit you cannot relocate or nullify — the trick is choosing "move" versus "undo" before reaching for a flag.

Undo, revert, reset, remove, cancel — the word is the command

Half the confusion is vocabulary: six verbs people type into the search box, three commands that actually exist. Match the word in your head to the flag that does it:

Every spelling on that list now has a one-stop answer page, too — the undo, cancel, revert FAQ collects the whole keep-changes family from our search board on one tab.

The word you typed What you want The command Changes kept?
undo / uncommitcommit gone, work backgit reset --soft HEAD~1yes — staged
undo, unstagedcommit gone, work back loosegit reset HEAD~1yes — mixed
remove / dropcommit and edits gonegit reset --hard HEAD~1no — reflog ~90 days
revert / cancel, pushedpublic undo, history intactgit revert HEADyes — new commit

The verbs are not synonyms in Git: revert never touches the past, reset rewrites it. Say the wrong word to a teammate and you get the wrong command.

Which undo should I use? A quick decision guide

  1. Not pushed, want to fix and re-commit → git reset --soft HEAD~1
  2. Not pushed, want to re-stage selectively → git reset HEAD~1 (mixed)
  3. Want the changes gone completely → git reset --hard HEAD~1 (reflog knows, if you regret it)
  4. Already pushed to a shared branch → git revert HEAD
  5. Commit belongs on another branch → cherry-pick, don't undo

Want the decision made for you? The git undo generator asks these three questions and prints the command.

And when Git refuses to run at all — fatal: not a git repository — that is a different problem from undoing anything: the 60-second fix for the error itself.

One last operational tip: if a bad commit did make it to a server — a botched deploy hook, a CI runner misbehaving after a force-push — the next place to look is the machine's logs, not Git. On any systemd box, journalctl -u <service> -n 100 shows you exactly what ran and when; our journalctl cheat sheet has the copy-paste patterns. And if your workflow includes local LLM tooling to review diffs, we compared the two main options in Ollama vs LM Studio. And the whole undo cluster — reset, revert, cherry-pick, clean — lives on the git-undo hub.