How to Safely Undo Local and Remote Commits in Git

Safely undo Git commits with reset, revert, reflog, and force-with-lease without losing work or breaking shared branches.

How to Safely Undo Local and Remote Commits in Git

Undoing a Git commit is easy. Undoing the right thing, without losing work or surprising everyone else on the branch, is the part that takes judgment.

The first question is not "which command do I run?" It is "who has seen this commit?" If the commit exists only on your laptop, you can usually rewrite it with git reset or git commit --amend. If the commit has already been pushed to a branch other people use, prefer git revert. That keeps history intact and creates a new commit that backs out the bad change.

Before you touch history, take a quick snapshot of where you are:

git status
git branch backup-before-undo
git log --oneline --decorate -5

That temporary branch is cheap insurance. If you reset too far or change your mind, the old commit still has a name.

If the commit has not been pushed

For a local commit you have not pushed, git reset is usually the cleanest tool. It moves your branch pointer backward. The mode you choose decides what happens to the files.

Use --soft when the commit message was wrong or you forgot one small file:

git reset --soft HEAD~1

Your last commit disappears, but the changes stay staged. You can add the missing file and commit again:

git add missing-file.yml
git commit -m "Update deployment config"

Use the default mixed reset when you want the changes back in your working tree, unstaged:

git reset HEAD~1

That is the everyday "uncommit this, but keep my edits" command. It is useful when one commit should really become two smaller commits, or when you committed a debugging print statement along with real code.

Use --hard only when you truly want to throw the local changes away:

git reset --hard HEAD~1

This resets tracked files to the earlier commit. It does not politely ask whether you meant it. If you have uncommitted work in tracked files, it can disappear from the working tree. Check git status first, and stash or branch anything you might want back.

For a tiny fix to the most recent local commit, git commit --amend is often better than reset:

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

That replaces the last commit with a new one. The same rule applies: amend freely before pushing; be careful after pushing.

If the commit has already been pushed

On a shared branch, use git revert unless you have a strong reason to rewrite history. Revert makes a new commit that applies the opposite patch.

git revert a1b2c3d

That command opens your editor with a generated message. Save it, and Git creates a new commit. The original commit remains in history, which is exactly what you want on main, master, develop, release branches, and any branch teammates may have pulled.

If you need to revert several consecutive commits, you can do it in one staged batch:

git revert --no-commit HEAD~3..HEAD
git status
git commit -m "Revert recent deployment changes"

Read the range carefully. HEAD~3..HEAD means the last three commits, not including HEAD~3 itself. When in doubt, list the commits first:

git log --oneline HEAD~3..HEAD

Merge commits need one extra decision. A merge has more than one parent, so Git needs to know which side should be treated as the mainline:

git revert -m 1 <merge-commit-sha>

Most teams use -m 1 when reverting a feature branch merge from the target branch, but do not run this blindly. Look at the merge commit with git show --summary <sha> and confirm the parent order.

If you pushed the wrong thing and must remove it

Sometimes revert is not enough. If you pushed a secret, a huge binary, or private customer data, a revert only removes it from the latest tree. The sensitive content still exists in history. That becomes an incident response problem, not just a Git cleanup problem.

For secrets, rotate the credential first. Then remove the data from history with a proper history rewrite tool such as git filter-repo, BFG Repo-Cleaner, or a host-specific secret removal process. Coordinate with the repository owner and assume anyone with access could have fetched the bad commit before you removed it.

For a normal mistaken commit on your own feature branch, rewriting the remote branch can be acceptable. Reset locally, then force push with a lease:

git reset --hard HEAD~1
git push --force-with-lease origin my-feature-branch

--force-with-lease is the safer form because it refuses to update the remote if someone else pushed new work since your last fetch. It is still a force push. It still rewrites the branch. Use it on personal or coordinated feature branches, not casually on shared branches.

A good habit before force pushing is:

git fetch origin
git log --oneline --left-right --graph origin/my-feature-branch...my-feature-branch

That shows what exists only on the remote and what exists only locally. If the left side contains someone else's commits, stop and talk to them.

Recovering with reflog

git reflog is the command that saves many bad afternoons. It records where your local HEAD and branch references have pointed recently. If you reset to the wrong place, deleted a branch, or amended the wrong commit, reflog often knows the old commit SHA.

git reflog

You might see something like:

7cc8a91 HEAD@{0}: reset: moving to HEAD~1
2b41f0d HEAD@{1}: commit: add retry around deploy step

To recover the old commit, create a branch at it:

git branch recovered-deploy-work 2b41f0d

I prefer creating a branch instead of immediately running another hard reset. It gives you a stable handle, and you can inspect the recovered work calmly:

git show recovered-deploy-work
git switch recovered-deploy-work

Reflog is local. Your teammate's reflog will not contain your lost local commits, and remote hosts may not expose the same recovery path. It is also pruned over time according to Git's garbage collection settings, so treat it as a recent safety net, not archival storage.

A practical decision guide

If you committed too early and have not pushed, use git reset --soft HEAD~1 or git reset HEAD~1.

If you committed the wrong file and want the edits gone locally, use git reset --hard HEAD~1 only after checking git status.

If the bad commit is already on a shared branch, use git revert <sha>.

If your feature branch is remote but only you use it, git reset plus git push --force-with-lease is usually acceptable.

If the commit contains a secret or sensitive data, do not rely on revert. Rotate the secret, rewrite history with the right tool, and coordinate cleanup.

The safest Git undo workflow is boring: inspect first, make a backup branch, choose the command based on whether the commit is shared, and use reflog when you need to recover from your own recovery attempt.