Git Reset vs Revert: Safe Undoing Techniques

The Fundamental Difference

Understanding when to use git reset versus git revert is crucial for safe Git operations. The key distinction lies in how they handle history:

Simple Rule: Use reset for local, unpublished changes. Use revert for shared, published history. Reset erases history, revert creates new history.

Git Reset

Purpose: Erase commits from history

Effect: Rewrites history (dangerous for shared branches)

Use when: Working locally, need to undo un-pushed changes

Analogy: Going back in time and pretending mistakes never happened

Git Revert

Purpose: Create a new commit that undoes previous changes

Effect: Adds new history (safe for shared branches)

Use when: Working on shared branches, need to undo published commits

Analogy: Acknowledging a mistake and fixing it publicly

Git Reset: The Three Modes

git reset has three modes that determine what gets affected: --soft, --mixed (default), and --hard.

🟢

--soft (Safe)

Only moves HEAD pointer. Changes staged as if ready to commit.

git reset --soft HEAD~1
🟡

--mixed (Default)

Moves HEAD and unstages changes. Work preserved in working directory.

git reset HEAD~1
🔴

--hard (Dangerous)

Moves HEAD, unstages, and discards all changes. Irreversible!

git reset --hard HEAD~1

Reset Mode Comparison

Mode HEAD Moves Staging Area Working Directory Risk Level
--soft ✅ Yes ✅ Changes staged ✅ Preserved Low
--mixed ✅ Yes ❌ Changes unstaged ✅ Preserved Medium
--hard ✅ Yes ❌ Cleared ❌ Discarded High

Critical Warning: Never use git reset --hard on commits that have been pushed to a shared repository unless you're absolutely certain no one else has based work on those commits.

Practical Reset Examples

Scenario 1: Undo Last Commit (Keep Changes)

You committed but forgot to include a file:

# Current state: committed too early
git log --oneline -3
# a1b2c3d Add login feature
# b2c3d4e Fix typo
# c3d4e5f Initial commit

# Undo commit but keep changes staged
git reset --soft HEAD~1

# Add missing file and recommit
git add forgot-file.js
git commit -m "Add login feature with validation"

Scenario 2: Unstage Files

You staged files but want to review before committing:

# Accidentally staged wrong files
git status
# Changes to be committed:
# modified: file1.js
# modified: file2.js

# Unstage all files
git reset

# Or unstage specific file
git reset file1.js

# Review changes, stage selectively
git add -p file1.js

Scenario 3: Complete Rollback (Danger!)

Everything went wrong and you want to start over:

# WARNING: This destroys all uncommitted work!
# Check what will be lost
git status

# Return to last commit, discard everything
git reset --hard HEAD

# Or go back 3 commits
git reset --hard HEAD~3

# Verify clean state
git status
# Should show: "nothing to commit, working tree clean"

Recovery Note: If you accidentally reset with --hard, you might recover lost work using git reflog and git cherry-pick, but there's no guarantee.

Git Revert: Safe Undoing

git revert creates a new commit that inverses the changes from a previous commit. It's safe for shared history.

a1b2c3d Initial project setup
b2c3d4e Add user authentication
c3d4e5f Add broken feature X ← TO BE REVERTED
d4e5f6g Revert "Add broken feature X" ← REVERT COMMIT

Revert Examples

Scenario 1: Revert Single Commit

A bug was introduced in a specific commit:

# Find the problematic commit
git log --oneline -10
# c3d4e5f Add feature that broke login
# b2c3d4e Add user authentication
# a1b2c3d Initial commit

# Revert the specific commit
git revert c3d4e5f

# Git opens editor for revert message
# Save and close to create revert commit

Scenario 2: Revert with Custom Message

Provide a clear reason for the revert:

git revert c3d4e5f -m "Reverting feature X due to production bug"

# Or skip editor entirely
git revert c3d4e5f --no-edit

Scenario 3: Revert Multiple Commits

Undo a range of commits:

# Revert commits in reverse order (newest to oldest)
git revert HEAD~3..HEAD

# Or specify range
git revert a1b2c3d..c3d4e5f

# Git creates separate revert commit for each
# You'll need to resolve conflicts for each commit

Scenario 4: Revert a Revert

Sometimes you need to restore a reverted feature:

# Original: Added feature X
git log --oneline --grep="feature X"
# c3d4e5f Add feature X
# d4e5f6g Revert "Add feature X"

# Now we want feature X back
git revert d4e5f6g

# This creates: "Revert 'Revert \"Add feature X\"'"
# Effectively restoring the original feature

Decision Flowchart: Reset or Revert?

Do you need to undo changes?
Yes
No

Real-World Scenarios

Team Collaboration

Situation: You pushed a buggy commit that others have pulled.

Solution: Use git revert to create a fix commit. Never reset pushed commits.

git revert bad-commit-hash
git push origin main

Local Development

Situation: You made several experimental commits locally that didn't work out.

Solution: Use git reset to clean up before pushing.

git reset --soft HEAD~3
# Recommit cleanly
git commit -m "Clean feature implementation"

Emergency Rollback

Situation: Production deployment failed, need immediate rollback.

Solution: Use git revert for each bad commit, or revert the merge commit.

# Revert the entire deployment
git revert -m 1 merge-commit-hash
git push origin main

Command Reference

Reset Commands

# Undo last commit, keep changes staged
git reset --soft HEAD~1

# Undo last commit, keep changes unstaged (default)
git reset HEAD~1
git reset --mixed HEAD~1

# Undo last commit, discard all changes (DANGER)
git reset --hard HEAD~1

