{"record":{"id":"f037908e460352e6","repo":"gitbutlerapp/gitbutler","slug":"found-multiple-updates-for-a-stack-using-the-merge","errorCode":null,"errorMessage":"Found multiple updates for a stack using the merge strategy","messagePattern":"Found multiple updates for a stack using the merge strategy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/upstream_integration.rs","lineNumber":297,"sourceCode":"        target_sha,\n        target_ref.ref_name.as_ref(),\n        target_ref_commit.detach(),\n        review_hints,\n    )?;\n\n    // Validate described updates and find commits to rebase\n    for stack in &mut stacks {\n        let relevant_updates = updates_with_selectors\n            .iter()\n            .filter(|(s, _)| stack.bottoms.contains(s))\n            .collect::<Vec<_>>();\n\n        if relevant_updates\n            .iter()\n            .any(|(_, kind)| *kind == BottomUpdateKind::Merge)\n        {\n            if relevant_updates.len() > 1 {\n                bail!(\"Found multiple updates for a stack using the merge strategy\");\n            }\n            if stack.heads.len() != 1 || stack.bottoms.len() != 1 {\n                bail!(\n                    \"Merge strategy must only be used on stacks with one head and one bottom commit\"\n                );\n            }\n\n            stack.to_merge = true\n        } else {\n            // currently the only other kind is rebase.\n            let mut tips = relevant_updates.iter().map(|(s, _)| *s).collect::<Vec<_>>();\n            let mut seen = tips.iter().cloned().collect::<HashSet<_>>();\n\n            while let Some(tip) = tips.pop() {\n                for c in editor\n                    .direct_children(tip)?\n                    .iter()\n                    .filter_map(|(c, _)| stack.nodes.contains_key(c).then_some(*c))","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-workspace/src/upstream_integration.rs#L279-L315","documentation":"Thrown by integrate_upstream_with_hints when a stack has more than one relevant update and at least one uses the Merge strategy. Merging upstream wholesale is only defined when there is exactly one update to apply; with multiple updates the library cannot decide how to combine a merge with the others. It is a precondition check before performing the integration.","triggerScenarios":"Review hints or upstream state produce multiple relevant_updates for one stack where any update kind is BottomUpdateKind::Merge and relevant_updates.len() > 1 — e.g. a merge-based integration hint combined with another hint affecting the same stack.","commonSituations":"Users supply conflicting review-integration hints for the same stack; automation applies both a merge hint and a rebase/other update; a stack that both needs a target merge and has additional branch updates pending.","solutions":["Ensure at most one update per stack when the merge strategy is involved — drop or deduplicate redundant hints.","Switch the stack's integration strategy from merge to rebase/another strategy that supports multiple updates.","Apply the merge update first, then re-run integration so subsequent updates are computed on the new state."],"exampleFix":"// before\nhints.push(ReviewIntegrationHint::Merge(target));\nhints.push(ReviewIntegrationHint::Update(branch)); // 2nd update -> bail\n\n// after\nlet hints = if needs_merge(target) {\n    vec![ReviewIntegrationHint::Merge(target)] // exactly one update\n} else {\n    vec![ReviewIntegrationHint::Update(branch)]\n};\nintegrate_upstream_with_hints(ws, &meta, &hints, single_branch)?;","handlingStrategy":"validation","validationCode":"fn merge_hints_valid(hints: &[ReviewIntegrationHint]) -> bool {\n    let merges = hints.iter().filter(|h| h.kind() == BottomUpdateKind::Merge).count();\n    merges == 0 || hints.len() == 1\n}","typeGuard":null,"tryCatchPattern":"match integrate_upstream_with_hints(ws, meta, &hints, single) {\n    Err(e) if e.to_string().contains(\"multiple updates\") => {\n        let merged = dedupe_to_single_update(&hints)?;\n        integrate_upstream_with_hints(ws, meta, &merged, single)\n    }\n    other => other,\n}","preventionTips":["Deduplicate review hints per stack before integration","Use rebase-based strategies when a stack has more than one pending update","Apply merge updates one at a time, re-running integration between them","Validate the one-update-per-merge-stack invariant in a unit test"],"tags":["git","merge","hints"],"backgroundTag":"conflicting-config-options","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}