affaan-m/ECC · warning

cannot reset a staged hunk while the file also has unstaged…

Error message

cannot reset a staged hunk while the file also has unstaged changes; unstage it first

What it means

reset_hunk refuses to reverse-apply a staged hunk (`git apply -R --index`) when the same file still has unstaged changes. Doing so would corrupt the working tree state because git's index-level reverse apply requires the working tree to match the index for that file. The library enforces the ordering: unstage or discard unstaged hunks first.

Solutions

  1. Unstage the file first (unstage_path or unstage_hunk), then reset the desired hunks.
  2. Discard the unstaged hunks of that file first (reset_hunk on its Unstaged hunks), then reset the staged hunk.
  3. In UI code, guard: only enable staged-hunk reset when entry.unstaged is false.

Example fix

// before: direct reset of staged hunk
reset_hunk(&worktree, &entry, &staged_hunk)?;

// after: unstage first when mixed state
if entry.unstaged {
    unstage_path(&worktree, &entry)?;
}
reset_hunk(&worktree, &entry, &staged_hunk)?;
Defensive patterns

Strategy: validation

Validate before calling

fn can_reset_staged_hunk(entry: &GitStatusEntry) -> bool {
    !entry.unstaged
}

Type guard

fn staged_hunk_reset_ok(entry: &GitStatusEntry, hunk: &GitPatchHunk) -> bool {
    matches!(hunk.section, GitPatchSectionKind::Staged) && !entry.unstaged
}

Try / catch

match reset_hunk(&worktree, &entry, &hunk) {
    Err(e) if e.to_string().contains("unstaged changes") => {
        unstage_path(&worktree, &entry)?;
        reset_hunk(&worktree, &entry, &hunk)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling reset_hunk with a hunk whose section is Staged while entry.unstaged == true — i.e. the file appears in both the staged and unstaged sections of the status view.

Common situations: Editing a file after partially staging it (via stage_hunk or `git add -p`), then trying to reset the staged portion directly; UIs that do not disable staged-hunk reset actions when the file has additional unstaged edits.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/85c757bdda8a9ffb. Report an issue: GitHub.

Appendix: source

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

    )
}

pub fn reset_hunk(
    worktree: &WorktreeInfo,
    entry: &GitStatusEntry,
    hunk: &GitPatchHunk,
) -> Result<()> {
    if entry.untracked {
        anyhow::bail!("cannot reset hunks for untracked files");
    }

    match hunk.section {
        GitPatchSectionKind::Unstaged => {
            git_apply_patch(&worktree.path, &["-R"], &hunk.patch, "reset selected hunk")
        }
        GitPatchSectionKind::Staged => {
            if entry.unstaged {
                anyhow::bail!(
                    "cannot reset a staged hunk while the file also has unstaged changes; unstage it first"
                );
            }
            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");
    }

View on GitHub (pinned to 8321021c54)