Git Error Messages Explained: What Git Is Actually Telling You
Git errors name the state and the next command usually appears verbatim in the output. Read the second half of the message — that's where the instruction is.
Push Rejected: "failed to push some refs"
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:you/repo.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally.The situation: someone (or you, from another machine) pushed commits you don't have. Git refuses to overwrite them.
Fixes by intent: (1) normal case — integrate their work first: git pull --rebase then push; (2) you have nothing local worth keeping: git pull and push; (3) you deliberately want your version to replace theirs (solo branch, force is acceptable): git push --force-with-lease — the --with-lease variant refuses if the remote moved since your last fetch, which is what makes it safe-ish versus plain --force. Never force-push a shared branch (main, release) without team agreement — it deletes other people's commits from history, and they will notice at the worst time.
Diverged Branches: "have diverged"
hint: Your branch and 'origin/main' have diverged,
hint: and have 2 and 3 different commits each, respectively.Both sides have unique commits. Choose a history shape: git pull --rebase (your commits replayed on top — linear history, the usual choice) or git pull (merge commit records the integration). The rebase option can hit conflicts commit-by-commit; the merge option hits them once. Either is correct; the anti-pattern is copy-pasting your files over theirs to make it stop.
Detached HEAD
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make...What happened: you checked out a commit hash, a tag, or a remote branch directly — HEAD points at a commit, not a branch, so new commits belong to no branch.
Fixes: (1) just looking? git switch - returns to your previous branch; (2) made commits you want to keep: git switch -c rescue-branch creates a branch at the current commit — nothing is lost; (3) made commits you don't want: switch away and they become unreachable (recoverable via reflog for ~30 days). The rule: in detached HEAD, create a branch before doing real work.
Merge Conflicts
CONFLICT (content): Merge conflict in src/app.js
Automatic merge failed; fix conflicts and then commit the result.The procedure: git status lists conflicted files (both modified); open each, resolve the markers (<<<<<<<, =======, >>>>>>>), keep the correct content, git add each resolved file, then git commit (or git rebase --continue if rebasing). Escape hatches: git merge --abort restores the pre-merge state — use it without shame when the conflict is bigger than expected; resolve from a clean state instead of mid-panic.
The habit that prevents most conflicts: pull/rebase at the start of work, keep branches short-lived, and never leave a feature branch unmerged for weeks.
"fatal: not a git repository"
You're outside a repo (or in a subdirectory of a repo whose .git was removed/broken). git status in the right directory confirms. The nested-repo variant: running git in a subdirectory that is its own repo, expecting the parent — git rev-parse --show-toplevel tells you which repo actually applies.
Other Frequent Messages
| Message | Meaning | Fix |
|---|---|---|
| Please tell me who you are | user.name/user.email unset | git config --global user.name "X" + email |
| nothing to commit, working tree clean | Not an error — no changes staged | Stage with git add; check git status if you expected changes |
| Changes not staged for commit | Informational | Stage what you intend; review diff before committing |
| fatal: refusing to merge unrelated histories | Two repos with no common ancestor | git pull --allow-unrelated-histories (know why before doing it) |
| error: Your local changes would be overwritten | Checkout/pull would clobber uncommitted work | Commit, stash, or discard deliberately — never blindly |
| fatal: Authentication failed | Bad credentials / expired token | Update PAT or SSH key; check the remote URL scheme |
| ssh: Permission denied (publickey) | SSH key not offered/accepted | ssh -T [email protected] to test; add key to agent |
| index.lock exists | A git process died mid-operation | Confirm no git process runs, then remove .git/index.lock |
The Reflog: Your Safety Net
Almost every "I destroyed my work" story in Git ends with the reflog. git reflog lists every position HEAD has held — find the commit before the mistake and git reset --hard <hash> (or git cherry-pick specific commits from it). This works for: bad rebases, force-pull losses, deleted branches, reset --hard regret. The exceptions where it can't help: uncommitted changes that were never staged (they were never in git at all) — which is the honest argument for committing early, even messily.