Terminal showing git log commit history before undoing last Git commit safely

You just committed, and immediately realized something is wrong: the commit message has a typo, you committed the wrong files, or you simply are not ready to commit yet. The good news is that Git makes this easy to fix. This guide shows you how to undo your last Git commit while keeping your changes safe, with three beginner-friendly methods and copy-paste commands that just work.

The key concept to understand first: in Git, a commit is a snapshot, and “undoing” a commit usually means moving the branch pointer back one step while deciding what happens to the files. Your changes do not have to disappear — in every method below, your actual work stays intact. The only thing that changes is how Git records it. Let us look at the three safe ways, from gentlest to most deliberate.

In This Guide

Before You Start: Check Where Your Commit Lives

Terminal with git reset soft HEAD command to undo last commit and keep changesThe safe method depends on one question: have you pushed the commit to a remote (like GitHub) yet? Check with:

git log --oneline -3
git status

git log --oneline -3 shows your last three commits. git status tells you whether your branch is “ahead of origin” — meaning you have local commits nobody else has seen yet. The rules are simple:

  • Commit is only on your computer (not pushed, or pushed only to your own private branch): Methods 1 and 2 (git reset) are safe and clean.
  • Commit is pushed to a shared branch (teammates may have it): use Method 3 (git revert). Never rewrite history other people have already pulled.

If you are unsure, assume the commit is shared and use git revert. It is always safe; git reset on pushed commits is the one move that can genuinely cause trouble for a team.

Method 1: Undo the Commit but Keep Everything Staged (git reset –soft)

This is the gentlest undo. It moves the branch pointer back one commit but leaves the staging area (index) and your working files exactly as they were. After running it, it is as if you staged everything but had not yet typed git commit.

# Undo the last commit, keep all changes staged
git reset --soft HEAD~1

HEAD~1 means “one commit before the current one.” After this command:

  • The commit is gone from your history (git log no longer shows it).
  • All the changes are still staged — git status shows them under “Changes to be committed,” exactly as they were before you committed.
  • Your working files are untouched.

Use --soft when the commit’s content is fine but you want to redo the commit itself — for example, to split it into two commits, change the message along with adding one more file, or simply recommit more carefully:

git reset --soft HEAD~1
# now adjust, add more files if needed...
git add forgotten-file.py
git commit -m "Add login validation with proper error messages"

Nothing is lost at any point in this flow, which is why --soft is the recommended first choice for beginners undoing a local commit.

Method 2: Undo the Commit and Unstage the Files (git reset –mixed)

This is the default git reset mode and the most common undo in daily Git use. It moves the branch pointer back one commit, unstages everything, but keeps your working files untouched. Afterwards it is as if you edited the files but never staged or committed anything.

# Undo the last commit, unstage changes, keep working files
git reset HEAD~1
# (equivalent to: git reset --mixed HEAD~1)

After this command:

  • The commit is gone from history.
  • git status shows the changes under “Changes not staged for commit.”
  • Every edit is still in your files, byte for byte.

Choose --mixed (the default) when you want to rethink what goes into the commit — unstage some files, split changes across multiple commits, or leave something out entirely:

git reset HEAD~1
git add login.py
git commit -m "Add login validation"
git add debug-notes.txt
git commit -m "Add debugging notes"

One commit cleanly becomes two. A word of warning about the third reset mode, --hard: git reset --hard HEAD~1 deletes the changes from your working files too. It has legitimate uses, but it is the only command in this guide that can destroy work, so beginners should treat it as off-limits until they fully understand the reflog safety net below.

Method 3: Undo a Commit You Already Pushed (git revert)

When the commit is already on GitHub (or any shared remote), rewriting history with git reset would create chaos for everyone who already pulled it. The safe alternative is git revert, which does not erase the commit — it creates a new commit that undoes the old one’s changes.

# Create a new commit that undoes the last commit
git revert HEAD

Git opens your editor with a pre-filled message like Revert "Add login validation". Save and close, and you get a new commit whose diff is the exact inverse of the bad one. Push normally:

git push origin main

Why is this safe for shared branches? Because history only moves forward — teammates pull the revert commit just like any other commit, and nobody’s local history conflicts with yours. The original commit remains visible in the log, which is actually a feature: the history honestly records that a change was made and then deliberately undone, and git log tells the full story.

To revert an older commit (not the last one), pass its hash: git revert a1b2c3d. If the revert itself conflicts — because later commits built on the reverted code — Git pauses and asks you to resolve the conflict exactly like a merge conflict, then continue. If the push gets rejected because the remote moved while you worked, see our guide to fixing the non-fast-forward push error.

Bonus: Fix Just the Commit Message

If the commit’s content is perfect and only the message has a typo, you do not need to undo anything. Amend the message in place:

git commit --amend -m "Add login validation with proper error messages"

This replaces the last commit with an otherwise identical commit carrying the new message. Like git reset, only use --amend on commits you have not pushed to a shared branch. (Amending a pushed commit is possible with a force push, but on shared branches it creates the same history-rewriting problems as reset.)

You can also use --amend to add a forgotten file to the last commit without creating a second commit:

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

