{"record":{"id":"91252282302b8b44","repo":"affaan-m/ECC","slug":"merge-blocked-between-left-branch-and-right-bra","errorCode":null,"errorMessage":"Merge blocked between {left_branch} and {right_branch} by {} conflict(s): {detail}","messagePattern":"Merge blocked between (.+?) and (.+?) by (.+?) conflict\\(s\\): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"ecc2/src/worktree/mod.rs","lineNumber":861,"sourceCode":"    } else {\n        Ok(WorktreeHealth::InProgress)\n    }\n}\n\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    }","sourceCodeStart":843,"sourceCodeEnd":879,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/worktree/mod.rs#L843-L879","documentation":"merge_into_base calls merge_readiness, and when the status is Conflicted it bails with `readiness.summary`. The summary string is the conflict report assembled by merge_readiness_for_branches (count of conflicts plus up to three path examples). This is a guarded refusal to perform a merge that git has already predicted will conflict.","triggerScenarios":"Calling merge_into_base on a worktree branch whose merge-tree preview against base_branch produced conflict paths (both sides modified the same regions). The bail happens before any working-tree mutation, so the repo is left untouched.","commonSituations":"Long-lived feature branch behind a rebased main; two agents working in different worktrees edited the same files and one tries to merge first; auto-merge pipeline hitting a genuinely conflicting change set.","solutions":["Rebase or manually resolve the conflicts before merging: `git -C <worktree> rebase <base>`.","Use branch_conflict_preview to surface the conflicting paths to the user before they retry.","After resolving, re-run merge_readiness to confirm status == Ready before calling merge_into_base.","If the conflicts are spurious from stale refs, `git fetch` and re-derive readiness."],"exampleFix":"// before\nlet outcome = merge_into_base(&worktree)?;\n\n// after\nlet readiness = merge_readiness(&worktree)?;\nif readiness.status == MergeReadinessStatus::Conflicted {\n    return Err(anyhow!(\"merge has {} conflict(s): {}\",\n        readiness.conflicts.len(), readiness.conflicts.join(\", \")));\n}\nlet outcome = merge_into_base(&worktree)?;","handlingStrategy":"validation","validationCode":"use crate::worktree::{merge_readiness, MergeReadinessStatus};\n\nlet readiness = merge_readiness(&worktree)?;\nif readiness.status == MergeReadinessStatus::Conflicted {\n    return Err(anyhow!(\n        \"merge blocked by {} conflict(s): {}\",\n        readiness.conflicts.len(),\n        readiness.conflicts.join(\", \")\n    ));\n}\nlet outcome = merge_into_base(&worktree)?;","typeGuard":"fn is_merge_ready(r: &MergeReadiness) -> bool {\n    r.status == MergeReadinessStatus::Ready\n}","tryCatchPattern":"match merge_into_base(&worktree) {\n    Ok(outcome) => Ok(outcome),\n    Err(e) => {\n        let m = format!(\"{e:#}\");\n        if m.starts_with(\"Merge blocked\") {\n            // surface the conflict list to the user; suggest rebase or manual resolve\n        }\n        Err(e)\n    }\n}","preventionTips":["Always call `merge_readiness` before `merge_into_base` and gate the merge button on Ready.","Rebase long-lived branches onto base regularly to shrink the conflict surface.","Use `branch_conflict_preview` to show users the conflicting paths before they retry.","After resolving conflicts, re-run `merge_readiness` to confirm Ready before retrying the merge."],"tags":["git","worktree","merge","conflict","state-machine"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}