Git Recovery Cheat Sheet

Undoing the commit, the push, the rebase and the reset — including how to recover work that looks permanently gone.

Git command reference

Before anything destructive

git status
git stash list
git log --oneline -5

Then take a free escape hatch. A tag costs nothing and makes any mistake below reversible:

git tag backup/before-$(date +%Y%m%d-%H%M%S)

Undo the last commit

git reset --soft HEAD~1    # undo commit, keep changes staged
git reset HEAD~1           # undo commit, keep changes unstaged
git reset --hard HEAD~1    # undo commit AND discard changes — destructive

--soft and mixed are always safe. --hard throws away uncommitted work with no prompt — the one flag worth pausing on.

Amend

git commit --amend                    # change message or add staged files
git commit --amend --no-edit          # keep message, absorb staged changes
git commit --amend --author="Name <email>"

Amending rewrites the commit. If it was already pushed, you now need a force push — see below.

Unstage and discard

git restore --staged FILE     # unstage, keep edits
git restore FILE              # discard edits — destructive
git restore .                 # discard everything unstaged — destructive

git clean -n                  # DRY RUN: what would be deleted
git clean -fd                 # delete untracked files and dirs

Always run git clean -n first. It is the only preview you get.

Recovering “lost” commits

Almost nothing is truly lost. Git keeps every commit you had checked out in the reflog for 90 days by default — including after a bad reset, rebase or deleted branch.

git reflog                            # everything HEAD has pointed at
git reflog show BRANCH                # per-branch history

git reset --hard HEAD@{3}             # jump back to a reflog entry
git branch recovered HEAD@{3}         # safer: recover onto a new branch

Deleted a branch by mistake:

git reflog | grep BRANCHNAME
git branch BRANCHNAME COMMIT_SHA

Commits with no ref at all:

git fsck --lost-found

Force pushing safely

git push --force-with-lease origin BRANCH   # refuses if remote moved
git push --force origin BRANCH              # overwrites unconditionally

Always prefer --force-with-lease. Plain --force will silently destroy a colleague’s commits if they pushed while you were rewriting. --force-with-lease aborts instead.

Rewriting history changes commit SHAs. Anyone else with the branch must git fetch && git reset --hard origin/BRANCH, not git pull.

Rebase

git rebase main
git rebase --continue        # after resolving conflicts
git rebase --skip
git rebase --abort           # back to where you started — always safe

If a rebase goes wrong and you have already continued past the point of repair:

git reflog
git reset --hard HEAD@{N}    # N = the entry before the rebase began

Reverting a pushed commit

git revert COMMIT            # new commit undoing it — safe on shared branches
git revert -m 1 MERGE_SHA    # revert a merge, -m 1 keeps the first parent

revert adds history rather than rewriting it, which is why it is the correct choice for anything already pushed to a shared branch.

Finding what broke

git log --oneline --graph --all -20
git log -S "search string"            # commits that added/removed a string
git log -p FILE                       # full history of one file
git blame FILE
git blame -L 10,20 FILE               # just those lines

git bisect start
git bisect bad                        # current commit is broken
git bisect good v1.2.0                # this one worked
# test, then: git bisect good | git bisect bad
git bisect reset

git log -S is the fastest way to find when a specific line of config or code appeared — far better than scrolling git log.

Stash

git stash push -m "message"
git stash push -u              # include untracked files
git stash list
git stash show -p stash@{0}    # view the diff
git stash pop                  # apply and remove
git stash apply stash@{1}      # apply and keep
git stash drop stash@{0}

git stash pop on a conflict leaves the stash in place — you have not lost it, despite the error message.

Fixing the wrong branch

Committed to main when you meant a feature branch:

git branch feature/work        # branch here, keeping the commits
git reset --hard origin/main   # move main back — destructive
git checkout feature/work

Fixing author identity

# Just the last commit
git commit --amend --author="Name <email>" --no-edit

# Across a range — rewrites SHAs, needs a force push
git rebase -i HEAD~5 --exec 'git commit --amend --author="Name <email>" --no-edit'

Remotes

git remote -v
git remote set-url origin URL
git fetch --all --prune        # drop refs for branches deleted upstream
git branch -vv                 # local branches and their upstreams