gitbutlerapp/gitbutler · info

validated non-empty squash commit list

Error message

validated non-empty squash commit list

What it means

Panic from `expect("validated non-empty squash commit list")` on `ordered_commit_ids.first()` in squash-step planning (plan.rs:285). The function bails at the top when `commit_ids.len() < 2`, and `ordered_commit_ids` is either the mapped selectors (same count) or `commit_ids.to_vec()`, so it has at least two elements and `first()` always yields the squash target. Unreachable by construction.

Source

Thrown at crates/but-workspace/src/branch/integrate_branch_upstream/plan.rs:285

                .into_iter()
                .map(|selector| selector.expect("checked all selectors are present"))
                .collect::<Vec<_>>(),
        )?;
        ordered_selectors
            .iter()
            .map(|selector| {
                editor
                    .find_selectable_commit(*selector)
                    .map(|(_, commit)| commit.id)
            })
            .collect::<Result<Vec<_>>>()?
    } else {
        commit_ids.to_vec()
    };

    let target_commit_id = *ordered_commit_ids
        .first()
        .expect("validated non-empty squash commit list");
    let merge_subject_ids = commit_ids
        .iter()
        .copied()
        .filter(|commit_id| *commit_id != target_commit_id)
        .collect::<Vec<_>>();
    let merge_outcome = editor.merge_commit_changes_to_tree(
        target_commit_id,
        merge_subject_ids,
        editor.repo().merge_options_force_ours()?,
    )?;
    let squashed_parent = editor
        .repo()
        .merge_base_octopus(ordered_commit_ids.iter().copied())
        .context("failed to compute squash merge-base")?
        .detach();

    let tip_commit_id = *ordered_commit_ids
        .last()

View on GitHub (pinned to caf1f223d3)

Solutions

  1. No caller-side action — the `len() < 2` bail above guarantees non-emptiness
  2. If editing, use `let Some(target) = ordered_commit_ids.first() else { bail!("squash produced no commits") };` so future edits cannot turn a logic slip into a panic
  3. Keep a unit test for the single-commit rejection path

Example fix

// before
let target_commit_id = *ordered_commit_ids.first().expect("validated non-empty squash commit list");

// after
let Some(target_commit_id) = ordered_commit_ids.first() else {
    bail!("squash step must have at least two commits");
};
Defensive patterns

Strategy: validation

Validate before calling

// Reject squash plans with fewer than two commits before planning:
fn valid_squash_step(commit_ids: &[ObjectId]) -> bool { commit_ids.len() >= 2 }

Prevention

When it happens

Trigger: Building a squash step from an interactive-integration plan; could only fire if the length guard is removed or `ordered_commit_ids` gains a filtering step that can empty it.

Common situations: Maintainers editing squash planning (e.g. dropping already-applied commits from the ordered list); none for end users.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/040bf2a14ba37711. Report an issue: GitHub.