{"record":{"id":"a4da82dc5c21c93e","repo":"affaan-m/ECC","slug":"base-branch-is-not-checked-out-in-repo-root-cu","errorCode":null,"errorMessage":"Base branch {} is not checked out in repo root (currently {})","messagePattern":"Base branch (.+?) is not checked out in repo root \\(currently (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/worktree/mod.rs","lineNumber":874,"sourceCode":"}\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\",\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()","sourceCodeStart":856,"sourceCodeEnd":892,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/worktree/mod.rs#L856-L892","documentation":"Thrown by merge_into_base at ecc2/src/worktree/mod.rs:874 when the repository root (the primary checkout that owns the worktree) is not currently sitting on the configured base branch. The merge step shells out `git merge` with -C pointed at base_checkout_path(), so git would otherwise merge into whatever branch happens to be checked out. The guard compares get_current_branch(repo_root) against worktree.base_branch (from the WorktreeInfo captured at session creation) and refuses to proceed on mismatch.","triggerScenarios":"Calling merge_into_base(&WorktreeInfo) after a developer manually `git checkout`'d a different branch in the main repo, or after another session left the repo root on its own feature branch. Also fires when the base branch recorded in WorktreeInfo has since been renamed or when the repo root is in detached-HEAD state (rev-parse --abbrev-ref HEAD returns 'HEAD').","commonSituations":"Concurrent agent sessions sharing one repo root where one session switched the root's branch; CI runs that checkout PR branches before invoking the merge; local development where the user switched branches in their editor while an ECC session is mid-merge; base_branch recorded as 'main' but repo default was renamed to 'master' or vice versa.","solutions":["Run `git -C <repo_root> checkout <base_branch>` (the value of worktree.base_branch) so the repo root matches what the session recorded, then retry merge_into_base.","If you intentionally want to merge into the currently checked-out branch instead, update the WorktreeInfo.base_branch field (or recreate the session) so it matches reality before calling merge.","If the repo root is in detached HEAD, checkout the named base branch explicitly to attach HEAD to a ref.","Avoid running concurrent operations that mutate the repo root's HEAD while a merge is in flight."],"exampleFix":"// before: merge assumes repo root is on base_branch\nmerge_into_base(&worktree)?;\n\n// after: ensure repo root is on base_branch first\nlet repo_root = std::env::current_dir()?;\nlet current = get_current_branch(&repo_root)?;\nif current != worktree.base_branch {\n    Command::new(\"git\")\n        .arg(\"-C\").arg(&repo_root)\n        .args([\"checkout\", &worktree.base_branch])\n        .status()?;\n}\nmerge_into_base(&worktree)?;","handlingStrategy":"validation","validationCode":"fn repo_root_on_base(worktree: &WorktreeInfo) -> Result<bool> {\n    let repo_root = base_checkout_path(worktree)?;\n    let current = get_current_branch(&repo_root)?;\n    Ok(current == worktree.base_branch)\n}\n\n// before merge_into_base:\nif !repo_root_on_base(&worktree)? {\n    Command::new(\"git\").arg(\"-C\").arg(&base_checkout_path(&worktree)?)\n        .args([\"checkout\", &worktree.base_branch]).status()?;\n}","typeGuard":"null","tryCatchPattern":"match merge_into_base(&worktree) {\n    Ok(o) => o,\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"is not checked out in repo root\") {\n            // surface the current vs expected branch to the user, offer checkout\n            return Err(e);\n        }\n        return Err(e);\n    }\n}","preventionTips":["Do not switch branches in the main repo while an ECC session is active — pin HEAD to base_branch.","Record base_branch at session creation and re-validate on every merge/rebase call.","Reject concurrent sessions that would mutate the shared repo root's HEAD.","In detached-HEAD roots, fail fast at session start rather than at merge time."],"tags":["git","worktree","merge","branch","state-mismatch"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}