The --no-edit flag keeps the existing message. This is the fastest fix for the classic “I committed and immediately realized I forgot a file” moment.

When NOT to Use git reset

git reset rewrites history, so there are situations where it is the wrong tool:

  • The commit is pushed to a shared branch. Teammates have the old history; your rewritten history will collide with theirs on the next pull. Use git revert instead.
  • You have uncommitted work you care about and are considering --hard. git reset --hard discards working-tree changes permanently. Stash first (git stash) or commit your work-in-progress, then reset.
  • You are in the middle of a merge or rebase. Finish or abort that operation first (git merge --abort / git rebase --abort). Resetting mid-operation produces confusing states.
  • Someone else’s branch. Never rewrite history on a branch you do not own without coordinating with its owner.

The underlying principle from the official git-reset documentation: reset is for cleaning up your own unpublished work; revert is for correcting published history. When in doubt, revert — it is never wrong, only slightly more verbose in the log.

Undo Just the Staging Area (Unstage Files Without Touching the Commit)

Sometimes the problem is smaller: you staged files with git add but have not committed yet, and you want to unstage them while keeping the edits. Modern Git has a dedicated command:

# Unstage a specific file (keeps your edits)
git restore --staged config.py

# Unstage everything
git restore --staged .

Older tutorials show git reset HEAD config.py, which does the same thing — git restore --staged is simply the newer, clearer spelling. Either way, the file’s content is untouched; only the staging area changes. This is also the escape hatch if you ran Method 1 (--soft) and then decided you actually wanted the files unstaged: follow it with git restore --staged . and you have effectively performed Method 2.

Safety Net: Recover Anything With git reflog

Here is the secret that makes all of this low-stress: Git almost never truly deletes commits right away. Every move your branch pointer makes is recorded in the reflog, and you can resurrect “lost” commits from it for weeks.

# Show every recent position of HEAD
git reflog

You will see output like:

a1b2c3d HEAD@{0}: reset: moving to HEAD~1
e4f5g6h HEAD@{1}: commit: Add experimental feature

If you reset away a commit you actually wanted, bring it back by creating a branch at the old position:

git branch recovery e4f5g6h
git log recovery --oneline -3   # verify it is the right commit
git merge recovery              # or cherry-pick what you need

This safety net is why --soft and --mixed resets are genuinely safe: even a mistaken reset is recoverable. (The one exception is --hard combined with uncommitted changes — those were never committed, so the reflog cannot restore them. Yet another reason beginners should avoid --hard.) The GitHub docs on rewriting history reinforce the same golden rule: never rewrite commits that others have based work on.

Frequently Asked Questions

What is the difference between git reset –soft, –mixed, and –hard?

All three move the branch pointer back, but they differ in what happens to your files. --soft keeps changes staged (as if you had not committed yet). --mixed (the default) unstages changes but keeps them in your working files. --hard discards the changes from your working files entirely — the only destructive option, and the one beginners should avoid.

Will git reset delete my code?

--soft and --mixed never touch your working files, so your code is safe. Only --hard deletes uncommitted changes. And even after a reset, the old commit usually survives in the reflog for weeks, so a mistaken reset is recoverable with git reflog.

How do I undo the last commit if I already pushed it?

Use git revert HEAD, which creates a new commit that undoes the pushed commit’s changes, then push normally. Do not use git reset on pushed shared-branch commits — rewriting published history breaks your teammates’ repositories.

Can I undo multiple commits at once?

Yes. git reset --soft HEAD~3 undoes the last three local commits while keeping all their changes staged. For pushed history, revert each commit individually or use git revert HEAD~3..HEAD to revert a range in one go.

What does HEAD~1 mean?

HEAD is Git’s name for your current commit, and ~1 means “one step back from it.” So HEAD~1 is the commit before the current one, HEAD~2 is two back, and so on. You can also use a commit hash instead of HEAD~1 for precision.

I ran git reset –hard by accident. Is my work gone?

Committed work is recoverable: check git reflog for the commit’s hash and restore it with git branch recovery <hash>. Truly uncommitted changes that were never staged or committed cannot be recovered by Git — which is why committing early and often is the best habit a beginner can build.

Key Takeaways

  • First check whether the commit is pushed: local-only commits can be reset; pushed shared commits should be reverted.
  • git reset --soft HEAD~1 undoes the commit but keeps everything staged — the gentlest option.
  • git reset HEAD~1 (mixed, the default) undoes the commit and unstages the files while keeping every edit.
  • git revert HEAD safely undoes a pushed commit by adding a new inverse commit on top.
  • Fix a bad message alone with git commit --amend; unstage without committing via git restore --staged.
  • Avoid git reset --hard as a beginner, and remember git reflog can resurrect almost any “lost” commit.

Conclusion

Undoing your last Git commit while keeping your changes is a routine operation, not an emergency: use --soft to redo the commit, --mixed to rethink its contents, --amend for a quick message fix, and git revert whenever the commit already left your machine. Learn which situation calls for which command, keep the reflog safety net in your back pocket, and you will never again fear the commit button — because every commit is reversible when you know how to undo it properly.

Leave a Reply

Your email address will not be published. Required fields are marked *