gitbutlerapp/gitbutler · error

No commits were provided to move

Error message

No commits were provided to move

What it means

Input validation in move_commits: the caller supplied an empty commit list, so there is nothing to move. Like cherry-pick, this is a caller/programming error; at least one commit selector is required for the move operation to proceed.

Solutions

  1. Ensure the commit ID list is non-empty before calling move_commits
  2. No-op early in the caller when the selection is empty
  3. Validate user selection at the UI/CLI layer before invoking the API

Example fix

// before
editor.move_commits(Vec::new(), relative_to, side)?;
// after
if ids.is_empty() { return Ok(()); }
editor.move_commits(ids, relative_to, side)?;
Defensive patterns

Strategy: validation

Validate before calling

if subject_commit_ids.is_empty() {
    return Ok(()); // nothing to move
}
editor.move_commits(subject_commit_ids, relative_to, side)?;

Type guard

fn has_commits(ids: &[gix::ObjectId]) -> bool { !ids.is_empty() }

Try / catch

match editor.move_commits(ids, relative_to, side) {
    Err(e) if e.to_string().contains("No commits were provided") => Ok(()),
    other => other,
}

Prevention

When it happens

Trigger: Calling WorkspaceEditor::move_commits with an empty iterator of gix::ObjectId as subject_commit_ids.

Common situations: Scripted moves where the selection list was filtered to empty; forgetting to push IDs into the Vec before the call.

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/c77769c89214ea92. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-workspace/src/commit/move_commit.rs:28

/// Move multiple commits.
///
/// The commits are ordered by parentage before moving so callers do not need to
/// provide them in graph order.
///
/// Each subject is plucked from its old slot - replaced in place by [`Step::None`] -
/// and inserted relative to `relative_to`. Leaving a placeholder behind means the source
/// topology is never rewritten, so every reference anchored in it keeps its position and
/// resolves through the placeholder to the commit below, which is exactly what a branch
/// a commit was moved out of should do.
pub fn move_commits<'ws, 'meta, M: RefMetadata>(
    editor: Editor<'ws, 'meta, M>,
    subject_commit_ids: impl IntoIterator<Item = gix::ObjectId>,
    relative_to: RelativeTo,
    side: InsertSide,
) -> anyhow::Result<SuccessfulRebase<'ws, 'meta, M>> {
    let subject_commit_ids = subject_commit_ids.into_iter().collect::<Vec<_>>();
    if subject_commit_ids.is_empty() {
        bail!("No commits were provided to move")
    }

    let mut ordered_selectors = editor.order_commit_selectors_by_parentage(subject_commit_ids)?;
    // Every insert lands adjacent to the anchor, so consecutive inserts stack away from
    // it. Parentage order already reads base-first, which is what inserting below wants;
    // inserting above walks the other way.
    if matches!(side, InsertSide::Above) {
        ordered_selectors.reverse();
    }

    let mut editor = editor;
    let mut plucked = Vec::with_capacity(ordered_selectors.len());
    for selector in ordered_selectors {
        // The first-parent edge is the slot the commit sat in and stays with the
        // placeholder; a merge commit's remaining parents are part of the commit itself
        // and travel with it.
        let mut parents = editor.direct_parents(selector)?;
        parents.sort_by_key(|(_, order)| *order);

View on GitHub (pinned to 58e5313667)