Committed too early or to wrong branch? Here’s how to undo commits without losing your work, depending on whether you’ve pushed or not.
Scenario 1: Undo Last Commit (Not Pushed Yet)
# Keep changes in working directory (most common) git reset --soft HEAD~1 # Now your changes are uncommitted, ready to re-commit # Use case: Fix commit message or add forgotten files # Example workflow: git reset --soft HEAD~1 # Add forgotten file git add forgotten-file.js # Re-commit with better message git commit -m "Better commit message"
Scenario 2: Undo Commit and Unstage Files
# Keep changes but unstage them git reset --mixed HEAD~1 # Or just: git reset HEAD~1 # --mixed is default # Files back to "Changes not staged" # Use case: Want to reorganize which files go in which commit
Scenario 3: Completely Discard Last Commit
# ⚠️ DANGER: Loses all changes permanently git reset --hard HEAD~1 # Use case: Commit was completely wrong, start over # Make sure you REALLY want to delete the work!
Understanding the Difference:
# HEAD~1 means "one commit before HEAD" # HEAD~2 = two commits back # HEAD~5 = five commits back # --soft: Move HEAD, keep staging area, keep working directory # --mixed: Move HEAD, clear staging area, keep working directory # --hard: Move HEAD, clear staging area, clear working directory # Visual: # Before: A -- B -- C (HEAD) # After reset --soft HEAD~1: # A -- B (HEAD) # Changes from C are staged # After reset --mixed HEAD~1: # A -- B (HEAD) # Changes from C are unstaged # After reset --hard HEAD~1: # A -- B (HEAD) # Changes from C are GONE
If You Already Pushed:
# ❌ Don't use reset if commit is pushed (rewrites history) # ✅ Use revert instead (creates new commit that undoes changes) git revert HEAD # This creates a new commit that reverses the last commit # Safe for shared branches because history isn't changed # Example: # Before: A -- B -- C (bad commit) # After revert: # A -- B -- C -- C' (C' undoes C) # Everyone can pull safely
Undo Multiple Commits:
# Undo last 3 commits, keep changes git reset --soft HEAD~3 # Undo commits back to specific commit git reset --soft abc1234 # See what commits would be undone git log --oneline -5 # Pick the commit hash you want to reset TO
Committed to Wrong Branch:
# Oh no! Committed to main instead of feature branch # 1. Create new branch at current commit git branch feature-branch # 2. Reset main to before your commits git reset --hard origin/main # 3. Switch to new branch git checkout feature-branch # Now your commits are on feature-branch, main is clean!
Recover After Accidental Hard Reset:
# You did: git reset --hard HEAD~5
# And lost commits you wanted!
# Find lost commits in reflog
git reflog
# Output shows:
# abc1234 HEAD@{0}: reset: moving to HEAD~5
# def5678 HEAD@{1}: commit: Important work ← This is your lost commit!
# Restore it
git checkout def5678
# Or create branch from it
git branch recovered-work def5678
# Or reset back to it
git reset --hard def5678
Interactive Rebase – Advanced Undo:
# Edit/delete/reorder last 3 commits git rebase -i HEAD~3 # Editor opens: pick abc1234 Commit message 1 pick def5678 Commit message 2 pick ghi9012 Commit message 3 # Options: # pick = keep commit # reword = change commit message # edit = amend commit # squash = combine with previous commit # drop = delete commit # Example - delete middle commit: pick abc1234 Commit message 1 drop def5678 Commit message 2 ← Delete this pick ghi9012 Commit message 3 # Save and close
Amend Last Commit (Different Use Case):
# Just want to modify last commit (message or files) git commit --amend # Add forgotten file to last commit: git add forgotten-file.js git commit --amend --no-edit # Keep same message # Change commit message only: git commit --amend -m "New message"
Safety Best Practices:
# Before any destructive operation: # 1. Check current state git status git log --oneline -10 # 2. Create backup branch git branch backup-$(date +%Y%m%d-%H%M%S) # 3. Now safe to experiment git reset --hard HEAD~3 # 4. If something goes wrong: git checkout backup-2024-02-03-143022 # Reflog is your safety net (keeps 90 days of history) git reflog # See all HEAD movements
