Getting out of trouble
A lookup table for "I am in a bad state and do not know how I got here", plus the three commands that recover almost anything.
When you are stuck, the answer is usually one of three commands. Start here.
The first three things to run
git status # what state am I in, and what does Git suggest?
git log --oneline --graph --all -20 # what does the graph look like?
git reflog # where has HEAD been?
git status in particular is underrated: for almost every unusual state, it names the commands
that get you out. Read it rather than skimming it.
“I do not know what state I am in”
git status
| What it says | What it means | Way out |
|---|---|---|
rebase in progress |
A rebase stopped on a conflict | git rebase --continue or --abort |
You have unmerged paths |
A merge conflict is open | Resolve, git add, git merge --continue |
detached HEAD |
HEAD points at a commit, not a branch | git switch - or git switch -c name |
Unmerged paths after cherry-pick |
Conflict during cherry-pick | git cherry-pick --continue or --abort |
nothing to commit, working tree clean |
Nothing is wrong | The problem is elsewhere |
“I lost commits”
git reflog
Every movement of HEAD is here, with a hash. Find the entry from before things went wrong:
git reset --hard HEAD@{5}
Or, more cautiously, look first:
git show HEAD@{5}
git switch -c recovery HEAD@{5}
If the commits were never on your HEAD — a branch someone deleted, say — look for dangling objects:
git fsck --lost-found
“My branch and the remote have diverged”
Your branch and 'origin/main' have diverged,
and have 2 and 3 different commits each, respectively.
Decide what you want:
# Keep both sets, with a merge commit
git pull --no-rebase
# Replay your commits on top of theirs — linear
git pull --rebase
# Throw away your local commits entirely
git reset --hard origin/main
“A file keeps showing as modified and I have not touched it”
Almost always line endings or file mode.
git diff path/to/file # if the diff looks empty, it is whitespace
# Line endings — apply the .gitattributes from chapter two, then:
git rm --cached -r .
git reset --hard
# File mode (chmod) noise:
git config core.fileMode false
“Someone deleted the branch I needed”
If you have it locally:
git switch -c recovered origin/deleted-branch # if the ref is still cached
git reflog # otherwise find the hash here
If only the remote had it, GitHub keeps deleted branches for a while — check the closed pull request, which still shows the commits and lets you restore the branch.
“The repository is enormous”
git count-objects -vH
Find the biggest blobs:
git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
awk '/^blob/ {print substr($0, 6)}' |
sort -k2 -n -r | head -20
Then remove them with git filter-repo, and remember that every collaborator must re-clone
afterwards.
“I need to undo something that is already pushed”
git revert <hash>
This creates a new commit that reverses the old one. History stays intact, nobody else’s clone breaks, and the change is undone. For a merge commit:
git revert -m 1 <merge-hash>
-m 1 means “keep the first parent’s side”, which is almost always what you want.
“Which commit broke this?”
git bisect start
git bisect bad
git bisect good <known-good-tag>
# test, then `git bisect good` or `git bisect bad`, until Git names it
git bisect reset
Automate it if you have a test:
git bisect start HEAD v1.2.0
git bisect run npm test
Git finds the exact commit unattended.
The mental checklist
When something looks wrong, in order:
git status— what does Git think is happening?git log --graph --all— what does the history actually look like?git reflog— where have I been?--abort— if mid-operation, get back to a known state- Branch before experimenting —
git switch -c attemptcosts nothing
You have finished the guide
You understand the object model, you can branch and merge deliberately rather than hopefully, you know which commands rewrite history and which do not, and you can recover from the situations that previously meant re-cloning.
If your next step is running that code somewhere, the Docker guide starts the same way this one did — with the problem, before the commands.