Git from First PrinciplesPost 14 of 16 · 16 of 16 written

14. Debugging mindset and recovery playbook

Git · 14 of 163 min readOct 2026

When Git confuses you, stop typing commands and answer four questions. Where is HEAD? Where do the relevant refs point? What is in the index and the working tree? Is an operation in progress? Almost every situation becomes clear from those four answers.

The diagnostic kit

git status                        # branch, any operation in progress, the three areas
git lg                            # the graph with every ref (log --oneline --graph --decorate --all)
git branch -vv                    # upstreams, ahead and behind
git reflog -n 20                  # HEAD's recent moves
git stash list
git remote -v                     # where fetch and push go
git config --list --show-origin   # a surprising setting, and its source

If status says you are in the middle of a merge, rebase, cherry-pick or bisect, that operation has --continue and --abort (rebase also has --skip). Finishing or aborting it is usually step one.

Asking the right question

Translate the symptom into model terms before you search or ask. “My commit disappeared” becomes “my branch no longer reaches commit X”. The model-level phrasing finds better answers and often answers itself.

When you ask a person or an AI for help, give them what they need to diagnose:

I ran:        git rebase main
I expected:   my 3 commits on top of main
Instead:      <paste the exact output>
git status:   <paste>
git log --oneline --graph --all -15:  <paste>

Git’s own messages often include hint: lines with the exact next command. Read them before searching.

The zero-cost safety habit

Branches are free, so before anything risky - an interactive rebase, a reset, a history rewrite - drop a label:

git branch backup/before-rebase

If it goes wrong, git reset --hard backup/before-rebase puts everything back. Delete the label when you are happy.

Recovery playbook

Situation What happened, in model terms Fix
Committed to main instead of a branch, not pushed main moved forward by mistake git branch feat → git reset --hard origin/main → git switch feat
Want to undo the last commit but keep its changes The branch should move back one git reset --soft HEAD~1
Commits “lost” after a reset or rebase The ref moved; commits are unreachable but still in the reflog git reflog → git branch rescue <sha>
Committed in detached HEAD, then switched away No ref ever pointed at those commits Find them in git reflog → git branch rescue <sha>
Deleted a branch by mistake Only the label is gone The delete message or git reflog gives the hash → git branch <name> <sha>
Dropped a stash The stash commit is unreachable git fsck --unreachable | grep commit → git stash apply <sha>
Push rejected as non-fast-forward The remote has commits you lack git fetch → rebase onto or merge origin/<branch> → push
“Your branch and origin have diverged” Both sides have commits the other lacks Decide: rebase your commits onto origin/<branch>, or merge it
A merge or rebase went badly halfway An operation is in progress git merge --abort or git rebase --abort
Wrong message on a commit already shared Rewriting would break others Leave it; if it matters, add a follow-up commit or a note
Committed a large file, not pushed Your commits reach a huge blob Last commit: git reset --soft HEAD~1, unstage it, ignore or LFS it, recommit. Older: git filter-repo --path <file> --invert-paths
Committed a secret The blob is in history, possibly public Rotate the key first, then git filter-repo, force-push, and have collaborators re-clone
Files show as modified but you changed nothing Line endings or file-mode bits differ Inspect with git diff; fix with .gitattributes or core.fileMode false
.gitignore does not ignore a file The file is already tracked git rm --cached <file>, then commit
Conflict in a lockfile A text merge of a generated file Take one side (git restore --theirs uv.lock), then regenerate it (uv lock) and git add it

When to stop and think

Stop if you are about to force-push to a shared branch, delete .git, or re-clone “to fix it”. Re-cloning throws away your local branches, stashes and reflog - the very things that make recovery possible. Diagnose first with the kit above.

Check yourself

  • A branch “disappeared”. Which two commands tell you what happened?
  • What is the zero-cost step before an interactive rebase?
  • You committed to main by mistake and have not pushed. Write the three commands that move the work to a branch.