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

  1. Ensure at most one update per stack when the merge strategy is involved — drop or deduplicate redundant hints.
  2. Switch the stack's integration strategy from merge to rebase/another strategy that supports multiple updates.
  3. 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

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


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)