affaan-m/ECC · error · anyhow::Error

git diff failed

Error message

git diff failed: {stderr}

What it means

`git_diff_patch_text_for_paths` runs a filtered `git diff` and bails when the command exits non-zero, forwarding git's stderr. This is a wrapper over git plumbing, so the error means git itself rejected the diff invocation (bad rev, bad pathspec, corrupt index) — the message mirrors whatever git printed.

Solutions

  1. Read the stderr in the message for git's specific complaint (e.g. 'fatal: ambiguous argument')
  2. For unborn-branch repos, guard the call: only diff once HEAD exists ('git rev-parse HEAD' succeeds)
  3. Remove a stale lock: check for .git/index.lock and delete it if no git process is running
  4. Verify the paths passed as filters exist and are tracked, or pass '--' before pathspecs
  5. Run 'git diff <same-args>' manually in the worktree to reproduce

Example fix

// before: fails on unborn HEAD
git_status_patch_view(&wt)?;
// after: skip diff when HEAD missing
let has_head = Command::new("git")
    .args(["rev-parse", "--verify", "HEAD"])
    .current_dir(&wt.path)
    .output()?;
let view = if has_head.status.success() {
    git_status_patch_view(&wt)?
} else {
    PatchView::default()
};
Defensive patterns

Strategy: try-catch

Validate before calling

let has_head = Command::new("git")
    .args(["rev-parse", "--verify", "HEAD"])
    .current_dir(&wt.path)
    .output()
    .map(|o| o.status.success())
    .unwrap_or(false);

Try / catch

match git_status_patch_view(&wt) {
    Ok(v) => v,
    Err(e) if e.to_string().contains("ambiguous argument") => {
        // unborn HEAD (no commits yet) — treat as empty view
        PatchView::default()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling git_status_patch_view (which calls this function) when the repo has no HEAD yet (unborn branch), a pathspec matches nothing with certain flags, the index is corrupt/locked, or invalid refs are passed as path filters.

Common situations: Freshly `git init`ed repo with no commits; a race where the worktree's branch was deleted; an index.lock left by a crashed process; quoting issues in path arguments.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/bc3ca3c1c9ce646a. Report an issue: GitHub.

Appendix: source

Thrown at ecc2/src/worktree/mod.rs:1079

    let mut command = Command::new("git");
    command
        .arg("-C")
        .arg(worktree_path)
        .arg("diff")
        .args(["--patch", "--find-renames"]);
    command.args(extra_args);
    command.arg("--");
    for path in paths {
        command.arg(path);
    }

    let output = command
        .output()
        .context("Failed to generate filtered git patch")?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        anyhow::bail!("git diff failed: {stderr}");
    }

    Ok(String::from_utf8_lossy(&output.stdout).into_owned())
}

fn git_diff_patch_lines_for_paths(
    worktree_path: &Path,
    extra_args: &[&str],
    paths: &[String],
) -> Result<Vec<String>> {
    if paths.is_empty() {
        return Ok(Vec::new());
    }

    let mut command = Command::new("git");
    command
        .arg("-C")
        .arg(worktree_path)

View on GitHub (pinned to 8321021c54)