affaan-m/ECC · warning · anyhow::Error

no staged changes to commit

Error message

no staged changes to commit

What it means

commit_staged calls has_staged_changes (which scans `git_status_entries` for any entry with `staged == true`) and bails if none exist. This prevents `git commit` from failing or producing an empty commit. It is a state precondition, not a git subprocess failure.

Source

Thrown at ecc2/src/worktree/mod.rs:461

                );
            }
            git_apply_patch(
                &worktree.path,
                &["-R", "--index"],
                &hunk.patch,
                "reset selected staged hunk",
            )
        }
    }
}

pub fn commit_staged(worktree: &WorktreeInfo, message: &str) -> Result<String> {
    let message = message.trim();
    if message.is_empty() {
        anyhow::bail!("commit message cannot be empty");
    }
    if !has_staged_changes(worktree)? {
        anyhow::bail!("no staged changes to commit");
    }

    let output = Command::new("git")
        .arg("-C")
        .arg(&worktree.path)
        .args(["commit", "-m", message])
        .output()
        .context("Failed to create commit")?;
    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        anyhow::bail!("git commit failed: {stderr}");
    }

    let rev_parse = Command::new("git")
        .arg("-C")
        .arg(&worktree.path)
        .args(["rev-parse", "--short", "HEAD"])
        .output()

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Call `git_status_entries` immediately before commit and disable the commit affordance unless at least one entry has `staged == true`.
  2. Stage explicitly via stage_path/stage_hunk before retrying.
  3. Refresh status after any staging mutation and re-check before commit.

Example fix

// before
let hash = commit_staged(&worktree, msg)?;

// after
if !has_staged_changes(&worktree)? {
    return Err(anyhow!("nothing staged; stage changes first"));
}
let hash = commit_staged(&worktree, msg)?;
Defensive patterns

Strategy: validation

Validate before calling

use crate::worktree::has_staged_changes;

if !has_staged_changes(&worktree)? {
    return Err(anyhow!("nothing staged to commit; stage at least one path first"));
}
let hash = commit_staged(&worktree, msg)?;

Type guard

fn has_any_staged(entries: &[GitStatusEntry]) -> bool {
    entries.iter().any(|e| e.staged)
}

Try / catch

match commit_staged(&worktree, msg) {
    Ok(hash) => Ok(hash),
    Err(e) if format!("{e}").contains("no staged changes") => {
        // surface 'stage changes first' and abort
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: User hits commit after unstaging everything; staged changes were already committed by another path; status was stale (the index changed underneath the UI) so the caller believed something was staged when nothing was.

Common situations: Double-commit attempt (UI did not refresh after the previous commit); race where a background task reset the index; user unstaged the last path but the commit button stayed enabled.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/48b3b4530ab2940f. Report an issue: GitHub.