Undo the mistake with the least-destructive tool that fits, after establishing two facts that change everything: has the work been committed, and has it been pushed/shared. Get those wrong and you either lose work or rewrite history other people depend on.
Hard rules: never break these
- Diagnose before acting. Establish: committed or not? pushed or not? on a shared branch? Run
git status, git log --oneline -10, and git reflog -20 first. The fix depends entirely on the answers.
- Never rewrite published history casually. For anything already pushed to a shared branch, undo forward with
git revert; do not reset/rebase/commit --amend and force-push unless the user explicitly owns the consequences and coordinates with collaborators.
- Take a safety net before any destructive op. Before
reset --hard, a force-push, or a history rewrite, snapshot the current tip: git branch backup/<desc> or git tag backup/<desc>. Reflog usually saves you, but a branch is free insurance.
--hard and clean destroy uncommitted work irrecoverably. Reflog only recovers committed states. Spell out exactly what will be lost and confirm before running them.
- Confirm before any destructive or shared-history command:
reset --hard, clean -fd, push --force*, history rewrites. Prefer push --force-with-lease over --force when a rewrite is genuinely required.
Step 1: Diagnose
git status: staged vs unstaged vs untracked; any operation in progress.
git log --oneline --graph -15: recent commits and branch shape.
git reflog -25: the time machine, every position HEAD has held, even "lost" ones. This is how you recover almost anything committed.
- Determine shared or local: has the affected commit/branch been pushed?
git status (ahead/behind), git branch -r --contains <sha>.
Step 2: Pick the fix
Match the situation to the safest tool:
Uncommitted work
- Discard changes in a file →
git restore <file> (was git checkout -- <file>).
- Unstage but keep changes →
git restore --staged <file> (was git reset HEAD <file>).
- Set aside without losing →
git stash push -m "<note>"; bring back with git stash pop.
- Remove untracked files →
git clean -nd (dry run first!), then git clean -fd once confirmed.
The last commit (not yet pushed)
- Wrong message →
git commit --amend.
- Forgot a file → stage it, then
git commit --amend --no-edit.
- Undo the commit, keep changes staged →
git reset --soft HEAD~1.
- Undo the commit, keep changes unstaged →
git reset --mixed HEAD~1 (default).
- Undo the commit and discard changes →
git reset --hard HEAD~1 (destructive; safety net first).
Already pushed / shared branch
- Undo a commit safely →
git revert <sha> (creates an inverse commit; history stays intact). Revert a merge with -m 1.
Wrong-branch / structural mistakes
- Committed to the wrong branch → create the right branch from here (
git branch feature), then git reset --hard origin/<wrong-branch> on the original (safety net first); or git cherry-pick the commits onto the right branch and drop them from the wrong one.
- Need an old version of one file →
git restore --source=<sha> -- <file>.
Recovering things that look lost
- Hard reset / branch deletion / bad amend → find the old SHA in
git reflog, then git branch recovered <sha> or git reset --hard <sha>.
- Botched rebase/merge →
git reset --hard ORIG_HEAD returns to the pre-operation tip.
- Deleted branch with no reflog entry →
git fsck --lost-found lists dangling commits.
- Dropped a stash →
git fsck --no-reflog | grep commit, or recover from git reflog stash.
Step 3: Safety net (before anything destructive)
Snapshot the current tip so the operation is reversible regardless of outcome:
git branch backup/before-undo # or: git tag backup/before-undo
Note the current HEAD SHA too. This costs nothing and turns "irreversible" into "recoverable."
Step 4: Execute (with confirmation)
- State plainly: what command runs, what it changes, and what (if anything) is lost forever.
- For destructive or shared-history operations, get an explicit yes. For force-pushes, use
--force-with-lease and confirm no one else's work is being overwritten.
- Run it.
Step 5: Verify
git status and git log --oneline -5 to confirm the tree and history are what you intended.
- Confirm recovered work is present (file contents, expected commits).
- Note the safety branch/tag you created; remove it once the user confirms the recovery is good.
1---2name: git-undo3description: Recover safely from Git mistakes such as discard, unstage, amend, reset, revert, restore lost commits via reflog, recover deleted branches, and fix bad rebases. Chooses the least-destructive fix and protects against data loss. Use when something in Git went wrong and needs undoing.4---56Undo the mistake with the *least-destructive* tool that fits, after establishing two facts that change everything: has the work been **committed**, and has it been **pushed/shared**. Get those wrong and you either lose work or rewrite history other people depend on.78## Hard rules: never break these910- **Diagnose before acting.** Establish: committed or not? pushed or not? on a shared branch? Run `git status`, `git log --oneline -10`, and `git reflog -20` first. The fix depends entirely on the answers.11- **Never rewrite published history casually.** For anything already pushed to a shared branch, undo *forward* with `git revert`; do not `reset`/`rebase`/`commit --amend` and force-push unless the user explicitly owns the consequences and coordinates with collaborators.12- **Take a safety net before any destructive op.** Before `reset --hard`, a force-push, or a history rewrite, snapshot the current tip: `git branch backup/<desc>` or `git tag backup/<desc>`. Reflog usually saves you, but a branch is free insurance.13- **`--hard` and `clean` destroy uncommitted work irrecoverably.** Reflog only recovers *committed* states. Spell out exactly what will be lost and confirm before running them.14- **Confirm before any destructive or shared-history command**: `reset --hard`, `clean -fd`, `push --force*`, history rewrites. Prefer `push --force-with-lease` over `--force` when a rewrite is genuinely required.1516## Step 1: Diagnose1718- `git status`: staged vs unstaged vs untracked; any operation in progress.19- `git log --oneline --graph -15`: recent commits and branch shape.20- `git reflog -25`: the time machine, every position `HEAD` has held, even "lost" ones. This is how you recover almost anything committed.21- Determine **shared or local**: has the affected commit/branch been pushed? `git status` (ahead/behind), `git branch -r --contains <sha>`.2223## Step 2: Pick the fix2425Match the situation to the safest tool:2627**Uncommitted work**28- Discard changes in a file → `git restore <file>` (was `git checkout -- <file>`).29- Unstage but keep changes → `git restore --staged <file>` (was `git reset HEAD <file>`).30- Set aside without losing → `git stash push -m "<note>"`; bring back with `git stash pop`.31- Remove untracked files → `git clean -nd` (dry run first!), then `git clean -fd` once confirmed.3233**The last commit (not yet pushed)**34- Wrong message → `git commit --amend`.35- Forgot a file → stage it, then `git commit --amend --no-edit`.36- Undo the commit, keep changes staged → `git reset --soft HEAD~1`.37- Undo the commit, keep changes unstaged → `git reset --mixed HEAD~1` (default).38- Undo the commit and discard changes → `git reset --hard HEAD~1` (destructive; safety net first).3940**Already pushed / shared branch**41- Undo a commit safely → `git revert <sha>` (creates an inverse commit; history stays intact). Revert a merge with `-m 1`.4243**Wrong-branch / structural mistakes**44- Committed to the wrong branch → create the right branch from here (`git branch feature`), then `git reset --hard origin/<wrong-branch>` on the original (safety net first); or `git cherry-pick` the commits onto the right branch and drop them from the wrong one.45- Need an old version of one file → `git restore --source=<sha> -- <file>`.4647**Recovering things that look lost**48- Hard reset / branch deletion / bad amend → find the old SHA in `git reflog`, then `git branch recovered <sha>` or `git reset --hard <sha>`.49- Botched rebase/merge → `git reset --hard ORIG_HEAD` returns to the pre-operation tip.50- Deleted branch with no reflog entry → `git fsck --lost-found` lists dangling commits.51- Dropped a stash → `git fsck --no-reflog | grep commit`, or recover from `git reflog stash`.5253## Step 3: Safety net (before anything destructive)5455Snapshot the current tip so the operation is reversible regardless of outcome:5657```58git branch backup/before-undo # or: git tag backup/before-undo59```6061Note the current `HEAD` SHA too. This costs nothing and turns "irreversible" into "recoverable."6263## Step 4: Execute (with confirmation)64651. State plainly: what command runs, what it changes, and **what (if anything) is lost forever**.662. For destructive or shared-history operations, get an explicit yes. For force-pushes, use `--force-with-lease` and confirm no one else's work is being overwritten.673. Run it.6869## Step 5: Verify7071- `git status` and `git log --oneline -5` to confirm the tree and history are what you intended.72- Confirm recovered work is present (file contents, expected commits).73- Note the safety branch/tag you created; remove it once the user confirms the recovery is good.