unfuck-my-git-state Recover a repo without making the blast radius worse. Core Rules 1. Snapshot first. Do not "just try stuff." 2. Prefer non-destructive fixes before force operations. 3. Treat as production data until backup is taken. 4. Use before manually editing . 5. After each fix, run verification before proceeding. Fast Workflow 1. Capture diagnostics: 2. Route by symptom using . 3. Generate non-destructive command plan: 4. Apply the smallest matching playbook. 5. Run verification gate. 6. Escalate only if the gate fails. For explicit routing: Regression Harness Use disposable simulat…

\"$worktree_file\"; then\n # Unborn branches can legitimately show all-zero HEADs; avoid false positives.\n if [[ -f \"$status_file\" ]] && grep -Eq 'No commits yet|branch\\.oid[[:space:]]+\\(initial\\)' \"$status_file\"; then\n :\n else\n matches+=(\"zero-hash-worktree-entry\")\n fi\n fi\n fi\n\n if [[ -f \"$status_file\" ]] && grep -Eiq 'detached|HEAD detached' \"$status_file\"; then\n matches+=(\"detached-head-state\")\n fi\n\n local cur_branch=\"\"\n local sym_branch=\"\"\n cur_branch=\"$(capture_to_value \"$branch_file\" || true)\"\n sym_branch=\"$(capture_to_value \"$symbolic_file\" || true)\"\n sym_branch=\"${sym_branch#refs/heads/}\"\n if [[ -n \"$cur_branch\" && -n \"$sym_branch\" && \"$cur_branch\" != \"$sym_branch\" ]]; then\n matches+=(\"head-ref-disagreement\")\n fi\n\n if [[ -f \"$status_file\" ]] && grep -Eiq 'unknown revision|not a valid object name|cannot lock ref|fatal:' \"$status_file\"; then\n matches+=(\"missing-or-broken-refs\")\n fi\n if [[ -f \"$show_ref_file\" ]] && grep -Eiq 'not a valid|fatal:' \"$show_ref_file\"; then\n matches+=(\"missing-or-broken-refs\")\n fi\n\n declare -A seen=()\n declare -a unique=()\n local symptom\n for symptom in \"${matches[@]}\"; do\n if [[ -z \"${seen[$symptom]:-}\" ]]; then\n unique+=(\"$symptom\")\n seen[$symptom]=1\n fi\n done\n\n if [[ \"${#unique[@]}\" -eq 0 ]]; then\n echo \"No deterministic symptom match found in snapshot: $snapshot_dir\"\n echo \"Use --symptom with one of:\"\n list_symptoms\n return 0\n fi\n\n echo \"Detected symptom(s) from snapshot: $snapshot_dir\"\n for symptom in \"${unique[@]}\"; do\n echo\n print_plan \"$symptom\"\n done\n}\n\nresolve_snapshot_from_repo() {\n local repo=\"$1\"\n local output\n output=\"$(bash \"$SCRIPT_DIR/snapshot_git_state.sh\" \"$repo\")\"\n printf '%s\\n' \"$output\" >&2\n\n local parsed\n parsed=\"$(printf '%s\\n' \"$output\" | sed -n 's/^Directory: //p' | tail -n 1)\"\n if [[ -n \"$parsed\" && -d \"$parsed\" ]]; then\n printf '%s\\n' \"$parsed\"\n return 0\n fi\n\n local top\n top=\"$(git -C \"$repo\" rev-parse --show-toplevel)\"\n local latest\n latest=\"$(ls -1dt \"$top/.git-state-snapshots\"/* 2>/dev/null | head -n 1 || true)\"\n if [[ -n \"$latest\" && -d \"$latest\" ]]; then\n printf '%s\\n' \"$latest\"\n return 0\n fi\n\n echo \"error: unable to resolve snapshot directory for repo '$repo'\" >&2\n return 1\n}\n\nSYMPTOM=\"\"\nSNAPSHOT=\"\"\nREPO=\"\"\nLIST_ONLY=0\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --symptom)\n [[ $# -ge 2 ]] || { echo \"error: --symptom requires a value\" >&2; usage; exit 1; }\n SYMPTOM=\"$2\"\n shift 2\n ;;\n --snapshot)\n [[ $# -ge 2 ]] || { echo \"error: --snapshot requires a value\" >&2; usage; exit 1; }\n SNAPSHOT=\"$2\"\n shift 2\n ;;\n --repo)\n [[ $# -ge 2 ]] || { echo \"error: --repo requires a value\" >&2; usage; exit 1; }\n REPO=\"$2\"\n shift 2\n ;;\n --list|-l)\n LIST_ONLY=1\n shift\n ;;\n -h|--help)\n usage\n exit 0\n ;;\n *)\n echo \"error: unknown argument '$1'\" >&2\n usage\n exit 1\n ;;\n esac\ndone\n\nif [[ \"$LIST_ONLY\" -eq 1 ]]; then\n list_symptoms\n exit 0\nfi\n\nif [[ -n \"$SYMPTOM\" ]]; then\n print_plan \"$SYMPTOM\"\n exit 0\nfi\n\nif [[ -z \"$SNAPSHOT\" && -z \"$REPO\" ]]; then\n echo \"error: provide --symptom, --snapshot, or --repo\" >&2\n usage\n exit 1\nfi\n\nif [[ -z \"$SNAPSHOT\" && -n \"$REPO\" ]]; then\n SNAPSHOT=\"$(resolve_snapshot_from_repo \"$REPO\")\"\nfi\n\nif [[ ! -d \"$SNAPSHOT\" ]]; then\n echo \"error: snapshot directory not found: $SNAPSHOT\" >&2\n exit 1\nfi\n\nrun_detection \"$SNAPSHOT\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":7410,"content_sha256":"5cd35f13055fc72de4a5552481c57ff620406e8252e959095ebe0ba89b82883f"},{"filename":"scripts/regression_harness.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nGUIDED_SCRIPT=\"$SCRIPT_DIR/guided_repair_plan.sh\"\n\nif [[ ! -x \"$GUIDED_SCRIPT\" ]]; then\n echo \"error: guided script not executable: $GUIDED_SCRIPT\" >&2\n exit 1\nfi\n\nWORK_ROOT=\"$(mktemp -d -t git-state-harness-XXXXXX)\"\nKEEP_TEMP=0\nSINGLE_SCENARIO=\"\"\n\ncleanup() {\n if [[ \"$KEEP_TEMP\" -eq 1 ]]; then\n echo \"Keeping harness workspace: $WORK_ROOT\"\n return 0\n fi\n if [[ -d \"$WORK_ROOT\" ]]; then\n find \"$WORK_ROOT\" -mindepth 1 -depth -delete 2>/dev/null || true\n rmdir \"$WORK_ROOT\" 2>/dev/null || true\n fi\n}\ntrap cleanup EXIT\n\nusage() {\n cat \u003c\u003c'EOF'\nUsage:\n regression_harness.sh\n regression_harness.sh --scenario \u003cname>\n regression_harness.sh --list\n regression_harness.sh --keep-temp\n\nScenarios:\n orphaned-worktree\n detached-head\n zero-hash-worktree\n manual-phantom-branch-lock\nEOF\n}\n\nlist_scenarios() {\n cat \u003c\u003c'EOF'\norphaned-worktree\ndetached-head\nzero-hash-worktree\nmanual-phantom-branch-lock\nEOF\n}\n\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --scenario)\n [[ $# -ge 2 ]] || { echo \"error: --scenario requires a value\" >&2; exit 1; }\n SINGLE_SCENARIO=\"$2\"\n shift 2\n ;;\n --keep-temp)\n KEEP_TEMP=1\n shift\n ;;\n --list)\n list_scenarios\n exit 0\n ;;\n -h|--help)\n usage\n exit 0\n ;;\n *)\n echo \"error: unknown argument '$1'\" >&2\n usage\n exit 1\n ;;\n esac\ndone\n\nmake_repo() {\n local name=\"$1\"\n local repo=\"$WORK_ROOT/$name\"\n mkdir -p \"$repo\"\n git -C \"$repo\" init -q\n git -C \"$repo\" config user.name \"Harness Bot\"\n git -C \"$repo\" config user.email \"[email protected]\"\n echo \"seed\" >\"$repo/seed.txt\"\n git -C \"$repo\" add seed.txt\n git -C \"$repo\" commit -q -m \"seed\"\n printf '%s\\n' \"$repo\"\n}\n\nassert_contains() {\n local haystack=\"$1\"\n local needle=\"$2\"\n if printf '%s\\n' \"$haystack\" | grep -Fq \"$needle\"; then\n return 0\n fi\n return 1\n}\n\nscenario_orphaned_worktree() {\n local repo wt out\n repo=\"$(make_repo orphaned-worktree)\"\n git -C \"$repo\" branch repair-me\n wt=\"$WORK_ROOT/orphaned-worktree-wt\"\n git -C \"$repo\" worktree add -q \"$wt\" repair-me\n\n find \"$wt\" -mindepth 1 -delete\n rmdir \"$wt\"\n\n out=\"$(bash \"$GUIDED_SCRIPT\" --repo \"$repo\")\"\n assert_contains \"$out\" \"[orphaned-worktree-metadata]\" &&\n assert_contains \"$out\" \"git worktree prune -v\"\n}\n\nscenario_detached_head() {\n local repo out\n repo=\"$(make_repo detached-head)\"\n git -C \"$repo\" checkout -q --detach\n out=\"$(bash \"$GUIDED_SCRIPT\" --repo \"$repo\")\"\n assert_contains \"$out\" \"[detached-head-state]\" &&\n assert_contains \"$out\" \"git reflog --date=iso -n 20\"\n}\n\nscenario_zero_hash_worktree() {\n local repo wt meta out\n repo=\"$(make_repo zero-hash-worktree)\"\n git -C \"$repo\" branch zero-head\n wt=\"$WORK_ROOT/zero-hash-worktree-wt\"\n git -C \"$repo\" worktree add -q \"$wt\" zero-head\n\n meta=\"$(find \"$repo/.git/worktrees\" -mindepth 1 -maxdepth 1 -type d | head -n 1)\"\n [[ -n \"$meta\" ]] || return 1\n printf '0000000000000000000000000000000000000000\\n' >\"$meta/HEAD\"\n\n out=\"$(bash \"$GUIDED_SCRIPT\" --repo \"$repo\")\"\n assert_contains \"$out\" \"[zero-hash-worktree-entry]\"\n}\n\nscenario_manual_phantom_branch_lock() {\n local out\n out=\"$(bash \"$GUIDED_SCRIPT\" --symptom phantom-branch-lock)\"\n assert_contains \"$out\" \"[phantom-branch-lock]\" &&\n assert_contains \"$out\" \"git worktree list --porcelain\"\n}\n\nrun_scenario() {\n local name=\"$1\"\n case \"$name\" in\n orphaned-worktree) scenario_orphaned_worktree ;;\n detached-head) scenario_detached_head ;;\n zero-hash-worktree) scenario_zero_hash_worktree ;;\n manual-phantom-branch-lock) scenario_manual_phantom_branch_lock ;;\n *)\n echo \"error: unknown scenario '$name'\" >&2\n return 1\n ;;\n esac\n}\n\nPASS=0\nFAIL=0\ndeclare -a SCENARIOS=(\n \"orphaned-worktree\"\n \"detached-head\"\n \"zero-hash-worktree\"\n \"manual-phantom-branch-lock\"\n)\n\nif [[ -n \"$SINGLE_SCENARIO\" ]]; then\n SCENARIOS=(\"$SINGLE_SCENARIO\")\nfi\n\nfor scenario in \"${SCENARIOS[@]}\"; do\n if run_scenario \"$scenario\"; then\n echo \"PASS $scenario\"\n PASS=$((PASS + 1))\n else\n echo \"FAIL $scenario\"\n FAIL=$((FAIL + 1))\n fi\ndone\n\necho\necho \"Harness result: $PASS passed, $FAIL failed\"\nif [[ \"$FAIL\" -ne 0 ]]; then\n exit 1\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":4264,"content_sha256":"50e835cbd96bb369aa5a12874187994492c888495e3dbb1bcc4a8066dcbc37dd"},{"filename":"scripts/snapshot_git_state.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nTARGET=\"${1:-.}\"\n\nif ! git -C \"$TARGET\" rev-parse --is-inside-work-tree >/dev/null 2>&1; then\n echo \"error: '$TARGET' is not inside a Git work tree\" >&2\n exit 1\nfi\n\nTOPLEVEL=\"$(git -C \"$TARGET\" rev-parse --show-toplevel)\"\nTOPLEVEL=\"$(cd \"$TOPLEVEL\" && pwd)\"\n\nGIT_DIR=\"$(git -C \"$TARGET\" rev-parse --git-dir)\"\nif [[ \"$GIT_DIR\" = /* ]]; then\n GIT_DIR_ABS=\"$GIT_DIR\"\nelse\n GIT_DIR_ABS=\"$TOPLEVEL/$GIT_DIR\"\nfi\n\nSTAMP=\"$(date +%Y%m%d-%H%M%S)\"\nOUT_DIR=\"$TOPLEVEL/.git-state-snapshots/$STAMP\"\nmkdir -p \"$OUT_DIR\"\n\nrun_capture() {\n local name=\"$1\"\n shift\n local out=\"$OUT_DIR/$name.txt\"\n {\n echo \"# $name\"\n echo \"# command: $*\"\n echo\n \"$@\"\n } >\"$out\" 2>&1 || true\n}\n\n{\n echo \"snapshot_time=$STAMP\"\n echo \"target=$TARGET\"\n echo \"toplevel=$TOPLEVEL\"\n echo \"git_dir=$GIT_DIR_ABS\"\n echo \"git_version=$(git --version)\"\n} >\"$OUT_DIR/context.txt\"\n\nif [[ -f \"$GIT_DIR_ABS/HEAD\" ]]; then\n cat \"$GIT_DIR_ABS/HEAD\" >\"$OUT_DIR/head-file.txt\" 2>/dev/null || true\nfi\n\nif [[ -d \"$GIT_DIR_ABS/worktrees\" ]]; then\n ls -la \"$GIT_DIR_ABS/worktrees\" >\"$OUT_DIR/worktrees-dir-listing.txt\" 2>/dev/null || true\nfi\n\nrun_capture status git -C \"$TARGET\" status --porcelain=v2 --branch\nrun_capture branch_current git -C \"$TARGET\" branch --show-current\nrun_capture symbolic_ref_head git -C \"$TARGET\" symbolic-ref -q HEAD\nrun_capture worktree_list git -C \"$TARGET\" worktree list --porcelain\nrun_capture branch_all_verbose git -C \"$TARGET\" branch -vv --all\nrun_capture remote_verbose git -C \"$TARGET\" remote -v\nrun_capture show_ref git -C \"$TARGET\" show-ref --head\nrun_capture reflog_head git -C \"$TARGET\" reflog --date=iso -n 50 HEAD\nrun_capture fsck git -C \"$TARGET\" fsck --full --no-reflogs\n\ncat \u003c\u003cEOF\nGit state snapshot captured.\nDirectory: $OUT_DIR\nUse these files to diagnose before changing refs or worktrees.\nEOF\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":1850,"content_sha256":"f1f998a75676aa8952e7a5ecc14e308f39b6c7d567dc6353d19135445fc899c4"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"unfuck-my-git-state","type":"text"}]},{"type":"paragraph","content":[{"text":"Recover a repo without making the blast radius worse.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Rules","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Snapshot first. Do not \"just try stuff.\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Prefer non-destructive fixes before force operations.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Treat ","type":"text"},{"text":".git/","type":"text","marks":[{"type":"code_inline"}]},{"text":" as production data until backup is taken.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"git symbolic-ref","type":"text","marks":[{"type":"code_inline"}]},{"text":" before manually editing ","type":"text"},{"text":".git/HEAD","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After each fix, run verification before proceeding.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Fast Workflow","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Capture diagnostics:","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/snapshot_git_state.sh .","type":"text"}]},{"type":"ordered_list","attrs":{"order":2,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Route by symptom using ","type":"text"},{"text":"references/symptom-map.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate non-destructive command plan:","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/guided_repair_plan.sh --repo .","type":"text"}]},{"type":"ordered_list","attrs":{"order":4,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Apply the smallest matching playbook.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"references/recovery-checklist.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" verification gate.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Escalate only if the gate fails.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"For explicit routing:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/guided_repair_plan.sh --list\nbash scripts/guided_repair_plan.sh --symptom phantom-branch-lock","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Regression Harness","type":"text"}]},{"type":"paragraph","content":[{"text":"Use disposable simulation tests before changing script logic:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/regression_harness.sh","type":"text"}]},{"type":"paragraph","content":[{"text":"Run one scenario:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/regression_harness.sh --scenario orphaned-worktree","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Playbook A: Orphaned Worktree Metadata","type":"text"}]},{"type":"paragraph","content":[{"text":"Symptoms:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git worktree list","type":"text","marks":[{"type":"code_inline"}]},{"text":" shows a path that no longer exists.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Worktree entries include invalid or zero hashes.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Steps:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"git worktree list --porcelain\ngit worktree prune -v\ngit worktree list --porcelain","type":"text"}]},{"type":"paragraph","content":[{"text":"If stale entries remain, back up ","type":"text"},{"text":".git/","type":"text","marks":[{"type":"code_inline"}]},{"text":" and remove the specific stale folder under ","type":"text"},{"text":".git/worktrees/\u003cname>","type":"text","marks":[{"type":"code_inline"}]},{"text":", then rerun prune.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Playbook B: Phantom Branch Lock","type":"text"}]},{"type":"paragraph","content":[{"text":"Symptoms:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git branch -d","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"git branch -D","type":"text","marks":[{"type":"code_inline"}]},{"text":" fails with \"already used by worktree\".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git worktree list","type":"text","marks":[{"type":"code_inline"}]},{"text":" seems to disagree with branch ownership.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Steps:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"git worktree list --porcelain","type":"text"}]},{"type":"paragraph","content":[{"text":"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.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Playbook C: Detached or Contradictory HEAD","type":"text"}]},{"type":"paragraph","content":[{"text":"Symptoms:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git status","type":"text","marks":[{"type":"code_inline"}]},{"text":" says detached HEAD unexpectedly.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git branch --show-current","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"git symbolic-ref -q HEAD","type":"text","marks":[{"type":"code_inline"}]},{"text":" disagree.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Steps:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"git symbolic-ref -q HEAD || true\ngit reflog --date=iso -n 20\ngit switch \u003cknown-good-branch>","type":"text"}]},{"type":"paragraph","content":[{"text":"If branch context is unknown, create a rescue branch from current commit:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"git switch -c rescue/$(date +%Y%m%d-%H%M%S)","type":"text"}]},{"type":"paragraph","content":[{"text":"Then reconnect to the intended branch after investigation.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Playbook D: Missing or Broken Refs","type":"text"}]},{"type":"paragraph","content":[{"text":"Symptoms:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"unknown revision","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"not a valid object name","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"cannot lock ref","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Steps:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"git fetch --all --prune\ngit show-ref --verify refs/remotes/origin/\u003cbranch>\ngit branch -f \u003cbranch> origin/\u003cbranch>\ngit switch \u003cbranch>","type":"text"}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"reflog","type":"text","marks":[{"type":"code_inline"}]},{"text":" to recover local-only commits before forcing branch pointers.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Last Resort: Manual HEAD Repair","type":"text"}]},{"type":"paragraph","content":[{"text":"Only after backup of ","type":"text"},{"text":".git/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Preferred:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"git show-ref --verify refs/heads/\u003cbranch>\ngit symbolic-ref HEAD refs/heads/\u003cbranch>","type":"text"}]},{"type":"paragraph","content":[{"text":"Fallback when ","type":"text"},{"text":"symbolic-ref","type":"text","marks":[{"type":"code_inline"}]},{"text":" cannot be used:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"echo \"ref: refs/heads/\u003cbranch>\" > .git/HEAD","type":"text"}]},{"type":"paragraph","content":[{"text":"Immediately run the verification gate.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Verification Gate (Must Pass)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run checks in ","type":"text"},{"text":"references/recovery-checklist.md","type":"text","marks":[{"type":"code_inline"}]},{"text":". Minimum bar:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git status","type":"text","marks":[{"type":"code_inline"}]},{"text":" exits cleanly with no fatal errors.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git symbolic-ref -q HEAD","type":"text","marks":[{"type":"code_inline"}]},{"text":" matches intended branch.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git worktree list --porcelain","type":"text","marks":[{"type":"code_inline"}]},{"text":" has no missing paths and no zero hashes.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"git fsck --no-reflogs --full","type":"text","marks":[{"type":"code_inline"}]},{"text":" has no new critical errors.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Escalation Path","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Archive ","type":"text"},{"text":".git","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"tar -czf git-metadata-backup-$(date +%Y%m%d-%H%M%S).tar.gz .git","type":"text"}]},{"type":"ordered_list","attrs":{"order":2,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Clone fresh from remote.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Recover unpushed work with reflog and cherry-pick from old clone.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Document failure mode and add guardrails to automation.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Automation Hooks","type":"text"}]},{"type":"paragraph","content":[{"text":"When building worktree tooling (iMi, scripts, bots), enforce:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"preflight snapshot and state validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"post-operation verification gate","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"hard stop on HEAD/ref inconsistency","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"explicit user confirmation before destructive commands","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Symptom router: ","type":"text"},{"text":"references/symptom-map.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verification checklist: ","type":"text"},{"text":"references/recovery-checklist.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Diagnostic snapshot script: ","type":"text"},{"text":"scripts/snapshot_git_state.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Guided plan generator: ","type":"text"},{"text":"scripts/guided_repair_plan.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Disposable regression harness: ","type":"text"},{"text":"scripts/regression_harness.sh","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"unfuck-my-git-state","author":"@skillopedia","source":{"stars":9,"repo_name":"skills","origin_url":"https://github.com/delorenj/skills/blob/HEAD/unfuck-my-git-state/SKILL.md","repo_owner":"delorenj","body_sha256":"df6950bb8c9839a1835978958dc7ff2e10a1b6bdd63a6a4df80758376d5c54da","cluster_key":"bfeebc051d27f6855bc152ce27b543000905ea3983bd949d33f8f849c705564d","clean_bundle":{"format":"clean-skill-bundle-v1","source":"delorenj/skills/unfuck-my-git-state/SKILL.md","attachments":[{"id":"f5cea8d7-870a-5b34-a40b-b770077bf8eb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f5cea8d7-870a-5b34-a40b-b770077bf8eb/attachment.yaml","path":"agents/openai.yaml","size":247,"sha256":"2f09f4c01470415f7b2a91fe5473c9ad2a9577d30ee090cae450108d64e0179c","contentType":"application/yaml; charset=utf-8"},{"id":"3bc65c88-f1d9-522a-8bec-8d49b83956c1","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3bc65c88-f1d9-522a-8bec-8d49b83956c1/attachment.md","path":"references/recovery-checklist.md","size":1046,"sha256":"d6c97b54ef07548a01dcb69cfc120c61b49b86ab9207da9f197aaa1ef73ed8e0","contentType":"text/markdown; charset=utf-8"},{"id":"68889b09-bca5-5e8e-aa15-b649df235ad5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/68889b09-bca5-5e8e-aa15-b649df235ad5/attachment.md","path":"references/symptom-map.md","size":1655,"sha256":"a776fea6e2a1b06ad5ad3072d618984aa9506ba40b9d760b160beb5f0c4f3902","contentType":"text/markdown; charset=utf-8"},{"id":"893161e0-b853-5309-8cca-f64f47f102e5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/893161e0-b853-5309-8cca-f64f47f102e5/attachment.sh","path":"scripts/guided_repair_plan.sh","size":7410,"sha256":"5cd35f13055fc72de4a5552481c57ff620406e8252e959095ebe0ba89b82883f","contentType":"application/x-sh; charset=utf-8"},{"id":"ca85291c-a770-584f-a7fb-fc0c86145576","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/ca85291c-a770-584f-a7fb-fc0c86145576/attachment.sh","path":"scripts/regression_harness.sh","size":4264,"sha256":"50e835cbd96bb369aa5a12874187994492c888495e3dbb1bcc4a8066dcbc37dd","contentType":"application/x-sh; charset=utf-8"},{"id":"4425c664-41df-5606-a85a-d5c7af79ad18","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4425c664-41df-5606-a85a-d5c7af79ad18/attachment.sh","path":"scripts/snapshot_git_state.sh","size":1850,"sha256":"f1f998a75676aa8952e7a5ecc14e308f39b6c7d567dc6353d19135445fc899c4","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"e55ea53e66231108643af920b6c40426dd69b565b36404ce63ae94c7faabf90b","attachment_count":6,"text_attachments":6,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"unfuck-my-git-state/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"data-analytics","category_label":"Data"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"data-analytics","import_tag":"clean-skills-v1","description":"Diagnose and recover broken Git state and worktree metadata with a staged, low-risk recovery flow. Use when Git reports detached or contradictory HEAD state, phantom worktree locks, orphaned worktree entries, missing refs, 0000000000000000000000000000000000000000 hashes, or branch operations fail with errors like already checked out, unknown revision, not a valid object name, or cannot lock ref."}},"renderedAt":1782981808953}

unfuck-my-git-state Recover a repo without making the blast radius worse. Core Rules 1. Snapshot first. Do not "just try stuff." 2. Prefer non-destructive fixes before force operations. 3. Treat as production data until backup is taken. 4. Use before manually editing . 5. After each fix, run verification before proceeding. Fast Workflow 1. Capture diagnostics: 2. Route by symptom using . 3. Generate non-destructive command plan: 4. Apply the smallest matching playbook. 5. Run verification gate. 6. Escalate only if the gate fails. For explicit routing: Regression Harness Use disposable simulat…