{"record":{"id":"1e92d94fc154cef3","repo":"Hmbown/CodeWhale","slug":"git-diff-failed","errorCode":null,"errorMessage":"git diff failed: {}","messagePattern":"git diff failed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/lib.rs","lineNumber":8382,"sourceCode":"    let mut cmd = crate::dependencies::Git::command()\n        .ok_or_else(|| anyhow::anyhow!(\"git not found on PATH\"))?;\n    cmd.arg(\"diff\");\n    if args.staged {\n        cmd.arg(\"--cached\");\n    }\n    if let Some(base) = &args.base {\n        cmd.arg(format!(\"{base}...HEAD\"));\n    }\n    if let Some(path) = &args.path {\n        cmd.arg(\"--\").arg(path);\n    }\n\n    let output = cmd\n        .output()\n        .map_err(|e| anyhow::anyhow!(\"Failed to run git diff. Is git installed? ({e})\"))?;\n    if !output.status.success() {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        bail!(\"git diff failed: {}\", stderr.trim());\n    }\n    let mut diff = String::from_utf8_lossy(&output.stdout).to_string();\n    if diff.len() > args.max_chars {\n        diff = crate::utils::truncate_with_ellipsis(&diff, args.max_chars, \"\\n...[truncated]\\n\");\n    }\n    Ok(diff)\n}\n\nfn review_target_label(args: &ReviewArgs) -> String {\n    let mut label = if args.staged {\n        \"staged\".to_string()\n    } else if let Some(base) = args\n        .base\n        .as_deref()\n        .map(str::trim)\n        .filter(|base| !base.is_empty())\n    {\n        format!(\"base:{base}\")","sourceCodeStart":8364,"sourceCodeEnd":8400,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/lib.rs#L8364-L8400","documentation":"The review diff runner spawns `git diff` (optionally `base...HEAD`, `--staged`, path filters) and bails with git's stderr on non-zero exit. It means git refused to compute the diff -- repo, ref, or path problems -- not that the diff was empty.","triggerScenarios":"Running review outside a git work tree; `--base` naming an unknown ref so `base...HEAD` is ambiguous; a path argument git rejects; a stale `.git/index.lock` held by another git process.","commonSituations":"Base-branch typo after clone defaults changed (main vs master); IDE running a git operation concurrently holding index.lock; invoking from a non-repo subdirectory; renamed/deleted path passed via --path.","solutions":["Confirm you are in a repo: `git rev-parse --show-toplevel`","Verify the base ref resolves: `git rev-parse <base>`; fix master/main typos","Remove a stale .git/index.lock only after confirming no git process is running","Re-run with a corrected --path or without it","Reproduce manually with the same git diff arguments to see the raw stderr"],"exampleFix":"# before\ncodewhale review --base main   # git diff failed: fatal: ambiguous argument 'main...HEAD'\n\n# after\ngit branch --show-current       # base is actually 'master'\ncodewhale review --base master","handlingStrategy":"validation","validationCode":"test \"$(git rev-parse --is-inside-work-tree 2>/dev/null)\" = true || { echo 'not a git repo'; exit 1; }\ngit rev-parse --verify -q \"$BASE^{commit}\" >/dev/null || { echo \"base ref '$BASE' does not resolve\"; exit 1; }","typeGuard":null,"tryCatchPattern":"match run_review(config, args).await {\n    Err(e) if e.to_string().contains(\"git diff failed\") => { eprintln!(\"git-level problem: {e:#}\"); fix_base_ref(); }\n    other => other?,\n}","preventionTips":["Verify repo and base refs before invoking review (`git rev-parse <base>`)","Avoid running review while another git process may hold .git/index.lock","Derive --base from `git branch --show-current` or remote HEAD instead of hardcoding"],"tags":["git","subprocess","diff","git-refs"],"backgroundTag":"subprocess-nonzero-exit","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}