Explore effective ways to undo Git commits using git reset and git revert without losing your work. Learn when to use each command and ensure a clean, conflict-free Git history.
You hit enter on a commit, and half a second later you see it: a stray debug file, a bug you meant to fix first, or a message that reads like nonsense. It happens to everyone. The good news is that Git gives you clean ways to walk a commit back without throwing away the work inside it.
The two tools you’ll reach for are git reset and git revert. They sound similar, but they do opposite things, and picking the wrong one on a shared branch is how you ruin a teammate’s afternoon. Let’s sort out which is which.
How Git thinks about commits
Every commit is a snapshot of your project at a moment in time, stamped with a unique SHA hash. Commits link back to their parents, so your history is really a chain: each snapshot points to the one before it.
When you undo a commit, you’re either moving the pointer that marks your current spot in that chain, or you’re adding a new snapshot that cancels out an old one. That distinction is the whole story. git reset moves the pointer. git revert adds a new commit. Everything below follows from that.
Why commits go wrong
A premature commit usually comes from one of a few places: you forgot to stage a file, the bug you thought you’d killed is still breathing, or you swept up files that never belonged in the commit. Whatever the cause, you’ve got two ways out:
git reset: Rewinds your branch to an earlier commit. Depending on the mode you pick, it can keep those changes staged, drop them back into your working directory, or wipe them entirely.git revert: Creates a brand new commit that undoes an earlier one. Your history stays intact, which is exactly what you want on a branch other people share.
Using git reset to Undo Commits
git reset is what you want when a commit shouldn’t exist in your history at all. It comes in three flavors: --soft, --mixed (the default), and --hard. They all move your branch pointer back; the difference is what they do to your staging area and your files.
One thing to be clear about up front: reset rewrites history. That’s fine when the commits are still local and only you have seen them. On anything you’ve already pushed, it’s trouble, and we’ll come back to why.
git reset --soft: Keeping Your Changes Staged
git reset --soft moves HEAD back to the commit you name and stops there. The changes from the undone commit stay staged, ready to go. This is the move when you committed too early and just want to add a little more before trying again.
Example:
git reset –soft HEAD~1This rewinds the last commit but leaves its changes staged, so you can tweak, add a forgotten file, and recommit with a cleaner message.
git reset --mixed: Keeping Your Changes in the Working Directory
git reset --mixed is the default, so you get it when you run git reset with no flag. It rewinds HEAD like --soft does, but it also unstages the changes. They’re still sitting in your working directory, just no longer staged, which is handy when you want to re-split your work into different commits.
Example:
git reset –mixed HEAD~2This rewinds the last two commits and unstages their changes, but the edits stay in your files so you can keep working.
git reset --hard: Removing All Local Changes
git reset --hard is the one to respect. It rewinds HEAD and clears both the staging area and your working directory to match. Any uncommitted work is gone, and it doesn’t stop at the commit you’re undoing: unstaged edits you had going get wiped too. There’s no trash can here.
Example:
git reset –hard HEAD~1This drops the last commit and resets your files to the previous commit’s state. Run it only when you’re sure you want that work gone.
When to Use git reset?
Reach for git reset when the commits are still local, haven’t been pushed anywhere, and you’re fine erasing them from history. It’s built for solo work on a branch nobody else has pulled.
Best Practices:
- Use
git reset --softwhen you want to redo a commit but keep everything staged. - Use
git reset --mixedwhen you want the commit gone but the files back in your hands to rework. - Use
git reset --hardonly when you genuinely want the changes destroyed. Double-check before you press enter.
Using git revert to Undo Commits Without Losing History
Once a commit is pushed and someone else might have it, resetting is the wrong tool. Rewriting shared history forces everyone to untangle their copies, and it’s the classic way to create a mess for the whole team. This is where git revert earns its keep. Instead of erasing a commit, it writes a new one that reverses the changes, leaving the original right where it is.
How git revert Works
When you revert a commit, Git builds a fresh commit that applies the exact opposite of what the target commit did. The bad change is neutralized, but the record of both commits stays in your history. Nobody has to rewrite anything on their end.
Example:
git revert HEADThis creates a new commit that cancels out the changes from the last commit.
Example: Reverting Multiple Commits
You can revert several commits at once by naming a range or by listing individual hashes.
Example:
git revert HEAD~2..HEADThis reverts the last two commits, creating a new commit for each one it undoes.
When to Use git revert?
Use git revert for anything that’s already been pushed or lives on a shared branch. It’s the safe choice for production and teamwork, because it fixes the mistake without rewriting history and yanking the ground out from under your collaborators.
Handling Conflicts with git revert
Reverting isn’t always frictionless. If later commits touched the same lines you’re trying to undo, Git can’t figure out the right result on its own. It’ll flag the conflicting spots and hand them to you to resolve by hand before the revert finishes.
Best Practices:
- Look at the commit you’re reverting first, so you know what it actually changed.
- Test the result locally before you push it to a shared branch.
Undoing Commits Safely: Final Thoughts
The rule is simpler than it looks. If the commit is still local and unpushed, git reset is fair game. If it’s been pushed or shared, use git revert so you’re adding to history instead of tearing it up.
Get that one distinction right and undoing a commit stops being scary. Treat git reset --hard with the caution it deserves, lean on git revert whenever other people are in the picture, and you can walk back almost any mistake without losing work or breaking anyone else’s day.


