Blog / Coding tips

Git Undo Last Commit but Keep Changes (Tested)

Cover image for Git Undo Last Commit but Keep Changes (Tested)

To undo your last Git commit but keep the changes, run git reset --soft HEAD~1. The commit disappears, and everything that was in it stays staged, ready to commit again. Use git reset HEAD~1 if you want the changes kept but unstaged, git reset --hard HEAD~1 only if you want to throw them away, and git revert HEAD if you've already pushed the commit. Below I try each one on a real test repository and show the exact output, so you can see what happens before you run it on your own project.

I've needed this more times than I'd like to admit: committing to the wrong branch, committing a console.log, and once nearly committing an .env.local file with an API key in it (the reason I wrote how to add a Gemini API key to Next.js and Vercel the safe way). Every command here was run with Git 2.47 in a throwaway repo with two commits:

$ git log --oneline
dbe2949 Add app.js (oops, debug log)
12f8028 Add base styles

Quick answer: which command should I use?

  • Keep the changes, staged: git reset --soft HEAD~1
  • Keep the changes, unstaged: git reset HEAD~1 (the same as --mixed)
  • Delete the commit and the changes: git reset --hard HEAD~1
  • Just fix the message or add a forgotten file: git commit --amend
  • The commit is already pushed: git revert HEAD, then push

HEAD~1 means "one commit before the one I'm on". You'll also see HEAD^, which means the same thing for a normal commit.

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

This is the one most people are looking for. It moves your branch back by one commit but leaves your files and the staging area exactly as they were just before you ran git commit:

$ git reset --soft HEAD~1
$ git log --oneline
12f8028 Add base styles
$ git status --short
A  app.js

The commit is gone from the log, and app.js shows A (added, staged). You can now edit the file, stage more files, or simply run git commit with a better message. Nothing on disk was touched.

git reset HEAD~1: undo the commit, keep changes unstaged

Without a flag, git reset uses --mixed. It removes the commit and unstages the changes, but your files on disk are still there:

$ git reset HEAD~1
$ git log --oneline
12f8028 Add base styles
$ git status --short
?? app.js

?? means untracked: the file exists, but Git isn't staging it. This is handy when you accidentally committed several unrelated changes together and want to split them up with separate git add commands. For a file that was already tracked before, you'd see M (modified, not staged) instead.

git reset --hard HEAD~1: undo the commit and delete the changes

This one is destructive. It removes the commit and resets your working folder to match the previous commit, so the changes are gone from disk too:

$ git reset --hard HEAD~1
HEAD is now at 12f8028 Add base styles
$ ls
style.css

app.js has disappeared. Be careful: --hard also wipes out any uncommitted work in tracked files, and that work can't be recovered by Git because it was never saved in a commit. Run git status first, and if anything is listed that you want to keep, use --soft instead or git stash it first.

Recovering from a hard reset with git reflog

The good news is that a commit you reset away isn't deleted straight away. Git keeps a log of where HEAD has been, called the reflog, for around 90 days by default:

$ git reflog -3
12f8028 HEAD@{0}: reset: moving to HEAD~1
dbe2949 HEAD@{1}: commit: Add app.js (oops, debug log)
12f8028 HEAD@{2}: reset: moving to HEAD~1
$ git reset --hard HEAD@{1}
HEAD is now at dbe2949 Add app.js (oops, debug log)
$ ls
app.js
style.css

HEAD@{1} means "where HEAD was one move ago", which was the commit I'd just removed. You could also use the hash, git reset --hard dbe2949. Both brought app.js back.

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

Often you don't really want to undo the commit. You want to change its message, or add a file you forgot. --amend replaces the last commit with a new one that includes whatever is staged:

$ echo "console.log('ready');" > app.js
$ git add app.js
$ git commit --amend -m "Add app.js"
$ git log --oneline
5b8578c Add app.js
12f8028 Add base styles

Notice the hash changed from dbe2949 to 5b8578c. Amending creates a new commit; it doesn't edit the old one. That's why you shouldn't amend a commit you've already pushed to a branch other people use.

Undo a commit that's already pushed: git revert

Once a commit is on GitHub (or any shared remote), rewriting it with reset means you'd have to force-push, which can break things for anyone who has already pulled it. The safe option is git revert. It doesn't remove anything. It adds a new commit that does the exact opposite of the old one:

$ git push origin main
$ git revert --no-edit HEAD
$ git log --oneline
f25e17b Revert "Add app.js"
5b8578c Add app.js
12f8028 Add base styles
$ ls
style.css
$ git push origin main

In my test the second push went through as a normal push, no --force needed, because history only moved forward. Without --no-edit, Git opens your editor so you can explain why you reverted, which is a nice habit on team projects.

What if I pushed a password or API key?

Reverting is not enough, because the secret is still in the history and anyone can check out the older commit. Treat the key as leaked: revoke it and create a new one first, then clean up. I wrote more about keeping secrets out of the browser and the repo in how I kept my AI API key off the browser.

Committed to the wrong branch?

This is a classic. You meant to commit on a feature branch but you were on main. A soft reset makes it painless, because staged changes come with you when you switch branches:

git reset --soft HEAD~1
git switch -c feature/search
git commit -m "Add search box"

The commit now lives on feature/search, and main is back where it was. If the branch already exists, use git switch feature/search without -c.

Soft vs mixed vs hard, side by side

Git keeps track of three things: the commit history, the staging area (also called the index), and the files in your folder. Each mode of git reset rewinds a different number of them:

  • --soft rewinds only the history. Staged files and your folder stay as they were.
  • --mixed (the default) rewinds history and the staging area. Your folder stays as it was.
  • --hard rewinds all three. Your folder now matches the older commit.

The official git reset documentation has more examples, including undoing several commits at once with HEAD~3.

FAQ

How do I undo the last commit but keep my changes?

Run git reset --soft HEAD~1 to keep them staged, or git reset HEAD~1 to keep them as unstaged edits. In both cases your files are not changed.

How do I undo a git commit that hasn't been pushed?

Any of the git reset commands work, because nobody else has the commit yet. Pick --soft or the default mixed mode unless you're sure you want the changes deleted.

What is the difference between git reset and git revert?

git reset moves your branch back and removes commits from its history. git revert keeps the history and adds a new commit that reverses an old one, which is why it's the safe choice for pushed commits.

Can I undo a git reset --hard?

Yes, if the work was committed. Find the old commit in git reflog and run git reset --hard with its hash or HEAD@{n}. Uncommitted changes wiped by --hard can't be recovered by Git.

// note

How to read this note.

This is a learning note from studying the web. It is one small topic, written so I can remember it. It is not a course and not a claim that I have finished the subject.

If a sentence is wrong, say so from the contact page and name this title. Drafts never appear here. Related notes, when they exist, are other published posts, and the same sample rule applies to each of them.

Related notes

How to Work Out Your UK Degree Classification
Coding tips

How to Work Out Your UK Degree Classification

Work out a First, 2:1 or 2:2 from module marks: credit-weighted averages, 30:70 or 1:2 year weightings, rounding at 69.5 and the mark you need.

October 4, 2026 · 10 min read