{"record":{"id":"b352b8b48f06ee07","repo":"affaan-m/ECC","slug":"repository-root-has-uncommitted-changes-commit","errorCode":null,"errorMessage":"Repository root {} has uncommitted changes; commit or stash them before merging","messagePattern":"Repository root (.+?) has uncommitted changes; commit or stash them before merging","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/worktree/mod.rs","lineNumber":882,"sourceCode":"    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\",\n            repo_root.display()\n        );\n    }\n\n    let output = Command::new(\"git\")\n        .arg(\"-C\")\n        .arg(&repo_root)\n        .args([\"merge\", \"--no-edit\", &worktree.branch])\n        .output()\n        .context(\"Failed to merge worktree branch into base\")?;\n\n    if !output.status.success() {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        anyhow::bail!(\"git merge failed: {stderr}\");\n    }\n\n    let merged_output = format!(","sourceCodeStart":864,"sourceCodeEnd":900,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/worktree/mod.rs#L864-L900","documentation":"Thrown by merge_into_base at ecc2/src/worktree/mod.rs:882 after the base-branch check passes. It runs git_status_short(&repo_root) and bails if any entry comes back, because a `git merge` into a dirty working tree can fail mid-merge or silently interleave uncommitted edits with merge results. The repo root (resolved by base_checkout_path) must be clean before the merge command shells out.","triggerScenarios":"Calling merge_into_base when the main checkout has staged or unstaged changes, untracked files that would be overwritten by the merge, or leftover artifacts from a prior merge/rebase. Also fires when an editor or build tool wrote to tracked files in the repo root between the branch check and the status check.","commonSituations":"Developer edited files in the main repo while an agent worked in its worktree; build tools regenerated lockfiles or dist artifacts in the repo root; a previous merge left conflict markers that were never resolved; untracked files (logs, coverage reports) collide with files incoming from the worktree branch.","solutions":["Commit or stash changes in the repo root: `git -C <repo_root> stash` (or `git -C <repo_root> commit -am 'wip'`), then retry merge_into_base.","Discard repo-root changes if unwanted: `git -C <repo_root> checkout -- .` and remove untracked files that conflict.","If untracked files are the cause, add them to .gitignore or move them out of the repo root.","Ensure no background processes (formatters, watchers, builds) are mutating the repo root during the merge."],"exampleFix":"// before\nmerge_into_base(&worktree)?;\n\n// after: pre-flight clean check\nlet repo_root = base_checkout_path(&worktree)?;\nif !git_status_short(&repo_root)?.is_empty() {\n    Command::new(\"git\").arg(\"-C\").arg(&repo_root)\n        .args([\"stash\", \"--include-untracked\"]).status()?;\n}\nlet outcome = merge_into_base(&worktree)?;","handlingStrategy":"validation","validationCode":"fn repo_root_clean(repo_root: &Path) -> Result<bool> {\n    Ok(git_status_short(repo_root)?.is_empty())\n}\n\n// before merge_into_base:\nlet repo_root = base_checkout_path(&worktree)?;\nif !repo_root_clean(&repo_root)? {\n    Command::new(\"git\").arg(\"-C\").arg(&repo_root)\n        .args([\"stash\", \"--include-untracked\"]).status()?;\n}","typeGuard":"null","tryCatchPattern":"match merge_into_base(&worktree) {\n    Ok(o) => o,\n    Err(e) if e.to_string().contains(\"has uncommitted changes\") => {\n        let repo_root = base_checkout_path(&worktree)?;\n        let _ = Command::new(\"git\").arg(\"-C\").arg(&repo_root)\n            .args([\"stash\", \"--include-untracked\"]).status();\n        merge_into_base(&worktree)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Treat the repo root as read-only while sessions are active — push agent work into worktrees only.","gitignore build artifacts so they do not appear in git_status_short.","Add a pre-merge hook that refuses to start when the repo root is dirty.","Document the clean-root precondition in the merge command's user-facing help."],"tags":["git","worktree","merge","dirty-tree","state-mismatch"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}