# Unstage specific file
git reset filename.js

# Reset to specific commit
git reset --hard a1b2c3d

Revert Commands

# Revert specific commit
git revert a1b2c3d

# Revert with custom message
git revert a1b2c3d -m "Fix production issue"

# Revert without opening editor
git revert a1b2c3d --no-edit

# Revert multiple commits (range)
git revert HEAD~3..HEAD

# Revert but don't commit (just apply changes)
git revert -n a1b2c3d

Recovery Commands

# See recent actions (including resets)
git reflog

# Recover lost commit from reflog
git cherry-pick abc1234

# Find dangling commits
git fsck --lost-found

# Create branch from recovered state
git branch recovery abc1234

Advanced Topics

Reverting Merge Commits

Reverting merges requires special care due to multiple parent commits:

# Identify merge commit
git log --oneline --merges -5

# Revert merge (specify mainline parent)
git revert -m 1 merge-commit-hash

# -m 1 means: treat first parent as main branch
# -m 2 means: treat second parent as feature branch

# View parents of a commit
git show --pretty=raw merge-commit-hash

Interactive Rebase for Cleanup

Combine reset with interactive rebase for history cleanup:

# Soft reset to 3 commits ago
git reset --soft HEAD~3

# Now recommit with interactive staging
git add -p
git commit -m "Combined clean commit"

# Or use interactive rebase directly
git rebase -i HEAD~5

Using ORIG_HEAD for Safety

Git stores previous HEAD in ORIG_HEAD after dangerous operations:

# After reset, ORIG_HEAD points to previous state
git reset --hard HEAD~2

# Changed mind? Go back!
git reset --hard ORIG_HEAD

# ORIG_HEAD is overwritten by each reset
# For multiple backups, use reflog

Frequently Asked Questions

What happens if I reset a commit that others have already pulled?

This creates a dangerous situation! If others have based work on the commits you reset:

  1. Their local history will diverge from the remote
  2. They'll get "non-fast-forward" errors when pushing
  3. Their future merges will reintroduce your "deleted" commits
  4. Team coordination becomes difficult and error-prone

Solution if this happens:

  1. Immediately communicate with affected team members
  2. If possible, revert your reset: git reset --hard ORIG_HEAD
  3. If not, everyone needs to align:
    # For team members to fix their repos:
    git fetch origin
    git reset --hard origin/main
  4. Establish team rules about reset vs revert

Prevention: Never reset commits that have been shared. Use revert instead.

Can I recover work after git reset --hard?

Maybe, but act quickly! Git doesn't immediately delete commits. Recovery options:

  1. git reflog - Shows recent HEAD movements:
    git reflog
    # Look for commits before reset
    # abc1234 HEAD@{2}: commit: My important work
    # def5678 HEAD@{1}: reset: moving to HEAD~1

    git reset --hard HEAD@{2}
  2. git fsck - Finds dangling objects:
    git fsck --lost-found
    # dangling commit abc1234...
    git show abc1234
    git cherry-pick abc1234
  3. IDE/Editor recovery - Some editors auto-save
  4. File system recovery tools - Last resort

Prevention is better:

  • Avoid --hard unless absolutely necessary
  • Commit frequently with meaningful messages
  • Push to remote regularly for backup
  • Use git stash for temporary work
What's the difference between git checkout, git reset, and git revert?

These three commands serve different purposes:

Command Purpose Affects History Safe for Shared
git checkout Switch branches or restore files No (moves HEAD only) ✅ Yes
git reset Move HEAD and optionally modify index/working dir Yes (rewrites) ❌ No (if pushed)
git revert Create new commit that undoes changes Yes (adds new) ✅ Yes

Specific examples:

# checkout: restore file from commit
git checkout abc1234 -- file.js

# reset: unstage file
git reset file.js

# revert: undo commit publicly
git revert abc1234

Modern Git (2.23+): git switch and git restore replace some checkout uses for clarity.

How do I undo a git revert?

Since revert creates a new commit, you can undo it in several ways:

  1. Revert the revert commit:
    # Original: Commit A added feature
    # Revert: Commit B reverted feature
    git log --oneline -3
    # c3d4e5f Revert "Add feature X" ← Commit B
    # b2c3d4e Add feature X ← Commit A

    git revert c3d4e5f
    # Creates: "Revert 'Revert \"Add feature X\"'"
  2. Reset if not pushed:
    git reset --hard HEAD~1
    # Only if revert wasn't pushed!
  3. Cherry-pick original commits:
    git cherry-pick b2c3d4e
    # Re-applies the original feature commit

Best practice: If you realize immediately after reverting that it was a mistake, and you haven't pushed, use reset. Otherwise, revert the revert commit for safety.

What should my team's policy be for reset vs revert?

Establish clear team rules to prevent conflicts:

  • Golden Rule: Never rewrite shared history (main, develop, release branches)
  • Reset allowed for:
    • Local feature branches not yet pushed
    • Cleaning up commits before PR review
    • Personal experimentation branches
  • Revert required for:
    • Any commit on main/develop branches
    • Hotfixes to production
    • Any commit that others might have pulled
  • Communication:
    • Announce before force-pushing to shared branches
    • Document reverts in commit messages and team chat
    • Coordinate during critical fixes
  • Tool configuration:
    # Protect main branch from force pushes
    # In repository settings:
    # - Require pull request reviews
    # - Require status checks
    # - Prevent force pushes to main

Training: Ensure all team members understand these rules and the consequences of breaking them.

Previous: Git Stash Techniques Next: Git Cherry-picking