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

Worktree has uncommitted changes; commit or discard them…

Error message

Worktree {} has uncommitted changes; commit or discard them before merging

What it means

merge_into_base refuses to run when the worktree has uncommitted changes (detected via has_uncommitted_changes). Merging with a dirty tree could lose or mix uncommitted work, so the library requires a clean worktree first.

Solutions

  1. Commit the pending changes in the worktree: `git -C <path> add -A && git commit`.
  2. Or stash them: `git -C <path> stash` and retry the merge.
  3. Discard unwanted changes with `git -C <path> restore .` if they are not needed.
  4. Run has_uncommitted_changes / git status in your own code before calling merge_into_base to fail earlier with a better UX.
  5. Add generated artifacts (build outputs) to .gitignore so tooling writes don't dirty the tree.

Example fix

// before
merge_into_base(&wt)?; // fails: dirty worktree
// after
Command::new("git").arg("-C").arg(&wt.path).args(["add","-A"]).status()?;
Command::new("git").arg("-C").arg(&wt.path).args(["commit","-m","wip"]).status()?;
merge_into_base(&wt)?;
Defensive patterns

Strategy: validation

Validate before calling

if has_uncommitted_changes(&wt)? {
    eprintln!("commit or stash changes in {} first", wt.branch);
    return Ok(());
}

Try / catch

if let Err(e) = merge_into_base(&wt) {
    if e.to_string().contains("uncommitted changes") {
        // prompt user to commit/stash, then retry
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Calling merge_into_base while the worktree has modified, staged, or untracked-but-tracked-index changes — e.g. edits made after the last commit in that worktree.

Common situations: Developer left WIP edits in the worktree; an editor or formatter auto-modified files; tests wrote artifacts into the worktree directory.

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/cbe750a9421ab6a3. Report an issue: GitHub.

Appendix: source

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

pub fn has_uncommitted_changes(worktree: &WorktreeInfo) -> Result<bool> {
    Ok(!git_status_short(&worktree.path)?.is_empty())
}

pub fn has_staged_changes(worktree: &WorktreeInfo) -> Result<bool> {
    Ok(git_status_entries(worktree)?
        .iter()
        .any(|entry| entry.staged))
}

pub fn merge_into_base(worktree: &WorktreeInfo) -> Result<MergeOutcome> {
    let readiness = merge_readiness(worktree)?;
    if readiness.status == MergeReadinessStatus::Conflicted {
        anyhow::bail!(readiness.summary);
    }

    if has_uncommitted_changes(worktree)? {
        anyhow::bail!(
            "Worktree {} has uncommitted changes; commit or discard them before merging",
            worktree.branch
        );
    }

    let repo_root = base_checkout_path(worktree)?;
    let current_branch = get_current_branch(&repo_root)?;
    if current_branch != worktree.base_branch {
        anyhow::bail!(
            "Base branch {} is not checked out in repo root (currently {})",
            worktree.base_branch,
            current_branch
        );
    }

    if !git_status_short(&repo_root)?.is_empty() {
        anyhow::bail!(
            "Repository root {} has uncommitted changes; commit or stash them before merging",

View on GitHub (pinned to 8321021c54)