GitoxideLabs/gitoxide · error · anyhow::Error
Tree conflicted, refusing to write commit
Error message
Tree conflicted, refusing to write commit
What it means
When a commit message was supplied (i.e. the merge is meant to produce a commit), the merge-tree operation refuses to write that commit if the merged tree still contains unresolved conflicts. Conflict paths are printed to stderr first so the caller can resolve them, then the command bails.
Solutions
- Inspect the conflict paths printed to stderr and resolve them (edit the tree, rerun with resolved inputs, or use `gix repo merge` with conflict resolution).
- Run without `--message` to obtain the conflicted tree/ids without attempting a commit, resolve manually, then commit yourself.
- Pick different base/ours/theirs revisions if the conflict stems from stale inputs.
Example fix
// before (automated commit despite conflicts) gix repo merge tree --update-head --message "merge" <base> <ours> <theirs> // after (let conflicts surface, resolve, then commit) gix repo merge tree <base> <ours> <theirs> # review conflicted paths # resolve, then: gix repo merge tree --update-head --message "merge" <base> <ours> <theirs>
Defensive patterns
Strategy: try-catch
Try / catch
match merge_tree(...) {
Err(e) if e.to_string().contains("Tree conflicted, refusing to write commit") => {
// read conflict paths from stderr, resolve, retry
}
r => r?,
} Prevention
- Pre-check mergeability (merge_base + a dry run without --message) before requesting a commit.
- Parse the conflicted paths the command prints to stderr and resolve them programmatically.
- Never auto-commit merge results in CI without first checking `has_unresolved_conflicts`.
When it happens
Trigger: Running `gix repo merge tree --message ...` where `res.has_unresolved_conflicts(TreatAsUnresolved::default())` returns true for the merge result.
Common situations: Merging branches with genuinely conflicting hunks; re-running an automated merge after a previous partial resolution; conflicts involving file/directory collisions or rename/rename cases the resolver leaves open.
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 GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/e2085d43fcbcddd7.
Report an issue: GitHub.
Appendix: source
Thrown at gitoxide-core/src/repository/merge/tree.rs:90
.into(),
current: ours_ref
.as_ref()
.map_or(ours_id_str.as_str().into(), |n| n.as_bstr())
.into(),
other: theirs_ref
.as_ref()
.map_or(theirs_id_str.as_str().into(), |n| n.as_bstr())
.into(),
};
let res = repo.merge_trees(base_id, ours_id, theirs_id, labels, options)?;
let has_conflicts = !res.conflicts.is_empty();
let has_unresolved_conflicts = res.has_unresolved_conflicts(TreatAsUnresolved::default());
if message.is_some() && has_unresolved_conflicts {
write_unresolved_conflict_paths(err, &res.conflicts)?;
if debug {
writeln!(err, "{:#?}", res.conflicts)?;
}
bail!("Tree conflicted, refusing to write commit");
}
let tree_id = {
let _span = gix::trace::detail!("Writing merged tree");
let mut written = 0;
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)")?;
tree_id
};
let conflicts = res.conflicts;View on GitHub (pinned to e73179060b)