affaan-m/ECC · error

git merge failed: {stderr}

Error message

git merge failed: {stderr}

What it means

Thrown by merge_into_base at ecc2/src/worktree/mod.rs:897 when `git -C <repo_root> merge --no-edit <worktree.branch>` exits non-zero. By this point all pre-flight checks (merge readiness, clean worktree, correct base branch, clean repo root) have passed, so the failure is from git itself — typically a content conflict that the dry-run readiness check did not predict, a ref mismatch, or a hook rejection. The bail forwards git's stderr verbatim.

Source

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

    }

    if !git_status_short(&repo_root)?.is_empty() {
        anyhow::bail!(
            "Repository root {} has uncommitted changes; commit or stash them before merging",
            repo_root.display()
        );
    }

    let output = Command::new("git")
        .arg("-C")
        .arg(&repo_root)
        .args(["merge", "--no-edit", &worktree.branch])
        .output()
        .context("Failed to merge worktree branch into base")?;

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

    let merged_output = format!(
        "{}\n{}",
        String::from_utf8_lossy(&output.stdout),
        String::from_utf8_lossy(&output.stderr)
    );

    Ok(MergeOutcome {
        branch: worktree.branch.clone(),
        base_branch: worktree.base_branch.clone(),
        already_up_to_date: merged_output.contains("Already up to date."),
    })
}

pub fn rebase_onto_base(worktree: &WorktreeInfo) -> Result<RebaseOutcome> {
    if has_uncommitted_changes(worktree)? {
        anyhow::bail!(

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Inspect the forwarded stderr for the exact conflict path; run `git -C <repo_root> status` to see conflicted files and resolve them, then `git merge --continue` (or `--abort` to roll back).
  2. If a hook rejected the merge, fix the hook's complaint (format the code, remove the secret) or run the merge with `--no-verify` manually if your policy allows it.
  3. Re-run merge_readiness on the worktree right before merge_into_base to catch conflicts that emerged between scheduling and execution.
  4. If the worktree branch was force-pushed, re-sync: rebase the worktree onto the new tip before merging.

Example fix

// before
let outcome = merge_into_base(&worktree)?;

// after: surface git's conflict detail and abort cleanly
match merge_into_base(&worktree) {
    Ok(o) => Ok(o),
    Err(e) if e.to_string().contains("git merge failed") => {
        let _ = Command::new("git").arg("-C").arg(&repo_root)
            .args(["merge", "--abort"]).status();
        anyhow::bail!("merge of {} aborted; resolve conflicts: {e}", worktree.branch);
    }
    Err(e) => Err(e),
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Re-run readiness immediately before merge to catch late conflicts
let readiness = merge_readiness(&worktree)?;
if readiness.status == MergeReadinessStatus::Conflicted {
    anyhow::bail!("pre-merge conflict: {}", readiness.summary);
}
let repo_root = base_checkout_path(&worktree)?;
if get_current_branch(&repo_root)? != worktree.base_branch
    || !git_status_short(&repo_root)?.is_empty() {
    anyhow::bail!("repo root not ready for merge");
}

Type guard

null

Try / catch

match merge_into_base(&worktree) {
    Ok(o) => Ok(o),
    Err(e) if e.to_string().starts_with("git merge failed") => {
        let repo_root = base_checkout_path(&worktree)?;
        let _ = Command::new("git").arg("-C").arg(&repo_root)
            .args(["merge", "--abort"]).status();
        // fall back: rebase the worktree onto base to linearize, then merge
        let _ = rebase_onto_base(&worktree);
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Conflicts on files modified in both branches since the merge base; pre-merge or prepare-commit-msg hooks that exit non-zero; the worktree branch was deleted or force-pushed between the readiness check and the merge command; large-file issues (LFS smudge failures); author/identity config problems when committer hooks fire.

Common situations: Two worktree sessions edited the same files and the second merge conflicts; force-push to the worktree branch changed its tip after merge_readiness ran; a pre-commit hook (lint, format, secret-scan) rejected the merge commit; git LFS not installed or authenticated and a tracked binary file fails to fetch.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/72ce8a35c0c5fa82. Report an issue: GitHub.