{"record":{"id":"27ac0a542ccb4f80","repo":"GitoxideLabs/gitoxide","slug":"initial-hunks-are-never-ancestors","errorCode":null,"errorMessage":"initial hunks are never ancestors","messagePattern":"initial hunks are never ancestors","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gix-merge/src/blob/builtin_driver/text/function.rs","lineNumber":108,"sourceCode":"                let intersecting_range = before_range_from_hunks(&intersecting);\n                let extended_range = filled_hunks_range.start..intersecting_range.end.max(filled_hunks_range.end);\n                fill_ancestor(&extended_range, &mut current_hunks);\n                fill_ancestor(&extended_range, &mut intersecting);\n            }\n            match conflict {\n                Conflict::Keep { style, marker_size } => {\n                    let marker_size = marker_size.get();\n                    let (hunks_front_and_back, num_hunks_front) = match style {\n                        ConflictStyle::Merge | ConflictStyle::ZealousDiff3 => {\n                            zealously_contract_hunks(&mut current_hunks, &mut intersecting, input, current_tokens)\n                        }\n                        ConflictStyle::Diff3 => (Vec::new(), 0),\n                    };\n                    let (our_hunks, their_hunks) = match filled_hunks_side {\n                        Side::Current => (&current_hunks, &intersecting),\n                        Side::Other => (&intersecting, &current_hunks),\n                        Side::Ancestor => {\n                            unreachable!(\"initial hunks are never ancestors\")\n                        }\n                    };\n                    let (front_hunks, back_hunks) = hunks_front_and_back.split_at(num_hunks_front);\n                    let first_hunk = first_hunk(front_hunks, our_hunks, their_hunks, back_hunks);\n                    let last_hunk = last_hunk(front_hunks, our_hunks, their_hunks, back_hunks);\n                    write_ancestor(input, ancestor_integrated_until, first_hunk.before.start as usize, out);\n                    write_hunks(front_hunks, input, current_tokens, out);\n                    // DEVIATION: this makes tests (mostly) pass, but probably is very different from what Git does.\n                    let hunk_storage;\n                    let nl = detect_line_ending(\n                        if front_hunks.is_empty() {\n                            hunk_storage = Hunk {\n                                before: ancestor_integrated_until..first_hunk.before.start,\n                                after: Default::default(),\n                                side: Side::Ancestor,\n                            };\n                            std::slice::from_ref(&hunk_storage)\n                        } else {","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-merge/src/blob/builtin_driver/text/function.rs#L90-L126","documentation":"In the gix-merge built-in text merge driver, hunks are labeled by which side they were computed for (`Current`, `Other`, or `Ancestor`). When assembling the conflict hunk pairs, the code asserts that the 'filled hunks' side is never `Ancestor`, because initial (ancestor) hunks are integrated separately before this point. The panic fires only if that internal classification invariants is violated.","triggerScenarios":"Not reachable through public merge APIs (`gix::merge::blob` / platform `merge()`): it requires the internal `filled_hunks_side` computation to label hunks as belonging to the ancestor side when constructing Conflict::Remainder style output.","commonSituations":"Only during gix-merge internal development, changes to hunk classification, or custom drivers reusing internal helpers with mislabeled sides.","solutions":["Ensure hunk-side classification assigns `Side::Current` or `Side::Other` when filling conflict hunks; ancestor hunks go through `write_ancestor`.","Use the public merge API (`merge::blob::Platform::merge`) instead of internal driver functions.","Report upstream with the inputs that triggered it if reproducible via the public API."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Ancestor-side hunks must be consumed before conflict assembly\nif filled_hunks_side == Side::Ancestor {\n    return Err(anyhow::anyhow!(\"ancestor hunks must be integrated before conflict output\"));\n}","typeGuard":"fn is_merge_side(side: Side) -> bool {\n    matches!(side, Side::Current | Side::Other)\n}","tryCatchPattern":null,"preventionTips":["Keep ancestor hunk handling in `write_ancestor`, never in ours/theirs assembly.","Add debug assertions on `filled_hunks_side` right after classification.","Use the public `merge::blob` API instead of internal driver functions."],"tags":["panic","git","merge","internal-invariant"],"backgroundTag":"internal-invariant-violation","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"}