git reset vs git revert

Revert adds a commit that undoes another; reset moves the branch back and rewrites history. One rule decides it, plus the three reset modes and how to recover.

Reviewed 2026-09-16

git revert adds a new commit that undoes an earlier one. History is preserved; everyone who has the branch can pull normally. git reset moves the branch pointer backwards, dropping commits from the branch. History is rewritten; anyone who already has those commits will be out of sync.

git revert

  • Creates a commit with the inverse change.
  • Safe on shared branches (main, release).
  • Can undo any commit, not just the latest.
  • Leaves a record that the change happened and was undone.

git reset

  • Moves the branch to an older commit.
  • For local, unpushed work only.
  • Can keep the changes (--soft, mixed) or discard them (--hard).
  • Makes it look as if the commits never existed.

The rule

Pushed and shared? Revert. Local and unpushed? Reset. The moment a commit is on a branch other people fetch, rewriting it forces every collaborator to repair their clone, and a force-push can silently discard their work. A revert costs one extra commit and nothing else.

What each command does to the three areas

Git keeps changes in three places: the working tree (files), the index (staged changes) and the branch (commits). The reset modes differ in which of them they touch.

CommandBranch pointerIndex (staged)Working treeUse when
git reset --soft HEAD~1Moves backKeeps the commit’s changes stagedUntouchedRedo the last commit with a different message or split it.
git reset HEAD~1 (mixed)Moves backUnstagedUntouchedUn-commit and re-stage selectively.
git reset --hard HEAD~1Moves backDiscardedDiscardedThrow the commit and all local changes away. Irreversible for uncommitted work.
git revert HEADMoves forward (new commit)Clean after commitClean after commitUndo a commit that has been pushed.

Reverting

git revert <commit>              # one commit; opens an editor for the message
git revert --no-edit <commit>    # keep the default message
git revert HEAD~3..HEAD          # revert the last three commits, newest first, one revert each
git revert -m 1 <merge-commit>   # revert a merge, keeping side 1 (the branch you merged into)

Reverting a commit that others have built on can produce conflicts, handled like any merge conflict: fix, git add, git revert --continue. Reverting a merge commit is special: it undoes the merged changes but Git remembers the merge, so merging the same branch again later brings nothing new. To re-apply, revert the revert.

Resetting

git reset --soft HEAD~1          # undo commit, keep changes staged
git reset HEAD~1                 # undo commit, keep changes unstaged
git reset --hard HEAD~1          # undo commit, discard changes
git reset --hard origin/main     # match the remote exactly, discarding local commits and edits
git reset <file>                 # unstage one file (same as git restore --staged <file>)

git reset --hard is the only command here that destroys uncommitted work with no undo. Before running it, git stash anything you might want, or at least run git status.

Recovering from a reset

Commits that were reset away are not deleted immediately. git reflog lists every position HEAD has had; find the entry from before the reset and go back:

$ git reflog
a1b2c3d HEAD@{0}: reset: moving to HEAD~2
e4f5a6b HEAD@{1}: commit: Add search filters
c7d8e9f HEAD@{2}: commit: Fix pagination
$ git reset --hard HEAD@{1}      # or: git branch rescue e4f5a6b

Reflog entries expire after 90 days by default. Uncommitted changes discarded by --hard are not in the reflog and cannot be recovered this way.

Scenarios

SituationDo this
Bad commit on main, already pushedgit revert <sha>, push. Open a fix in a new branch.
Last commit on my feature branch has a typo in the messagegit commit --amend (a reset plus re-commit). Fine if not pushed, or if only you use the branch and you force-push with --force-with-lease.
I want to squash my last five local commitsgit reset --soft HEAD~5 then one git commit. Or git rebase -i HEAD~5.
I committed to main instead of a branch, not pushedgit branch feature to save the commit, then git reset --hard origin/main.
A merged pull request broke productiongit revert -m 1 <merge-sha> on main, deploy, then fix on the feature branch and revert the revert when merging again.
Local branch is a mess, I want the remote versiongit fetch then git reset --hard origin/<branch>.
  • git restore <file> discards uncommitted changes to a file. git restore --staged <file> unstages it. These replace the old git checkout -- <file> and git reset <file> forms.
  • git checkout <sha> looks at an old commit without moving any branch (detached HEAD). It does not undo anything.
  • git rebase -i rewrites a range of commits (reorder, squash, drop). Same sharing rule as reset: local branches only.

The full command list is in the Git commands reference.

navigateEnter openEsc close