{"record":{"id":"b6336bf02c0ec5c1","repo":"gitbutlerapp/gitbutler","slug":"no-commit-ids-provided-for-discard","errorCode":null,"errorMessage":"no commit IDs provided for discard","messagePattern":"no commit IDs provided for discard","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/commit/discard_commit.rs","lineNumber":19,"sourceCode":"use crate::WorkspaceState;\nuse but_api_macros::but_api;\nuse but_core::{DiffSpec, DryRun, sync::RepoExclusive};\nuse but_oplog::legacy::{OperationKind, SnapshotDetails, Trailer};\nuse but_rebase::graph_rebase::Editor;\nuse tracing::instrument;\n\nuse crate::commit::types::{CommitDiscardResult, MoveChangesResult};\n\nfn unique_subject_commit_ids(\n    subject_commit_ids: Vec<gix::ObjectId>,\n) -> anyhow::Result<Vec<gix::ObjectId>> {\n    let mut seen = gix::hashtable::HashSet::default();\n    let subject_commit_ids = subject_commit_ids\n        .into_iter()\n        .filter(|commit_id| seen.insert(*commit_id))\n        .collect::<Vec<_>>();\n    if subject_commit_ids.is_empty() {\n        anyhow::bail!(\"no commit IDs provided for discard\");\n    }\n    Ok(subject_commit_ids)\n}\n\n/// Discard `subject_commit_ids`, removing them from branch history.\n///\n/// Unlike [`super::uncommit::commit_uncommit()`], the commits' changes are **not**\n/// reassigned to the workspace — they are permanently removed from their branches.\n///\n/// When `dry_run` is enabled, the returned workspace previews the discard\n/// without materializing the rebase.\n/// See [`commit_discard_only_with_perm()`] for details.\n#[but_api(try_from = crate::commit::json::CommitDiscardResult)]\npub fn commit_discard_only(\n    ctx: &mut but_ctx::Context,\n    subject_commit_ids: Vec<gix::ObjectId>,\n    dry_run: DryRun,\n) -> anyhow::Result<CommitDiscardResult> {","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/commit/discard_commit.rs#L1-L37","documentation":"Discarding commits requires at least one commit ID to remove from branch history. The helper `unique_subject_commit_ids` deduplicates the caller-supplied list and bails if nothing remains. This guards downstream rebase machinery from running a pointless or destructive no-op operation.","triggerScenarios":"Calling `commit_discard_only_with_perm` or `commit_discard_with_perm` with an empty `subject_commit_ids` vector (or a vector containing only duplicate IDs that dedupe to zero entries).","commonSituations":"UI layers passing an empty selection when the user confirms discard with nothing selected; CLI argument parsing producing an empty list; upstream filtering logic removing all IDs before the call.","solutions":["Ensure the caller collects at least one commit ID before invoking discard","Check the selection/argument list for emptiness in your UI or CLI layer and short-circuit with a user-facing message","If IDs may contain duplicates, deduplicate is handled internally, but verify your source of IDs isn't returning an empty set due to a filtering bug"],"exampleFix":"// before\napi.commit_discard_only(ctx, vec![])?;\n// after\nif commit_ids.is_empty() {\n    eprintln!(\"Nothing selected to discard\");\n    return Ok(());\n}\napi.commit_discard_only(ctx, commit_ids)?;","handlingStrategy":"validation","validationCode":"if commit_ids.is_empty() {\n    return Err(anyhow::anyhow!(\"nothing selected to discard\"));\n}","typeGuard":null,"tryCatchPattern":"match api.commit_discard_only(ctx, ids) {\n    Err(e) if e.to_string().contains(\"no commit IDs provided\") => warn_user(\"select commits first\"),\n    other => other?,\n}","preventionTips":["Disable/hide discard actions in the UI until at least one commit is selected","Validate list arguments at API boundaries before crossing into domain calls","Remember duplicates are deduped internally — an all-duplicate list still yields entries, only a truly empty set triggers this"],"tags":["validation","empty-input","discard"],"backgroundTag":"missing-required-argument","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}