unfuck-my-git-state
unfuck-my-git-state
Recover a repo without making the blast radius worse.
Core Rules
- Snapshot first. Do not "just try stuff."
- Prefer non-destructive fixes before force operations.
- Treat
.git/as production data until backup is taken. - Use
git symbolic-refbefore manually editing.git/HEAD. - After each fix, run verification before proceeding.
Fast Workflow
- Capture diagnostics:
bash scripts/snapshot_git_state.sh .
- Route by symptom using
references/symptom-map.md. - Generate non-destructive command plan:
bash scripts/guided_repair_plan.sh --repo .
- Apply the smallest matching playbook.
- Run
references/recovery-checklist.mdverification gate. - Escalate only if the gate fails.
For explicit routing:
bash scripts/guided_repair_plan.sh --list
bash scripts/guided_repair_plan.sh --symptom phantom-branch-lock
Regression Harness
Use disposable simulation tests before changing script logic:
bash scripts/regression_harness.sh
Run one scenario:
bash scripts/regression_harness.sh --scenario orphaned-worktree
Playbook A: Orphaned Worktree Metadata
Symptoms:
git worktree listshows a path that no longer exists.- Worktree entries include invalid or zero hashes.
Steps:
git worktree list --porcelain
git worktree prune -v
git worktree list --porcelain
If stale entries remain, back up .git/ and remove the specific stale folder under .git/worktrees/<name>, then rerun prune.
Playbook B: Phantom Branch Lock
Symptoms:
git branch -dorgit branch -Dfails with "already used by worktree".git worktree listseems to disagree with branch ownership.
Steps:
git worktree list --porcelain
Find the worktree using that branch, switch that worktree to another branch or detach HEAD there, then retry the branch operation in the main repo.
Playbook C: Detached or Contradictory HEAD
Symptoms:
git statussays detached HEAD unexpectedly.git branch --show-currentandgit symbolic-ref -q HEADdisagree.
Steps:
git symbolic-ref -q HEAD || true
git reflog --date=iso -n 20
git switch <known-good-branch>
If branch context is unknown, create a rescue branch from current commit:
git switch -c rescue/$(date +%Y%m%d-%H%M%S)
Then reconnect to the intended branch after investigation.
Playbook D: Missing or Broken Refs
Symptoms:
unknown revision,not a valid object name, orcannot lock ref.
Steps:
git fetch --all --prune
git show-ref --verify refs/remotes/origin/<branch>
git branch -f <branch> origin/<branch>
git switch <branch>
Use reflog to recover local-only commits before forcing branch pointers.
Last Resort: Manual HEAD Repair
Only after backup of .git/.
Preferred:
git show-ref --verify refs/heads/<branch>
git symbolic-ref HEAD refs/heads/<branch>
Fallback when symbolic-ref cannot be used:
echo "ref: refs/heads/<branch>" > .git/HEAD
Immediately run the verification gate.
Verification Gate (Must Pass)
Run checks in references/recovery-checklist.md. Minimum bar:
git statusexits cleanly with no fatal errors.git symbolic-ref -q HEADmatches intended branch.git worktree list --porcelainhas no missing paths and no zero hashes.git fsck --no-reflogs --fullhas no new critical errors.
Escalation Path
- Archive
.git:
tar -czf git-metadata-backup-$(date +%Y%m%d-%H%M%S).tar.gz .git
- Clone fresh from remote.
- Recover unpushed work with reflog and cherry-pick from old clone.
- Document failure mode and add guardrails to automation.
Automation Hooks
When building worktree tooling (iMi, scripts, bots), enforce:
- preflight snapshot and state validation
- post-operation verification gate
- hard stop on HEAD/ref inconsistency
- explicit user confirmation before destructive commands
Resources
- Symptom router:
references/symptom-map.md - Verification checklist:
references/recovery-checklist.md - Diagnostic snapshot script:
scripts/snapshot_git_state.sh - Guided plan generator:
scripts/guided_repair_plan.sh - Disposable regression harness:
scripts/regression_harness.sh