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
- Pass at least one subject commit to squash into the target
- Return early with a no-op or user-facing message when the selection is empty
- 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
- Require at least one subject in the squash UI/CLI
- Guard empty multi-selects before invoking the API
- Remember the target counts toward the two-commit minimum
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
- Cannot squash a commit into itself
- No changes were provided to uncommit
- no commit IDs provided for discard
- No commits were provided to move
- No commits were provided to squash
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_selectorsView on GitHub (pinned to 58e5313667)