Oh Shit, Git?! — Rescue Recipes in Plain English
Git is hard: screwing up is easy, and figuring out how to fix your mistakes can feel impossible.
The docs have a chicken-and-egg problem—you cannot search for how to get out of a mess unless you already know the name of the thing you need. So you stare at git help and close the tab.
This post is the opposite: bad situations in plain English, then the commands that fixed them for me.
Inspired by the legendary Oh Shit, Git!?! (hat tip to Katie Sylor-Miller and everyone who contributed). I adapted and organized the flows I actually use on real repos.

Oh shit, I did something terribly wrong — does Git have a time machine?
Yes. It is called reflog.
git reflog # You will see everything you've done in Git, across all branches. # Each entry has an index: HEAD@{index} # Find the one from BEFORE you broke everything.
git reset HEAD@{index} # Magic time machine
Use this to recover deleted work, undo a bad merge attempt, or go back to when things actually worked. I use reflog constantly.
Oh shit, I committed and immediately need one small change
You committed, ran tests, and realized you forgot a space. Again.
# Make your change, then: git add . # or: git add path/to/file git commit --amend --no-edit # Your last commit now includes that change
Faster than an interactive rebase for a one-line fix.
Oh shit, I need to change the message on my last commit
git commit --amend # Follow the prompts to change the message
Stupidly strict commit message rules? Fixed in thirty seconds.
Oh shit, I committed to main but it should have been a new branch
# Create a branch from current state (keeps your commit) git branch some-new-branch-name # Remove the last commit from main git reset HEAD~ --hard # Switch to the branch where the commit now lives git checkout some-new-branch-name
Your work is on some-new-branch-name. main is clean again.
Note: If you already pushed to a shared main, this gets painful—you may need git reset HEAD@{n} from reflog instead of HEAD~. If you tried other fixes first, the index number changes. Reflog is your friend.
Oh shit, I committed to the wrong branch
Option A: reset, stash, move
git reset HEAD~ --soft git stash git checkout name-of-the-correct-branch git stash pop git add . git commit -m "Your message here"
Option B: cherry-pick
git checkout name-of-the-correct-branch git cherry-pick wrong-branch-name git checkout wrong-branch-name git reset HEAD~ --hard
Pick whichever matches how your brain works. I use stash more; cherry-pick is cleaner when the commit is already perfect.
Oh shit, I ran git diff and nothing showed up
You changed files, but git diff is empty because you already staged everything.
git diff --staged
Yes, it is a feature. No, it is not obvious the first time. File under ¯\_(ツ)_/¯.
Oh shit, I need to undo a commit from five commits ago
You do not have to manually copy old file contents. Revert creates a new commit that undoes an old one.
git log # Arrow keys to scroll; copy the hash of the commit to undo git revert <saved-hash> # Git opens an editor for the revert message — save and exit
Safe for shared branches because it does not rewrite history—it adds a new “undo” commit on top.
Oh shit, I need to undo changes to one file
git log # Find a commit from BEFORE the file broke; copy its hash git checkout <saved-hash> -- path/to/file # Old version is now staged git commit -m "Restore file to known-good version"
When I finally learned this, it felt huge. On what planet is checkout -- the intuitive “undo file” command? Still: it works.
Oh shit, I give up — nuclear option
cd .. sudo rm -r fucking-git-repo-dir git clone https://some.github.url/fucking-git-repo-dir.git cd fucking-git-repo-dir
Thanks to Eric V. for the joke. (All complaints about sudo in a Git blog post can go to him.)
The actually-approved nuclear reset
If your branch is so broken you need to match the remote exactly—destructive, unrecoverable locally:
git fetch origin git checkout main git reset --hard origin/main # Remove untracked files and directories git clean -d --force # Repeat checkout / reset / clean for each broken branch
Quick reference
| Situation | Command to remember |
|---|---|
| “Go back in time” | git reflog → git reset HEAD@{n} |
| “Fix last commit” | git commit --amend |
| “Undo old commit safely” | git revert <hash> |
| “Restore one file” | git checkout <hash> -- path/to/file |
| “Diff staged stuff” | git diff --staged |
| “Wrong branch” | reset --soft + stash or cherry-pick |
Disclaimer
This is not exhaustive Git documentation. There are purer, more theoretical ways to do almost everything here—I arrived at these through trial and error, swearing, and table-flipping.
Take what helps. When in doubt, reflog first, ask second, reset --hard last.
If you want the long-form “build Git from scratch” mental model, check out Build Your Own Git From Scratch. If you want collaboration workflows, see Git & GitHub from Zero to Collaborating.