This work was originally created for my book Learn Git in a Day - The Visual Guide.
Join us

TL;DR:
Every developer breaks git. The difference between a junior and a senior is not avoiding the mess, it's knowing the two commands that get you out of it. This post is a field guide: the problem you're facing, the command that fixes it, and just enough context to use it without making things worse. At the end is a cheat sheet for quick reference.
This work was originally created for my book Learn Git in a Day - The Visual Guide.
Undo uncommitted edits: You changed a file, hated the result, and want the last committed version back:
FILE="src/app.py"
git restore "$FILE"
This is destructive. Your edits are gone, no undo. If you might want them later, stash instead.
Unstage a file: You ran git add . and swept in a file that doesn't belong in this commit:
FILE="debug.log"
git restore --staged "$FILE"
The file stays modified in your working directory. Only the staging is undone.
Pause your work: Someone pings you with an urgent bug and your current work is half-finished:
git stash
# fix the bug, commit, push
git stash pop
Use git stash -u if you have untracked files, since plain stash ignores them. And if pop says nothing came back, check git stash list, then git stash apply stash@{n} to recover a specific entry.
Clear out untracked files: Build artifacts, editor backups, and experiment files pile up. git clean deletes them, and it does not go through the trash:
git clean -n # preview what would be deleted
git clean -fd # delete untracked files and directories
Always run -n first. Add -x only if you also want to remove ignored files (build caches, node_modules), and expect a slow reinstall afterward.
Git tracks a file that's in .gitignore: Adding a file to .gitignore does nothing if git already tracks it. You have to remove it from the index:
FILE="config/local.env"
git rm --cached "$FILE"
git commit -m "stop tracking $FILE"
The file stays on disk. It just leaves git's index.
These fixes rewrite the most recent commit. Safe before pushing, dangerous after.
Fix a commit message typo:
git commit --amend -m "add rate limiting to the API gateway"
Add a forgotten file:
FILE="tests/test_gateway.py"
git add "$FILE"
git commit --amend --no-edit
Undo the commit but keep the work: You committed too early, or bundled unrelated changes:
git reset --soft HEAD~1
Your changes come back staged. Recommit them properly, maybe as two commits this time.
Nuke the commit and the work:
git reset --hard HEAD~1
Both the commit and the changes disappear from your working directory. The commit itself survives in the reflog, so even this is recoverable (see below) - but once it's unreachable, git's default is to keep it for only about 30 days, so don't sit on it.
Undo a pushed commit: Once a commit is on a shared branch, do not rewrite it. Revert it instead:
git revert HEAD
This creates a new commit that inverts the bad one. History stays intact, teammates stay unbroken.
Clean up your commits before pushing: You have five commits named "wip", "fix", "fix again", "typo", and "actually works". Interactive rebase lets you squash, reorder, reword, and drop them before anyone sees the mess:
N=5
git rebase -i HEAD~"$N"
Git opens an editor listing the commits. Change pick to squash (or s) to fold a commit into the one above it, reword (or r) to edit its message, drop to delete it. Save, close, done. Same rule as amend: only do this to commits you haven't pushed.
Committed on the wrong branch: You were "on main" in your head but on feature/x in reality:
SHA=$(git rev-parse HEAD)
git switch main
git cherry-pick "$SHA"
git switch feature/x
git reset --hard HEAD~1
Copy the commit to where it belongs, then remove it from where it doesn't.
Detached HEAD: You checked out a tag or a raw SHA and git is warning you about a detached HEAD. Nothing is broken. If you made commits you want to keep, put a branch on them:
git switch -c rescue-branch
If you made nothing worth keeping:
git switch main
Cherry-pick a single commit: You need one fix from another branch without merging the whole thing:
SHA="a1b2c3d"
git cherry-pick "$SHA"
Your branch is behind main: Before you've pushed, rebase for a clean linear history:
git fetch origin
git rebase origin/main
After you've pushed and others may have pulled, merge instead:
git merge origin/main
The rule: rebase private history, merge shared history.
Abort a rebase: Conflicts piled up and you want out:
git rebase --abort
You're back exactly where you started. Same escape hatch exists for merges (git merge --abort) and cherry-picks (git cherry-pick --abort).
Force push safely: You rebased or amended a pushed branch and now git rejects your push. Never use bare --force on a branch someone else touches:
git push --force-with-lease
--force-with-lease refuses the push if the remote moved since you last fetched, which means you can't silently erase a teammate's commits. Bare --force can.
A pull made a mess: You pulled, got an ugly merge or unexpected conflicts, and want to pretend it never happened:
git reset --hard ORIG_HEAD
ORIG_HEAD points to where your branch was before the last drastic operation (pull, merge, rebase).
The reflog is your time machine: Git keeps a local log of everywhere HEAD has been. Reachable history sticks around for about 90 days, but a commit that becomes unreachable - a deleted branch tip, a hard-reset-away commit, a botched rebase - is kept for only about 30 days by default before garbage collection can reclaim it. So almost nothing is truly lost, as long as you recover it within the month.
Deleted a branch:
git reflog
# find the SHA of the branch tip, e.g. a1b2c3d
BRANCH="feature/payments"
SHA="a1b2c3d"
git branch "$BRANCH" "$SHA"
Hard-reset past commits you needed:
git reflog
SHA="a1b2c3d"
git reset --hard "$SHA"
Lost a commit and not sure how (a botched rebase, an amend, whatever): inspect it before deciding what to do with it:
git reflog
SHA="a1b2c3d"
git checkout "$SHA" # look around, run the tests
git switch -c recovered "$SHA" # keep it on a branch
Undo a merge commit: Reverting a merge needs the -m 1 flag to tell git which parent is the mainline:
SHA="d4e5f6a"
git revert -m 1 "$SHA"
Warning: after reverting a merge, re-merging the same branch later brings nothing in, because git considers those commits already merged. You'd have to revert the revert first.
You committed a secret: Two steps, in this order:
FILE="config/secrets.env"
git filter-repo --path "$FILE" --invert-paths
Everyone on the team must re-clone afterward, since all SHAs change.
Find the commit that broke things: git bisect binary-searches your history. On a 1000-commit range, it finds the culprit in about 10 steps:
GOOD_SHA="a1b2c3d"
git bisect start
git bisect bad
git bisect good "$GOOD_SHA"
# test each checkout, then mark it:
git bisect good # or: git bisect bad
# when done:
git bisect reset
If you have a test script, automate the whole thing:
git bisect run ./test.sh
Find who changed a line and why:
FILE="src/gateway.py"
git blame "$FILE"
SHA="a1b2c3d" # a SHA from the blame output
git show "$SHA"
blame gives you the SHA per line, show gives you the full commit with its message. The message is usually where the "why" lives, assuming your team writes real commit messages.
Rename a branch:
NEW_NAME="feature/rate-limiting"
git branch -m "$NEW_NAME"
# if already pushed:
git push origin -u "$NEW_NAME"
git push origin --delete <old-name>
Fix line-ending chaos: Mixed Windows/Linux teams see phantom diffs where every line changed. Set this once per machine:
# Linux / macOS
git config --global core.autocrlf input
# Windows
git config --global core.autocrlf true
Better yet, commit a .gitattributes file so the repo enforces it for everyone:
* text=auto
Uncommitted work is the only thing git can truly lose: restore, reset --hard, and checkout destroy uncommitted changes with no undo. Commit or stash before anything risky.
Committed work is almost never lost: The reflog keeps it even after resets and deleted branches - roughly 30 days once it's unreachable, so recover it within the month.
Private history is yours to rewrite, shared history is not: Amend and rebase freely before pushing. After pushing, revert instead, and if you must force push, use --force-with-lease.
| Problem | Fix |
|---|---|
| I want to undo my uncommitted edits | git restore <file> |
| I staged a file by mistake | git restore --staged <file> |
| I committed something wrong | git revert HEAD |
| I need to pause my work | git stash → do other work → git stash pop |
| I made a typo in my last commit message | git commit --amend -m "fixed message" |
| I forgot to add a file to my last commit | git add <file> → git commit --amend --no-edit |
| My branch is behind main | git fetch then git rebase origin/main (before pushing) or git merge origin/main (after pushing) |
| I want to abandon a rebase | git rebase --abort |
| I'm in detached HEAD | git switch main |
| I want to undo my last commit but keep the changes | git reset --soft HEAD~1 |
| I want to nuke my last commit entirely | git reset --hard HEAD~1 |
| I committed on the wrong branch | git switch <right-branch> → git cherry-pick <sha> → clean up the wrong branch |
| I deleted a branch and I need it back | git reflog → git branch <name> <sha> |
| I hard-reset and lost commits | git reflog → git reset --hard <sha> |
| A merge went sideways | git merge --abort |
| I pushed and then rewrote history | git push --force-with-lease |
| Git keeps tracking a file that's in .gitignore | git rm --cached <file> → commit |
| I committed a secret | Rotate the secret first, then rewrite history with git filter-repo |
| I need one commit from another branch | git cherry-pick <sha> |
| I need to find which commit broke things | git bisect start → git bisect bad → git bisect good <sha> |
| Who wrote this line and why | git blame <file> → git show <sha> |
| My branch name is wrong | git branch -m <new-name> |
| I want to undo a merge commit | git revert -m 1 <merge-sha> |
| My pull created a mess | git reset --hard ORIG_HEAD |
| I stashed something and lost it | git stash list → git stash apply stash@{n} |
| I think I lost a commit | git reflog → git checkout <sha> |
| I have messy untracked files to clear out | git clean -n (preview) → git clean -fd |
| I want to clean up my commits before pushing | git rebase -i HEAD~<n> |
| Line endings are driving me crazy | git config core.autocrlf (input on Linux/macOS, true on Windows) |
Find the most updated version of this guide at eon01/GitCheatSheet.
Don't forget to checkout my book Learn Git in a Day - The Visual Guide for a more in-depth, visual approach to learning git.
Join other developers and claim your FAUN.dev() account now!
FAUN.dev() is a developer-first platform built with a simple goal: help engineers stay sharp withou…

Founder, FAUN.dev
@eon01Influence
Total Hits
Posts

Build Real GitOps Pipelines From Empty Clusters to Automated Deploys
> Get Your Copy