gitbutlerapp/gitbutler · warning

No uncommitted changes to discard

Error message

No uncommitted changes to discard

What it means

For uncommitted discards, DiffSpecBuilder accumulates specs from the selected uncommitted changes (hunks/files/path-prefixes), reconciles them against the worktree, then ensure! requires at least one resulting spec. The error means the selection resolved to zero actual worktree changes — e.g. the selected hunks were already discarded, files reverted externally, or the selectors matched nothing dirty. Discard then refuses to open a transaction and record an oplog entry for a no-op.

Source

Thrown at crates/but/src/command/legacy/discard.rs:458

                match selection {
                    UncommittedSelection::All => builder.push_changes_from_uncommitted_area()?,
                    UncommittedSelection::Changes(changes) => {
                        for change in *changes {
                            match change {
                                UncommittedDiscardSource::HunkOrFile(change) => {
                                    builder.push_changes_from_uncommitted(&change)?;
                                }
                                UncommittedDiscardSource::PathPrefix(hunks) => {
                                    builder.push_changes_from_path_prefix(&hunks)?;
                                }
                            }
                        }
                    }
                }
                builder.reconcile_worktree_diff_specs()?;
                builder.into_diff_specs()
            };
            anyhow::ensure!(!changes.is_empty(), "No uncommitted changes to discard");
            let paths = paths_from_changes(&changes);
            ExecutableDiscardOperation::Uncommitted { paths, changes }
        }
    };

    let (mut outcome, mut ws) = but_transaction::with_transaction_with_perm(
        ctx,
        meta,
        perm,
        SnapshotDetails::new(oplog_operation_kind),
        DryRun::No,
        |mut tx| {
            let outcome = match executable {
                ExecutableDiscardOperation::Branches { branches, commits } => {
                    if !commits.is_empty() {
                        tx.discard_commits(commits.iter().map(|c| c.commit_id))?;
                    }
                    for branch in &branches {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Check the worktree is still dirty for those paths: `git status` / `git diff` — if clean, there is nothing to discard.
  2. Refresh selectors with `but id` and re-run if changes still exist under different IDs.
  3. Avoid concurrent revert operations while issuing discards.

Example fix

# before
$ but discard 2b   # hunk 2b was already discarded
Error: No uncommitted changes to discard

# after
$ but id   # pick a currently dirty change
$ but discard 5f
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the worktree is still dirty before discarding
if repo.status_dirty_paths().is_empty() {
    return Ok(()); // nothing uncommitted, nothing to discard
}

Try / catch

if let Err(e) = discard_uncommitted(ctx, selection) {
    if e.to_string().contains("No uncommitted changes to discard") {
        return Ok(()); // benign: selection already clean
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Calling discard-uncommitted with selectors from a stale ID map after another tool (editor autosave-revert, git checkout --) already cleared the changes; or with filters whose reconcile_worktree_diff_specs() produced an empty set.

Common situations: Two windows both operating on the worktree; retrying a discard that already succeeded; IDE auto-reverting a file between `but id` and the discard call.

Related errors


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