gitbutlerapp/gitbutler · error

No commits were provided to cherry-pick

Error message

No commits were provided to cherry-pick

What it means

cherry_pick_commits deduplicates the input ids into a Vec and bails when the result is empty. There is nothing to apply, and proceeding would create an empty rebase outcome, so it fails fast. This is a pure caller-input error; no editor state has been consumed.

Source

Thrown at crates/but-workspace/src/commit/cherry_pick.rs:34

/// Sources are read from the object database, so they may live anywhere in the repository,
/// including on branches that aren't part of the workspace. Duplicates are dropped, keeping the
/// first occurrence, and the rest are applied in the order given rather than reordered, like
/// `git cherry-pick`: the first source lands at `side` of `relative_to`, and each later one
/// directly above the one before it.
/// Child commits, and the target commit, if applicable, are rebased atop the cherry-picked commits.
pub fn cherry_pick_commits<'ws, 'meta, M: RefMetadata>(
    mut editor: Editor<'ws, 'meta, M>,
    source_commits: impl IntoIterator<Item = gix::ObjectId>,
    relative_to: RelativeTo,
    side: InsertSide,
) -> anyhow::Result<(SuccessfulRebase<'ws, 'meta, M>, Vec<Selector>)> {
    let mut seen = HashSet::new();
    let sources = source_commits
        .into_iter()
        .filter(|id| seen.insert(*id))
        .collect::<Vec<_>>();
    if sources.is_empty() {
        bail!("No commits were provided to cherry-pick")
    }
    if matches!(
        (&relative_to, side),
        (RelativeTo::Reference(_), InsertSide::Above)
    ) {
        bail!("Cannot cherry-pick above a reference")
    }

    let target = relative_to.to_selector(&editor)?;

    let mut inserted_selectors = Vec::with_capacity(sources.len());
    let mut previous_selector = None;
    for source in sources {
        // Give the copy its own change ID, retaining all other metadata.
        let mut template = editor.find_commit(source)?;
        let mut headers = Headers::try_from_commit(&template.inner).unwrap_or_default();
        headers.change_id = Headers::from_config(&editor.repo().config_snapshot()).change_id;
        headers.set_in_commit(&mut template.inner);

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Guard the call: if the selection is empty, skip the operation (no-op) instead of invoking the API.
  2. Fix the caller that produced an empty commit list (selection state, filter bug).
  3. In batch processing, filter empty batches before calling.

Example fix

// before
cherry_pick_commits(editor, selected_ids, relative_to, side)?;

// after
if selected_ids.is_empty() {
    return Ok((successful_rebase, vec![])); // nothing to pick
}
cherry_pick_commits(editor, selected_ids, relative_to, side)?;
Defensive patterns

Strategy: validation

Validate before calling

let ids: Vec<_> = source_commits.into_iter().collect::<HashSet<_>>().into_iter().collect();
if ids.is_empty() {
    // nothing selected: no-op instead of an API error
    return Ok((successful_rebase, vec![]));
}

Prevention

When it happens

Trigger: Calling cherry_pick_commits with an empty iterator, or with an iterator whose items all deduplicate away (single id repeated is still one entry — only zero unique ids triggers this).

Common situations: UI 'cherry-pick selected commits' invoked with an empty selection; upstream list filtered down to nothing before the call; batch loops where one chunk is legitimately empty.

Related errors


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