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

git merge-tree failed

Error message

git merge-tree failed: {stderr}

What it means

merge_readiness_for_branches runs `git merge-tree` to test whether merging the branch into its base would conflict. When the merge-tree invocation exits nonzero (not just 'has conflicts' output, but an actual git failure), the library bails with git's stderr.

Solutions

  1. Read the stderr — it names the missing ref or git-level failure.
  2. `git fetch --all` to restore missing objects/refs in shallow or pruned clones.
  3. Verify both refs resolve: `git rev-parse <base> <head>`.
  4. Upgrade git to >= 2.38 if merge-tree --write-tree is unsupported.
  5. Run `git fsck` if the object database looks corrupted.
Defensive patterns

Strategy: validation

Validate before calling

fn refs_resolve(path: &Path, refs: &[&str]) -> bool {
    refs.iter().all(|r| Command::new("git").arg(path).args(["rev-parse", "--verify", r])
        .output().map(|o| o.status.success()).unwrap_or(false))
}

Try / catch

match merge_readiness(&wt) {
    Ok(r) => use(r),
    Err(e) => { log::error!("merge-tree failed: {e:#}"); /* fall back to attempting the merge or surfacing the git error */ }
}

Prevention

When it happens

Trigger: Calling merge_readiness / branch_conflict_preview on worktrees whose base or head ref is missing (unborn branch, deleted commit), a corrupt object database, or a git version too old to support the used merge-tree mode.

Common situations: Branch deleted on remote but still referenced locally; shallow clone missing objects; git < 2.38 lacking `--write-tree` merge-tree; repository corrupted after interrupted gc.

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

Appendix: source

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

        let overflow = conflicts.len().saturating_sub(3);
        let detail = if overflow > 0 {
            format!("{conflict_summary}, +{overflow} more")
        } else {
            conflict_summary
        };

        return Ok(MergeReadiness {
            status: MergeReadinessStatus::Conflicted,
            summary: format!(
                "Merge blocked between {left_branch} and {right_branch} by {} conflict(s): {detail}",
                conflicts.len()
            ),
            conflicts,
        });
    }

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

pub fn branch_conflict_preview(
    left: &WorktreeInfo,
    right: &WorktreeInfo,
    max_lines: usize,
) -> Result<Option<BranchConflictPreview>> {
    if left.base_branch != right.base_branch {
        return Ok(None);
    }

    let repo_root = base_checkout_path(left)?;
    let readiness = merge_readiness_for_branches(&repo_root, &left.branch, &right.branch)?;
    if readiness.status != MergeReadinessStatus::Conflicted {
        return Ok(None);
    }

    Ok(Some(BranchConflictPreview {

View on GitHub (pinned to 8321021c54)