gitbutlerapp/gitbutler · error

Need at least 2 commits to squash

Error message

Need at least 2 commits to squash

What it means

Validation in squash_commits: squashing is defined as combining a target commit with at least one subject commit, so fewer than 2 commits is a no-op and is rejected. The caller must pass a subject list plus a target yielding at least two commits in total.

Solutions

  1. Pass at least one subject commit to squash into the target
  2. Return early with a no-op or user-facing message when the selection is empty
  3. Validate the selection count in the calling layer

Example fix

// before
editor.squash_commits(target, Vec::new(), strategy)?;
// after
if subjects.is_empty() { bail!("select commits to squash"); }
editor.squash_commits(target, subjects, strategy)?;
Defensive patterns

Strategy: validation

Validate before calling

if subjects.is_empty() {
    bail!("select at least one commit to squash into the target");
}
editor.squash_commits(target, subjects, strategy)?;

Type guard

fn squashable(target: gix::ObjectId, subjects: &[gix::ObjectId]) -> bool { !subjects.is_empty() }

Try / catch

match editor.squash_commits(target, subjects, strategy) {
    Err(e) if e.to_string().contains("at least 2 commits") => show_selection_required_message(),
    other => other,
}

Prevention

When it happens

Trigger: Calling squash_commits with an empty subjects iterator (note: the message says 'at least 2' because target + subjects must total ≥ 2 commits).

Common situations: Batch scripts that collect commits to squash from a UI multi-select that came back empty; default-initialized vectors passed through.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/ee11b6e1136da0fe. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-workspace/src/commit/squash_commits.rs:146

///   commits' own change ranges.
/// - A message determined by `how_to_combine_messages`:
///   - `KeepTarget`: target message only.
///   - `KeepSubject`: subject messages only.
///   - `KeepBoth`: target message followed by subject messages.
///
/// Subject messages are appended in the order they are provided, with at least
/// one blank line between non-empty message blocks.
///
pub fn squash_commits<'ws, 'meta, M: RefMetadata, S: ToCommitSelector, T: ToCommitSelector>(
    editor: Editor<'ws, 'meta, M>,
    subjects: Vec<S>,
    target_commit: T,
    how_to_combine_messages: MessageCombinationStrategy,
) -> Result<SquashCommitsOutcome<'ws, 'meta, M>> {
    let mut seen_subjects = std::collections::HashSet::with_capacity(subjects.len());

    if subjects.is_empty() {
        bail!("Need at least 2 commits to squash")
    }

    let (target_commit_selector, target_commit_obj) =
        editor.find_selectable_commit(target_commit)?;

    let mut subject_selectors = Vec::with_capacity(subjects.len());
    for subject_commit in subjects {
        let (subject_commit_selector, _) = editor.find_selectable_commit(subject_commit)?;
        if subject_commit_selector == target_commit_selector {
            bail!("Cannot squash a commit into itself")
        }
        if !seen_subjects.insert(subject_commit_selector) {
            continue;
        }
        subject_selectors.push(subject_commit_selector);
    }

    let subject_commit_ids = subject_selectors

View on GitHub (pinned to 58e5313667)