Skip to main content

git reset & git revert โ€” Undoing Changes

The Core Differenceโ€‹

CommandWhat it doesRewrites history?Safe for shared branches?
git resetMoves the branch pointer backward, discarding or unstaging commitsโœ… YesโŒ No
git revertCreates a new commit that undoes the changes of a previous commitโŒ Noโœ… Yes

Rule of thumb:

  • Use git reset on local, unpushed changes
  • Use git revert on pushed or shared history

git resetโ€‹

git reset moves the current branch's HEAD pointer to a specified commit, with three modes controlling what happens to the commits being "removed":

--soft โ€” Keep changes stagedโ€‹

git reset --soft HEAD~1
  • Moves HEAD back one commit
  • Changes from the "removed" commit are placed in the index (staged)
  • Working tree is untouched
  • Use case: Undo a commit but keep all changes ready to recommit (e.g., to rewrite the message or split the commit)
Before: A -- B -- C (HEAD)
After: A -- B (HEAD) index contains C's changes (staged)

--mixed โ€” Keep changes unstaged (default)โ€‹

git reset HEAD~1
git reset --mixed HEAD~1
  • Moves HEAD back one commit
  • Changes from the "removed" commit are placed in the working tree (unstaged)
  • Use case: Undo a commit and unstage everything โ€” start the staging process fresh

--hard โ€” Discard all changesโ€‹

git reset --hard HEAD~1
  • Moves HEAD back one commit
  • All changes from the "removed" commits are permanently discarded
  • Working tree and index are both reset to the target commit's state
  • Use case: Completely discard bad commits and all associated changes
git reset --hard permanently destroys uncommitted changes. The changes are gone โ€” not in the trash, not recoverable via git reflog (the commits are recoverable, but any unstaged changes in the working tree are lost forever). Double-check before running.

Reset to a specific commitโ€‹

# Reset to a specific SHA
git reset --soft a3f9bc2

# Reset to origin/main (discard all local commits since last push)
git reset --hard origin/main

# Reset a single file to the last committed state (does not move HEAD)
git restore src/main/java/com/example/TransactionService.java
git checkout HEAD -- src/main/java/com/example/TransactionService.java

git revertโ€‹

git revert creates a new commit that inverts the changes of a specified commit. The original commit is preserved in history โ€” only a new "undo" commit is added. This is safe for shared branches.

Revert the last commitโ€‹

git revert HEAD
# Opens editor for the revert commit message, then commits

Revert a specific commitโ€‹

git revert a3f9bc2

Revert without auto-committing (inspect first)โ€‹

git revert --no-commit a3f9bc2
# Changes are staged โ€” review them, then:
git revert --continue
# or abort:
git revert --abort

Revert a range of commitsโ€‹

# Revert commits from a3f9bc2 (exclusive) to HEAD (inclusive)
git revert a3f9bc2..HEAD

Revert a merge commitโ€‹

Reverting a merge commit requires specifying which parent to treat as the mainline:

# -m 1 = treat the first parent (the branch you merged INTO) as mainline
git revert -m 1 <merge-commit-sha>

Comparison: Reset Modesโ€‹

Commit History: A -- B -- C -- D (HEAD on main)

git reset --soft HEAD~2:
History: A -- B (HEAD)
Index: changes from C + D are staged
Worktree: unchanged

git reset --mixed HEAD~2:
History: A -- B (HEAD)
Index: clean (no staged changes)
Worktree: changes from C + D are present but unstaged

git reset --hard HEAD~2:
History: A -- B (HEAD)
Index: clean
Worktree: clean โ€” C and D's changes are GONE

git revert C, then git revert D:
History: A -- B -- C -- D -- D' -- C' (HEAD)
(D' and C' are new commits that undo D and C respectively)

Recovering from a Mistake with reflogโ€‹

If you ran git reset --hard and lost commits, git reflog can recover them. See Reflog.

git reflog
# HEAD@{0}: reset: moving to HEAD~2
# HEAD@{1}: commit: feat: add export โ† the SHA you want
# HEAD@{2}: commit: feat: add service

git reset --hard HEAD@{1} # recover the lost commit

When to Use Which
  • Just committed but not pushed, want to redo the commit: git reset --soft HEAD~1
  • Committed wrong files, want to unstage and re-stage: git reset HEAD~1 (mixed)
  • Need to completely discard local changes: git reset --hard origin/main
  • Pushed to shared branch, need to undo: git revert <sha> โ€” always

Interview Questionsโ€‹

Q: Why is revert preferred over reset on shared branches?โ€‹

A: Revert preserves immutable history and collaboration safety while still undoing behavior.

Q: What is the operational risk of reset --hard in active repos?โ€‹

A: It can permanently discard local work and cause accidental loss when used without backup/reflog awareness.

Q: How do you undo a bad production deploy safely?โ€‹

A: Revert the specific release commits (or merge commit), validate rollback behavior in CI, and redeploy with monitoring.

Q: When should you use revert --no-commit?โ€‹

A: When batching multiple reversions into one reviewed rollback commit after inspecting cumulative effects.

Q: How does reflog change recovery strategy after mistakes?โ€‹

A: It provides a local movement journal for HEAD, enabling restoration of prior states after destructive operations.

Q: What senior guardrail would you enforce around reset?โ€‹

A: Forbid reset-based history rewrites on protected branches and require revert-based remediation in team workflows.