What you'll learn
- Quick answer
- What "undo a commit" actually means
- Fix the last commit with git commit --amend
- Move the branch back with git reset (--soft, --mixed, --hard)
- Undo a pushed commit safely with git revert
- Which one should you use?
- The golden rule for shared branches
- Ran --hard by mistake? Recover with git reflog
- FAQ
Quick Answer
To undo the most recent commit but keep your changes, run git reset --soft HEAD~1. To fix the last commit's message or files, use git commit --amend. If the commit is already pushed to a shared branch, don't rewrite history, use git revert to create a new commit that cancels it out. Only git reset --hard deletes your work, so use it carefully.
What "undo a commit" actually means
The first time a commit goes wrong, panic is normal. You committed too early, wrote a messy message, or added a file you didn't mean to. The good news: Git almost never throws work away without being told to. Learning how to undo a commit in Git is really about answering two small questions before you type anything.
- Do you want to keep the changes from that commit, or delete them completely?
- Has the commit been pushed to a branch other people share, or is it still only on your machine?
Those two answers point you straight to the right tool. Keeping your changes and undoing a local commit is a job for git reset or git commit --amend. Undoing something you already pushed is a job for git revert. Get these two questions right and you will almost never lose work.
Before you touch anything, look at where you are:
git status
git log --oneline -5The git log line shows your last five commits with their short hashes. Keep that terminal open, you'll want to see what changes after each command.
Fix the last commit with git commit --amend
Often you don't want to remove a commit at all, you just want to fix the most recent one. Maybe the message had a typo, or you forgot to stage one file. That's exactly what --amend is for.
To change only the message of the last commit:
git commit --amend -m "Add login form validation"To add a forgotten file to the last commit without changing the message, stage it first, then amend:
git add forgotten-file.js
git commit --amend --no-editThe --no-edit flag keeps the existing message. The forgotten file now rides along inside the previous commit, as if you'd never missed it.
The catch: amending creates a brand-new commit with a new hash, it does not edit the old one in place. That's fine when the commit is still only on your computer. If you already pushed it, amending rewrites history, and that causes problems for teammates (more on that below). Rule of thumb: amend freely before you push, think twice after.
Move the branch back with git reset (--soft, --mixed, --hard)
git reset moves your branch pointer backward, effectively "removing" one or more commits from the tip of your branch. The flag you choose decides what happens to the actual code those commits contained. This is the part beginners find confusing, so let's go slowly. HEAD~1 just means "one commit before where I am now".
--soft: keep everything, staged and ready
Removes the commit but leaves all its changes staged, as if you had just run git add on them. This is the safest and most common way to undo a local commit.
git reset --soft HEAD~1Use this when you committed too early and want to re-commit, maybe splitting the work into two cleaner commits.
--mixed: keep everything, unstaged (the default)
Removes the commit and unstages the changes, but keeps them in your working files. If you run git reset HEAD~1 with no flag, this is what you get.
git reset --mixed HEAD~1Use this when you want to re-pick which files or lines go into the next commit.
--hard: delete the changes too
Removes the commit and permanently discards the changes in your working files. There is no confirmation prompt.
git reset --hard HEAD~1Only use --hard when you are sure you want those changes gone. It is the one command in this guide that can actually lose work. Before running it, it's worth running git status so you know exactly what you're about to throw away.
Undo a pushed commit safely with git revert
Everything above rewrites history, it changes which commits exist. That's perfectly safe on your own machine, but dangerous once a commit is on a shared branch like main. For that situation, Git gives you git revert.
Instead of deleting the bad commit, revert creates a new commit that undoes its changes. History stays intact and moves forward, which is exactly what your teammates need.
git revert HEADThat reverts the most recent commit. To revert a specific older commit, pass its hash (you can copy it from git log --oneline):
git revert 4f2a1c9Git opens an editor with a ready-made message like Revert "Add login form validation", save and close it to finish. If you'd rather skip the editor, add --no-edit. The original commit still exists in history, and a new commit sits on top cancelling it out. Safe, honest, and easy for anyone to trace later.
Think ofrevertas an undo you can show in public, andresetas an undo you do quietly before anyone sees.
Which one should you use?
Here's the whole decision on one screen. Match your situation to a row.
| Method | Keeps your changes? | Rewrites history? | Safe after push? |
|---|---|---|---|
git commit --amend | Yes | Yes | No |
git reset --soft | Yes (staged) | Yes | No |
git reset --mixed | Yes (unstaged) | Yes | No |
git reset --hard | No | Yes | No |
git revert | Yes (as a new commit) | No | Yes |
Quick recommendations:
- Typo in the last message, or forgot a file? Use
git commit --amend. - Committed too soon and it's still local? Use
git reset --soft HEAD~1, then re-commit cleanly. - Already pushed to a shared branch? Use
git revert. Don't reset. - Want the changes gone for good? Use
git reset --hard, carefully.
If you want to practise these with guided exercises, our free Git course walks through each command with hands-on examples.
The golden rule for shared branches
This is the one gotcha that trips up almost every beginner, so read it twice.
Never rewrite history on a branch other people have already pulled.
reset and --amend both change which commits exist. When you then push that changed history, Git refuses a normal push and you're tempted to use git push --force. On a shared branch, force-pushing can erase your teammates' commits and leave everyone with a broken, conflicting copy of the project. It's one of the fastest ways to cause a mess in a team.
So the safe pattern is simple:
- Commit only on your machine, not pushed yet?
resetand--amendare perfectly fine. - Commit already pushed and shared? Use
git revertinstead. It moves history forward, so a normalgit pushworks and nobody loses anything.
The one exception is a private branch that is truly yours, for example your own feature branch that nobody else has pulled. There, a git push --force-with-lease after amending is acceptable. --force-with-lease is safer than --force because it refuses to overwrite work you haven't seen yet.
Ran --hard by mistake? Recover with git reflog
Say you ran git reset --hard and your heart sank, the commit looks gone. In most cases, it isn't. Git keeps a private log of everywhere HEAD has been, called the reflog, and it's your safety net.
git reflogYou'll see a list like this, newest at the top:
a1b2c3d HEAD@{0}: reset: moving to HEAD~1
9f8e7d6 HEAD@{1}: commit: Add login form validation
4c5b6a7 HEAD@{2}: commit: Set up projectThe commit you "lost" is the one just before your reset, here 9f8e7d6 at HEAD@{1}. To bring it back, reset to that entry:
git reset --hard HEAD@{1}Or, if you'd rather inspect it first without moving your branch, check it out by its hash:
git checkout 9f8e7d6A couple of things to know: the reflog is local to your machine and isn't shared, and entries expire after about 90 days by default. That's plenty of time to fix a bad afternoon, but it means the reflog is a rescue tool, not a backup. The real lesson is to run git status before any --hard, so you rarely need the reflog at all.
Frequently Asked Questions
How do I undo the last commit but keep my changes?
Run git reset --soft HEAD~1. This removes the commit but keeps all its changes staged, so you can immediately re-commit them, split them into smaller commits, or edit them first. Nothing is deleted, and it's completely safe as long as you haven't pushed the commit yet.
What is the difference between git reset and git revert?
git reset moves your branch pointer backward and rewrites history, which is ideal for local commits nobody has seen. git revert leaves history intact and adds a new commit that cancels out the old one, which is the safe choice for commits already pushed to a shared branch.
Is git reset --hard reversible?
Usually, yes, for a short window. Even after git reset --hard, the old commit stays in your local reflog for about 90 days. Run git reflog, find the commit, and restore it with git reset --hard HEAD@{1}. Uncommitted changes that were never committed, however, cannot be recovered, so always check git status first.
How do I undo a commit I already pushed to GitHub?
If the branch is shared with others, use git revert <commit-hash> and push the new revert commit. Avoid git reset plus git push --force on shared branches, because force-pushing rewritten history can erase your teammates' work. Reserve force-push for private branches only you use.
How do I just fix the message of my last commit?
Run git commit --amend -m "Your corrected message". This replaces the most recent commit with a corrected version. Do this before pushing. If the commit is already on a shared branch, amending rewrites history and can disrupt teammates, so leave it or use a revert instead.
What does HEAD~1 mean in these commands?
HEAD is a pointer to your current commit, and HEAD~1 means "one commit before that". So git reset --soft HEAD~1 moves your branch back by a single commit. Use HEAD~2 to go back two commits, and so on. You can also target an exact commit by its hash, which you can copy from git log --oneline.
