GitoxideLabs/gitoxide · error · anyhow::Error
Tree conflicted
Error message
Tree conflicted
What it means
After performing a tree merge, unresolved conflicts remain: `has_unresolved_conflicts` was true, so the command exits with `Tree conflicted` as a non-zero-error signal. The detailed conflict list is written to stderr just before the bail. This is a controlled outcome of the merge, not an internal error.
Solutions
- Inspect the conflict list printed on stderr and resolve each path manually
- Re-run with different merge options or merge-base inputs
- Abort/redo the merge using lower-level gix APIs if partial state was written
Example fix
// before $ gix merge commit --into ref/heads/main other-branch # exits: Tree conflicted // after: resolve conflicts shown on stderr, then commit $ gix status && gix merge commit --into ref/heads/main other-branch
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check: attempt merge in-memory and inspect conflicts before writing
let res = repo.merge_tree(ours, theirs, base, Default::default())?;
if !res.conflicts.is_empty() {
eprintln!("{} conflicts expected", res.conflicts.len());
}
Try / catch
let exit_code = match result {
Err(e) if e.to_string() == "Tree conflicted" => {
eprintln!("merge had unresolved conflicts; see stderr dump");
1
}
other => { other?; 0 }
};
Prevention
- Run a dry-run/in-memory merge first and inspect conflicts
- Avoid merging branches with known overlapping edits without review
- Automate conflict counting via the merge result API before committing
When it happens
Trigger: Running `gix merge commit` where the merge computation returns conflicts that could not be auto-resolved; the command prints `res.conflicts` to stderr and then bails.
Common situations: Merging branches with competing changes to the same paths; history rewrites merged back into a branch; automated merge pipelines hitting real content conflicts.
Related errors
- File conflicted
- Tree conflicted, refusing to write commit
- Tree conflicted
- initial hunks are never ancestors
- we prohibit this
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/ea902f0bf9b51327.
Report an issue: GitHub.
Appendix: source
Thrown at gitoxide-core/src/repository/merge/commit.rs:80
let tree_id = res
.tree
.detach()
.write(|tree| {
written += 1;
repo.write(tree)
})
.map_err(|err| anyhow!("{err}"))?;
writeln!(out, "{tree_id} (wrote {written} trees)")?;
}
if debug {
writeln!(err, "{:#?}", res.conflicts)?;
}
if !has_conflicts {
writeln!(err, "{} possibly resolved conflicts", res.conflicts.len())?;
}
if has_unresolved_conflicts {
bail!("Tree conflicted")
}
Ok(())
}
fn refname_and_commit(
repo: &gix::Repository,
revspec: BString,
) -> anyhow::Result<(Option<BString>, gix::hash::ObjectId)> {
let spec = repo.rev_parse(revspec.as_bstr())?;
let commit_id = spec
.single()
.context("Expected revspec to expand to a single rev only")?
.object()?
.peel_to_commit()?
.id;
let refname = spec.first_reference().map(|r| r.name.shorten().as_bstr().to_owned());
Ok((refname, commit_id))
}View on GitHub (pinned to e73179060b)