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
- Ensure the commit ID list is non-empty before calling move_commits
- No-op early in the caller when the selection is empty
- 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
- Validate non-empty selection before calling move_commits
- Treat empty input as a caller-level no-op
- Check that ID collection logic actually populates the Vec
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
- Need at least 2 commits to squash
- No changes were provided to uncommit
- no commit IDs provided for discard
- Aborting due to empty branch name
- Aborting due to empty
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)