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

git merge failed

Error message

git merge failed: {stderr}

What it means

This error is raised by `merge_into_base` when a `git merge --no-edit <branch>` command executed in the base checkout exits with a non-zero status. The library surfaces git's raw stderr so the developer can see the actual merge conflict or failure. It wraps all git-level merge failures, including conflicts, unrelated histories, and corrupt refs.

Solutions

  1. Inspect the stderr in the message, run 'git merge --no-edit <branch>' manually in the base checkout to see conflict markers
  2. Resolve conflicts: run 'git status', edit conflicted files, 'git add' them, 'git commit' to complete the merge
  3. If conflicts are hard to reconcile, rebase the worktree branch onto the base first ('git rebase <base>') then retry
  4. If histories are unrelated, merge with '--allow-unrelated-histories' or rebuild the branch from the correct base
  5. Ensure the base checkout is clean and on the expected base branch before retrying

Example fix

// before (raw merge that fails)
let output = Command::new("git").args(["merge", "--no-edit", &worktree.branch]).output()?;
// after (pre-check the merge and abort cleanly on conflict)
let check = Command::new("git").args(["merge-tree", "--write-tree", &worktree.branch]).output()?;
anyhow::ensure!(check.status.success(), "merge conflicts detected; resolve before merging");
Defensive patterns

Strategy: try-catch

Validate before calling

let check = Command::new("git")
    .args(["merge-tree", "--write-tree", &worktree.branch])
    .current_dir(&repo_root)
    .output()?;
let merge_clean = check.status.success();

Type guard

fn is_merge_conflict(err: &anyhow::Error) -> bool {
    err.to_string().contains("CONFLICT")
}

Try / catch

match merge_into_base(&wt) {
    Ok(outcome) => outcome,
    Err(e) if e.to_string().contains("CONFLICT") => {
        eprintln!("Resolve merge conflicts in {} then retry", wt.branch);
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling merge_into_base when the worktree branch and base branch have diverged and git cannot auto-merge (conflict markers required), when the merge is refused (e.g. 'refusing to merge unrelated histories'), or when the underlying git process fails for any reason.

Common situations: Parallel development where base branch advanced with conflicting edits; a worktree branch that was rebased onto a different root; a branch deleted or corrupted underneath the tool; divergent commits touching the same lines in the base checkout.

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/72ce8a35c0c5fa82. Report an issue: GitHub.

Appendix: 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 8321021c54)