Recover from wrong files staged, broken code committed, and bad merge decisions.
Claude commits fast. That's mostly good — but occasionally a commit lands with the wrong files, broken code, or a message that makes the history unreadable. Recovery is fast if you know the right tool. The wrong tool can make things worse.
⬡ What you'll build
Type 1: Wrong files staged — Commit contains files that shouldn't have been included (.env, unrelated changes, debug files). The code in scope is correct, the commit just has extra stuff.
Type 2: Broken code committed — The commit itself is logically wrong: TypeScript errors, runtime failures, half-implemented feature. The code doesn't work.
Type 3: Bad commit message — Code is correct, commit is on the right branch, but the message is useless ("fix stuff", "wip", "asdfghjkl").
Each type has a different recovery path.
This is the most important decision in commit recovery:
Is the bad commit pushed to a remote branch?
├── YES → Always use git revert (safe, non-destructive)
└── NO → Local only?
├── Only you use this branch → git reset is safe
└── Others may have pulled it → use git revert
git revert creates a new commit that undoes the bad one. History is preserved. Safe for shared branches.
git reset moves the branch pointer backward. History is rewritten. Only safe for local, unshared commits.
# Undo the last commit, keep the changes staged
git reset --soft HEAD~1
# Now unstage the files that shouldn't be there
git restore --staged .env
git restore --staged config/local.json
# Re-commit without the bad files
git add lib/auth.ts app/api/auth/route.ts
git commit -m "feat(auth): add JWT validation to API routes"--soft keeps your changes staged. --mixed (the default) unstages them. --hard discards them entirely — be careful.
# Create a new commit that removes the unwanted file
git rm --cached .env
git commit -m "chore: remove accidentally committed .env file"
# Also: rotate any secrets that were exposed
# The file is gone from HEAD but still in history — assume it's compromised✕If .env was pushed to a public repo
Treat all exposed credentials as compromised immediately. Rotate API keys, change passwords, revoke tokens — before removing the file. Removing the file from git history requires git filter-repo or a force push, which rewrites history for all contributors.
# Option A: Undo the commit entirely, keep changes to fix
git reset --mixed HEAD~1
# Now fix the code, then re-commit
# Option B: If multiple commits are broken
git reset --mixed HEAD~3 # goes back 3 commits
# Fix all the issues, recommit cleanly
# Option C: Nuclear — discard everything back to last good state
git reset --hard HEAD~1 # DESTROYS the changes — no recovery# Safe revert — creates a new "undo" commit
git revert HEAD
git push
# If multiple commits need reverting (e.g., last 3 commits)
git revert HEAD~2..HEAD
git pushgit revert is non-destructive. The bad commit stays in history, but a new commit undoes its changes. This is the only safe option once code is pushed.
# Amend the last commit's message
git commit --amend -m "feat(auth): replace cookie session with JWT token validation"
# Interactive rebase to fix older commits (last 5)
git rebase -i HEAD~5
# In the editor: change 'pick' to 'reword' on the bad commits
# Then type the new message when promptedNever amend or rebase commits that have been pushed to a shared branch. It rewrites history, causing divergence for anyone who pulled those commits.
If you ran git reset --hard and lost work you needed:
# git reflog shows every HEAD movement — including resets
git reflog
# Output:
# a3f2c1b HEAD@{0}: reset: moving to HEAD~2
# 7b91d4a HEAD@{1}: commit: feat(auth): add OAuth support ← the lost commit
# 2d4e8f3 HEAD@{2}: commit: fix(dashboard): resolve filter bug
# Restore to the state before the reset
git checkout 7b91d4a
# Or create a branch at that point
git checkout -b recovery/lost-auth-work 7b91d4agit reflog is the safety net beneath git reset. It records every state change for 90 days (default). Nothing is truly lost until the reflog expires.
Most bad commits are preventable. Add these to your CLAUDE.md critical rules:
## Critical rules — git
- NEVER run git add . or git add -A — stage specific files only
- ALWAYS run tsc --noEmit before committing any TypeScript changes
- NEVER commit with a one-word message ("fix", "update", "wip")
- NEVER use git reset --hard without confirming with me first
- NEVER push --force to main or master
- ALWAYS show me the staged diff (git diff --staged) before committingMake this a non-negotiable ritual before every Claude-assisted commit:
# 1. Review what's about to be committed
git diff --staged
# 2. Check nothing unexpected is staged
git status
# 3. Verify TypeScript
node node_modules/typescript/bin/tsc --noEmit
# 4. Only then: commit
git commit -m "type(scope): description"This 60-second gate prevents 90% of bad commits before they happen.
You're ready for production git work when:
git reflog documentation
Complete reference for git reflog — the recovery tool beneath git reset.