git-undo · diff
git:20260606.7d504a8 to git:20260608.0cc2133
14 added, 14 removed. Audit A to A.
---
name: git-undo
- description: Recover safely from Git mistakes — 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.
- argument-hint: "(optional) what went wrong — e.g. 'committed to main', 'lost my commits'"
+ description: 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.
+ argument-hint: "(optional) what went wrong, e.g. 'committed to main', 'lost my commits'"
---
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
+ ## 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.
+ - **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.
+ - **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
+ ## 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.
+ - `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
+ ## 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).
+ - 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)
+ ## 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)
+ ## Step 4: Execute (with confirmation)
1. State plainly: what command runs, what it changes, and **what (if anything) is lost forever**.
2. 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.
3. Run it.
- ## Step 5 — Verify
+ ## 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.