You Made a Commit You Want to Undo. Now What?
It happens to everyone. You commit too early, include a file you should not have, write the wrong message, or realize the code just is not right. The good news: Git is designed to handle this. The better news: in most cases, you can undo a commit without losing any of your work.
The right approach depends on one key question: has the commit been pushed to a remote, or is it still local? Local commits give you more flexibility. Pushed commits require a safer strategy because other people may have already pulled your changes.
Undoing a Local Commit with git reset
If the commit is still local (not pushed), git reset is the most common tool. It moves the branch pointer back to a previous commit, effectively "un-committing" your changes. What happens to those changes depends on the flag you use.
git reset --soft: undo the commit, keep everything staged
git reset --soft HEAD~1
This undoes the last commit but keeps all the changes staged (in the index). Your working directory is untouched. It is as if you never ran git commit -- you are right back to the moment just before committing.
When to use it: You want to change the commit message, add more files to the commit, or split the commit into smaller ones. Your code is fine, you just committed prematurely.
git reset --mixed: undo the commit, unstage the changes
git reset --mixed HEAD~1
This is the default behavior of git reset (you can omit the --mixed flag). It undoes the commit and unstages the changes, but your files remain modified in the working directory. Nothing is lost.
When to use it: You want to rethink what to include in the commit. Maybe you want to stage only some of the files, or you want to review the changes before re-committing.
git reset --hard: undo the commit, discard all changes
git reset --hard HEAD~1
This undoes the commit and permanently deletes all the changes. Your working directory is reset to match the previous commit. There is no recovering the discarded changes through normal Git operations.
When to use it: You are certain you do not want the changes at all. Maybe you committed experimental code that did not work out and you want a clean slate. Use this with caution.
Resetting further back
HEAD~1 refers to one commit before the current HEAD. You can go further:
git reset --soft HEAD~3
This undoes the last three commits, combining all their changes into the staging area. You can also target a specific commit by its hash:
git reset --soft a1b2c3d
Fixing the Last Commit with git commit --amend
If you just need to tweak the last commit rather than fully undo it, --amend is the fastest option:
git commit --amend -m "corrected commit message"
This replaces the previous commit with a new one. You can also use it to add forgotten files:
git add forgotten-file.ts
git commit --amend --no-edit
The --no-edit flag keeps the original commit message. The result is a single, corrected commit instead of a sequence of "oops" commits.
Important: Like reset, amend rewrites history. Only use it on commits that have not been pushed. If you amend a pushed commit and force-push, anyone who has pulled the original commit will have problems.
Undoing a Pushed Commit with git revert
Once a commit has been pushed to a shared remote, you should not rewrite history. Other developers may have based their work on that commit. Instead, use git revert:
git revert HEAD
This creates a new commit that does the exact opposite of the target commit. If the original commit added a line, the revert commit removes it. If it deleted a file, the revert commit restores it. The original commit stays in the history, and the revert is added on top.
When to use it: Any time you need to undo a commit that has already been pushed. It is safe because it adds to the history rather than rewriting it.
Reverting an older commit
You can revert any commit, not just the most recent one:
git revert a1b2c3d
Git will attempt to create a revert commit that undoes only the changes from that specific commit. If those changes overlap with subsequent work, you may get a conflict that needs manual resolution.
Reverting multiple commits
To revert a range of commits:
git revert HEAD~3..HEAD
This creates individual revert commits for each commit in the range. If you prefer a single revert commit, add the --no-commit flag and commit manually:
git revert --no-commit HEAD~3..HEAD
git commit -m "revert last 3 commits"
Choosing the Right Approach
| Situation | Method | What happens to your changes |
|---|---|---|
| Committed too early, want to re-stage | git reset --soft HEAD~1 |
Changes remain staged |
| Committed too early, want to rework | git reset --mixed HEAD~1 |
Changes remain in working directory |
| Committed something you want to discard | git reset --hard HEAD~1 |
Changes are deleted |
| Need to fix the commit message or add a file | git commit --amend |
Previous commit is replaced |
| Need to undo a pushed commit safely | git revert HEAD |
New commit undoes the changes |
Recovering from Mistakes
Even if you use git reset --hard and realize you should not have, there is often a way to recover. Git keeps a log of where HEAD has pointed, called the reflog:
git reflog
This shows a list of recent HEAD positions. You can find the commit you thought you lost and reset back to it:
git reset --hard HEAD@{2}
The reflog is your safety net. Entries are kept for 90 days by default, so you have a generous window to recover from mistakes.
Undoing Commits in GitSquid
GitSquid simplifies the most common undo operations. You can press Cmd+Z (or Ctrl+Z on Windows and Linux) immediately after committing to undo the last commit, keeping your changes staged and ready to rework. For more specific operations, right-clicking any commit in the graph gives you access to reset and revert options through a context menu, so you can target exactly the commit you want to undo without memorizing flags.
Undoing a Git commit does not have to be stressful. Understand the difference between reset, amend, and revert, know which one fits your situation, and you can fix mistakes confidently. The most important distinction is simple: if it is local, reset or amend. If it is pushed, revert.