{"record":{"id":"cbe750a9421ab6a3","repo":"affaan-m/ECC","slug":"worktree-has-uncommitted-changes-commit-or-dis","errorCode":null,"errorMessage":"Worktree {} has uncommitted changes; commit or discard them before merging","messagePattern":"Worktree (.+?) has uncommitted changes; commit or discard them before merging","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"ecc2/src/worktree/mod.rs","lineNumber":865,"sourceCode":"\npub fn has_uncommitted_changes(worktree: &WorktreeInfo) -> Result<bool> {\n    Ok(!git_status_short(&worktree.path)?.is_empty())\n}\n\npub fn has_staged_changes(worktree: &WorktreeInfo) -> Result<bool> {\n    Ok(git_status_entries(worktree)?\n        .iter()\n        .any(|entry| entry.staged))\n}\n\npub fn merge_into_base(worktree: &WorktreeInfo) -> Result<MergeOutcome> {\n    let readiness = merge_readiness(worktree)?;\n    if readiness.status == MergeReadinessStatus::Conflicted {\n        anyhow::bail!(readiness.summary);\n    }\n\n    if has_uncommitted_changes(worktree)? {\n        anyhow::bail!(\n            \"Worktree {} has uncommitted changes; commit or discard them before merging\",\n            worktree.branch\n        );\n    }\n\n    let repo_root = base_checkout_path(worktree)?;\n    let current_branch = get_current_branch(&repo_root)?;\n    if current_branch != worktree.base_branch {\n        anyhow::bail!(\n            \"Base branch {} is not checked out in repo root (currently {})\",\n            worktree.base_branch,\n            current_branch\n        );\n    }\n\n    if !git_status_short(&repo_root)?.is_empty() {\n        anyhow::bail!(\n            \"Repository root {} has uncommitted changes; commit or stash them before merging\",","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/worktree/mod.rs#L847-L883","documentation":"merge_into_base's second guard: after passing the merge-readiness check, it calls has_uncommitted_changes (any non-empty `git status --porcelain` output) on the worktree and bails if dirty. Merging a worktree with uncommitted changes risks conflicts git cannot resolve, so the library requires a clean tree first.","triggerScenarios":"Calling merge_into_base on a worktree that has staged or unstaged modifications, untracked files (porcelain reports them with `??`), or leftover conflict markers from a prior operation.","commonSituations":"Agent edited files but never committed; a build tool wrote artifacts that git now reports as untracked; an earlier commit failed partway and left the index dirty.","solutions":["Commit or discard changes first: `commit_staged` plus stage_path for unstaged work, or `reset_path` to discard.","Pre-check with `has_uncommitted_changes(&worktree)?` and gate the merge UI on a clean tree.","If untracked build artifacts are the only noise, gitignore them so porcelain goes quiet.","Refresh status immediately before the merge to avoid stale-clean assumptions."],"exampleFix":"// before\nlet outcome = merge_into_base(&worktree)?;\n\n// after\nif has_uncommitted_changes(&worktree)? {\n    return Err(anyhow!(\"commit or discard changes in {} before merging\", worktree.branch));\n}\nlet outcome = merge_into_base(&worktree)?;","handlingStrategy":"validation","validationCode":"use crate::worktree::has_uncommitted_changes;\n\nif has_uncommitted_changes(&worktree)? {\n    return Err(anyhow!(\n        \"worktree {} has uncommitted changes; commit or discard before merging\",\n        worktree.branch\n    ));\n}\nlet outcome = merge_into_base(&worktree)?;","typeGuard":"fn is_clean(worktree: &WorktreeInfo) -> anyhow::Result<bool> {\n    Ok(!has_uncommitted_changes(worktree)?)\n}","tryCatchPattern":"match merge_into_base(&worktree) {\n    Ok(outcome) => Ok(outcome),\n    Err(e) if format!(\"{e:#}\").contains(\"uncommitted changes\") => {\n        // prompt user to commit or discard, then retry\n        Err(e)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Gate the merge button on `has_uncommitted_changes == false`, checked from a fresh status.","Commit or discard before any merge attempt; never merge a dirty tree.","Gitignore deterministic build artifacts so they do not keep the tree perpetually dirty.","Refresh status immediately before the merge to avoid stale-clean assumptions."],"tags":["git","worktree","merge","validation","dirty-tree"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}