Undoing Changes
Quick Summary
Git provides ways to undo changes at every level — from discarding uncommitted edits to rewriting entire commit histories. The right tool depends on what you want to undo and whether the changes have been shared.
Undo Decision Flowchart
flowchart TD
A["What do you want to undo?"] --> B{Pushed to remote?}
B -->|No| C{Committed?}
B -->|Yes| D["git revert<br/>(safe, creates new commit)"]
C -->|No| E{Staged?}
C -->|Yes, last commit| F["git commit --amend<br/>or git reset"]
C -->|Yes, older commit| G["git rebase -i"]
E -->|No| H["git checkout -- file<br/>or git restore file"]
E -->|Yes| I["git restore --staged file<br/>or git reset HEAD file"]
By Severity Level
Level 1: Discard Unstaged Changes (Working Directory)
# Discard changes to a specific file
git restore <file>
git checkout -- <file> # Legacy equivalent
# Discard ALL unstaged changes
git restore .
git checkout -- . # Legacy
This is irreversible
Discarding unstaged changes permanently deletes your modifications. There's no undo for this undo.
Level 2: Unstage Files (Keep Changes)
# Unstage a specific file (keep modifications in working directory)
git restore --staged <file>
git reset HEAD <file> # Legacy equivalent
# Unstage everything
git restore --staged .
git reset HEAD # Legacy
Level 3: Amend the Last Commit
# Fix the commit message
git commit --amend -m "Corrected message"
# Add forgotten files to the last commit
git add forgotten-file.js
git commit --amend --no-edit
# Change author
git commit --amend --author="Name <email>" --no-edit
Level 4: Undo Commits (Local Only)
# Undo last commit, keep changes staged
git reset --soft HEAD~1
# Undo last commit, keep changes unstaged
git reset --mixed HEAD~1 # (default)
git reset HEAD~1
# Undo last commit, DELETE all changes
git reset --hard HEAD~1
| Reset Mode | Commit | Staging | Working Directory |
|---|---|---|---|
--soft | ❌ Undone | ✅ Preserved | ✅ Preserved |
--mixed (default) | ❌ Undone | ❌ Unstaged | ✅ Preserved |
--hard | ❌ Undone | ❌ Cleared | ❌ Deleted |
Level 5: Undo Pushed Commits (Safe)
# Create a NEW commit that reverses the changes of a specific commit
git revert <commit-hash>
# Revert the last commit
git revert HEAD
# Revert without auto-committing
git revert --no-commit <hash>
# Revert a merge commit (specify parent)
git revert -m 1 <merge-commit-hash>
Revert vs Reset
revert creates a new commit that undoes changes — safe for shared branches.
reset rewrites history — only safe for local, unpushed commits.
Common Undo Scenarios
| Scenario | Command |
|---|---|
| Accidentally edited a file | git restore <file> |
| Staged wrong files | git restore --staged <file> |
| Commit message typo | git commit --amend -m "..." |
| Forgot to include a file | git add file && git commit --amend --no-edit |
| Want to redo last commit differently | git reset --soft HEAD~1 |
| Need to undo a pushed commit | git revert <hash> |
| Accidentally deleted a branch | git reflog + git branch recover <hash> |
| Bad merge | git revert -m 1 <merge-hash> |
Best Practices
- Use
git restore(Git 2.23+) instead of the overloadedgit checkoutfor discarding changes - Use
revertfor shared history — neverreset --hardon pushed commits - Check
git statusbefore dangerous operations — know what you're undoing - Keep
reflogin mind — your last resort for recovering "lost" commits
What's Next
- Recovery Techniques — Recover deleted branches, lost commits, and corrupted repos
- Cheatsheet — Quick reference for all Git commands