{"record":{"id":"70ebfdfa1a851aef","repo":"GitoxideLabs/gitoxide","slug":"tree-conflicted-70ebfd","errorCode":null,"errorMessage":"Tree conflicted","messagePattern":"Tree conflicted","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"gitoxide-core/src/repository/merge/tree.rs","lineNumber":134,"sourceCode":"                let commit_id = repo.commit(\"HEAD\", message, tree_id, Some(head_id))?;\n                let mut index = repo.index_from_tree(&tree_id)?;\n                index.write(Default::default())?;\n                commit_id\n            } else {\n                repo.new_commit(message, tree_id, Some(head_id))?.id()\n            };\n            writeln!(out, \"{commit_id} (commit)\")?;\n            return Ok(());\n        }\n\n        if debug {\n            writeln!(err, \"{conflicts:#?}\")?;\n        }\n        if has_conflicts {\n            writeln!(err, \"{} possibly resolved conflicts\", conflicts.len())?;\n        }\n        if has_unresolved_conflicts {\n            bail!(\"Tree conflicted\")\n        }\n        Ok(())\n    }\n\n    fn persist_in_memory_objects(repo: &mut gix::Repository) -> anyhow::Result<()> {\n        let objects = repo.objects.take_object_memory().expect(\"always write in memory first\");\n        for (_id, (kind, data)) in objects.iter() {\n            repo.write_buf(*kind, data).map_err(|err| anyhow!(\"{err}\"))?;\n        }\n        Ok(())\n    }\n\n    fn write_unresolved_conflict_paths(\n        err: &mut dyn std::io::Write,\n        conflicts: &[gix::merge::tree::Conflict],\n    ) -> std::io::Result<()> {\n        let how = TreatAsUnresolved::default();\n        let mut paths = BTreeSet::new();","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gitoxide-core/src/repository/merge/tree.rs#L116-L152","documentation":"At the end of the merge-tree run, after printing conflict details, the command fails with 'Tree conflicted' if any conflicts remain unresolved (`has_unresolved_conflicts`). Unlike error 112 this path also fires when no commit message was given, i.e. it marks the overall merge as conflicted even for dry-run style invocations.","triggerScenarios":"`tree()` completes the merge, prints conflicts (and optionally `--debug` dumps), and `has_unresolved_conflicts` is true; the function then bails instead of returning Ok.","commonSituations":"Scripting merges whose inputs genuinely conflict; using a `TreatAsUnresolved` configuration that classifies certain conflict kinds as blocking; CI jobs merging feature branches that overlap with main.","solutions":["Read the conflict list printed on stderr and resolve those paths before re-running.","Treat a non-zero exit as the signal to abort or escalate the automated merge.","Adjust `TreatAsUnresolved` semantics only if your tooling can safely handle those conflict kinds."],"exampleFix":"// caller side\nmatch merge_result {\n    Ok(id) => use_tree(id),\n    Err(e) if e.to_string().contains(\"Tree conflicted\") => {\n        // parse conflicted paths from stderr and resolve\n        resolve_conflicts();\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let res = tree_merge(...)?;\nif res.has_unresolved_conflicts(TreatAsUnresolved::default()) {\n    // handle conflicts before treating the run as successful\n} else {\n    persist(res)?;\n}","preventionTips":["Check `res.has_unresolved_conflicts(...)` on the merge result instead of relying on exit codes alone.","Log the `conflicts` list for post-mortem in automated pipelines.","Test merge automations against known-conflicting fixtures."],"tags":["merge","conflict","git","gitoxide"],"backgroundTag":"git-command-failed","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}