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.
--mixed (Default)
Moves HEAD and unstages changes. Work preserved in working directory.
--hard (Dangerous)
Moves HEAD, unstages, and discards all changes. Irreversible!
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:
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:
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:
# 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.
Revert Examples
Scenario 1: Revert Single Commit
A bug was introduced in a specific 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:
# Or skip editor entirely
git revert c3d4e5f --no-edit
Scenario 3: Revert Multiple Commits
Undo a range of commits:
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:
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?
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 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.
# 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.
git revert -m 1 merge-commit-hash
git push origin main
Command Reference
Reset Commands
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
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
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:
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:
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:
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
This creates a dangerous situation! If others have based work on the commits you reset:
- Their local history will diverge from the remote
- They'll get "non-fast-forward" errors when pushing
- Their future merges will reintroduce your "deleted" commits
- Team coordination becomes difficult and error-prone
Solution if this happens:
- Immediately communicate with affected team members
- If possible, revert your reset:
git reset --hard ORIG_HEAD - If not, everyone needs to align:
# For team members to fix their repos:
git fetch origin
git reset --hard origin/main - Establish team rules about reset vs revert
Prevention: Never reset commits that have been shared. Use revert instead.
Maybe, but act quickly! Git doesn't immediately delete commits. Recovery options:
- 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} - git fsck - Finds dangling objects:
git fsck --lost-found
# dangling commit abc1234...
git show abc1234
git cherry-pick abc1234 - IDE/Editor recovery - Some editors auto-save
- File system recovery tools - Last resort
Prevention is better:
- Avoid
--hardunless absolutely necessary - Commit frequently with meaningful messages
- Push to remote regularly for backup
- Use
git stashfor temporary work
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:
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.
Since revert creates a new commit, you can undo it in several ways:
- 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\"'" - Reset if not pushed:
git reset --hard HEAD~1
# Only if revert wasn't pushed! - 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.
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.