gitbutlerapp/gitbutler · error
Found multiple updates for a stack using the merge strategy
Error message
Found multiple updates for a stack using the merge strategy
What it means
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.
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.
Example fix
// before
hints.push(ReviewIntegrationHint::Merge(target));
hints.push(ReviewIntegrationHint::Update(branch)); // 2nd update -> bail
// after
let hints = if needs_merge(target) {
vec![ReviewIntegrationHint::Merge(target)] // exactly one update
} else {
vec![ReviewIntegrationHint::Update(branch)]
};
integrate_upstream_with_hints(ws, &meta, &hints, single_branch)?; Defensive patterns
Strategy: validation
Validate before calling
fn merge_hints_valid(hints: &[ReviewIntegrationHint]) -> bool {
let merges = hints.iter().filter(|h| h.kind() == BottomUpdateKind::Merge).count();
merges == 0 || hints.len() == 1
} Try / catch
match integrate_upstream_with_hints(ws, meta, &hints, single) {
Err(e) if e.to_string().contains("multiple updates") => {
let merged = dedupe_to_single_update(&hints)?;
integrate_upstream_with_hints(ws, meta, &merged, single)
}
other => other,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- An octopus merge commits must have at least two parents
- Branch not found
- cannot currently handle more than 1 parent
- Cannot merge : it shares no history with /
- Failed to find commit
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/f037908e460352e6.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/upstream_integration.rs:297
target_sha,
target_ref.ref_name.as_ref(),
target_ref_commit.detach(),
review_hints,
)?;
// Validate described updates and find commits to rebase
for stack in &mut stacks {
let relevant_updates = updates_with_selectors
.iter()
.filter(|(s, _)| stack.bottoms.contains(s))
.collect::<Vec<_>>();
if relevant_updates
.iter()
.any(|(_, kind)| *kind == BottomUpdateKind::Merge)
{
if relevant_updates.len() > 1 {
bail!("Found multiple updates for a stack using the merge strategy");
}
if stack.heads.len() != 1 || stack.bottoms.len() != 1 {
bail!(
"Merge strategy must only be used on stacks with one head and one bottom commit"
);
}
stack.to_merge = true
} else {
// currently the only other kind is rebase.
let mut tips = relevant_updates.iter().map(|(s, _)| *s).collect::<Vec<_>>();
let mut seen = tips.iter().cloned().collect::<HashSet<_>>();
while let Some(tip) = tips.pop() {
for c in editor
.direct_children(tip)?
.iter()
.filter_map(|(c, _)| stack.nodes.contains_key(c).then_some(*c))View on GitHub (pinned to 58e5313667)