Git

Undo Last Git Commit Without Losing Changes

Undoing a commit in Git doesn't have to mean losing the work in it — the right command depends entirely on whether the commit has already been pushed, and whether you want to keep the changes staged, unstaged, or completely rewrite the commit itself.

The Problem

You just committed something — wrong message, forgot a file, committed too early, or want to split it into smaller commits — and you want to undo the commit itself while keeping all the actual file changes so you don't have to redo the work.

Why It Happens

This isn't really an "error" scenario, but people often reach for the wrong command here and end up either losing changes accidentally, or creating messy history on a shared branch. The right approach depends on your exact situation:

  • You want to undo the commit but keep the changes staged, ready to commit again immediately
  • You want to undo the commit and have the changes unstaged, so you can review or reorganize them before recommitting
  • You just want to fix the last commit's message or add a forgotten file, without undoing anything
  • The commit was already pushed to a shared branch, so rewriting history locally isn't safe without coordinating with others

The Fix

If you haven't pushed yet and want to undo the commit while keeping changes staged (ready to immediately commit again):

git reset --soft HEAD~1

This moves the branch pointer back one commit, but leaves all the file changes exactly as they were, already staged. Running git status right after will show everything as "Changes to be committed" — nothing is lost.

If you want to undo the commit and have the changes unstaged instead, so you can selectively re-stage or edit them first:

git reset HEAD~1

This is the default behavior of git reset (technically --mixed, which is the default mode) — commit undone, files modified on disk, but nothing staged yet.

If you only need to fix the commit message or add one small forgotten file to the most recent commit, rather than fully undoing it, use --amend instead of reset:

git add forgotten-file.js
git commit --amend

This opens your editor to adjust the commit message if needed, and folds the staged changes into the previous commit as if they'd always been part of it.

If the commit has already been pushed to a shared branch, avoid rewriting history with reset or amend unless you're certain no one else has pulled it yet. Instead, use revert, which creates a brand new commit that undoes the changes, without altering existing history:

git revert HEAD

This is the safe option for shared branches — everyone's history stays consistent, and the undo itself becomes a normal, visible commit in the log.

Still Not Working?

If you already ran reset --hard by mistake and it looks like your changes are genuinely gone (not just unstaged, but deleted from disk), don't panic immediately — the commit itself may still be recoverable through the reflog, even though the working directory changes from a hard reset are harder to recover:

git reflog

Find the commit hash from before the reset, and check it out into a new branch to recover the commit itself:

git checkout -b recovered-commit <commit-hash>

Note that this recovers the commit's content, but any uncommitted changes that existed only in your working directory (never staged or committed at all) are not tracked by Git and cannot be recovered this way — this is exactly why committing work-in-progress frequently, even with a placeholder message you plan to clean up later, is safer than leaving significant changes uncommitted for long stretches.