{"record":{"id":"668d6005e363d87e","repo":"gitbutlerapp/gitbutler","slug":"visible-worktree-worktree-name-head-changed-shap","errorCode":null,"errorMessage":"Visible worktree {worktree_name} HEAD changed shape during the edit: expected {}, got {}","messagePattern":"Visible worktree (.+?) HEAD changed shape during the edit: expected (.+?), got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-rebase/src/graph_rebase/materialize.rs","lineNumber":151,"sourceCode":"    /// one, validated against the shape recorded at editor creation.\n    pub(super) fn linked_checkout_specs(&self) -> Result<Vec<LinkedCheckoutSpec>> {\n        let mut specs = Vec::new();\n        for checkout in &self.checkouts {\n            let Checkout::Worktree {\n                worktree_name,\n                selector,\n                ref_name: expected_ref,\n                initial_head,\n                merge_base_override,\n            } = checkout\n            else {\n                continue;\n            };\n            let (target, actual_ref) = self\n                .checkout_target(*selector)?\n                .with_context(|| format!(\"Visible worktree {worktree_name} HEAD was removed\"))?;\n            if actual_ref.as_ref() != expected_ref.as_ref() {\n                bail!(\n                    \"Visible worktree {worktree_name} HEAD changed shape during the edit: \\\n                     expected {}, got {}\",\n                    expected_ref\n                        .as_ref()\n                        .map_or_else(|| \"detached\".into(), ToString::to_string),\n                    actual_ref\n                        .as_ref()\n                        .map_or_else(|| \"detached\".into(), ToString::to_string)\n                );\n            }\n            specs.push(LinkedCheckoutSpec {\n                name: worktree_name.clone(),\n                initial_head: *initial_head,\n                ref_name: expected_ref.clone(),\n                target,\n                merge_base_override: *merge_base_override,\n            });\n        }","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-rebase/src/graph_rebase/materialize.rs#L133-L169","documentation":"A companion check to the HEAD-drift guard while applying worktree checkouts: the editor compares the shape of HEAD — attached branch ref vs detached. If the worktree's HEAD switched between attached and detached (or to a differently-shaped ref) since the editor was created, it aborts rather than forcing a checkout onto a differently-shaped HEAD.","triggerScenarios":"During materialize, `checkout_target` reports a ref whose shape differs from the editor's expectation: the user ran `git checkout --detach` or `git switch <branch>` in the linked worktree mid-edit.","commonSituations":"Users experimenting in a linked worktree while an edit session is open; scripts detaching HEAD for inspection; tooling that checks out commits by id.","solutions":["Recreate the editor session after HEAD shape stabilizes","Avoid detaching or switching HEAD in linked worktrees while a graph edit is in progress","If the detached state is intentional, rebuild the plan against the current (detached) HEAD"],"exampleFix":"// before\neditor.materialize(...)?; // bails on attached/detached mismatch\n\n// after: detect the shape change before materializing\nlet (actual_ref, _) = editor.checkout_target(*selector)?;\nif actual_ref.as_ref() != expected_ref.as_ref() {\n    return rebuild_editor_and_retry(); // one is detached, the other is not\n}\neditor.materialize(...)?;","handlingStrategy":"validation","validationCode":"// Rust: detect attached/detached shape changes before materializing\nlet (actual_ref, _) = editor.checkout_target(*selector)?;\nif actual_ref.as_ref() != expected_ref.as_ref() {\n    // one side is a branch ref, the other detached — rebuild the editor\n    return recreate_editor();\n}","typeGuard":null,"tryCatchPattern":"Catch the bail, report which worktree changed shape (expected vs got), and rebuild the editor session from the current HEAD state rather than forcing the checkout.","preventionTips":["Avoid detaching HEAD or switching branches in linked worktrees during pending edits","Re-capture editor specs after any manual git operation in a linked worktree"],"tags":["worktree","detached-head","optimistic-concurrency","graph-rebase"],"backgroundTag":"optimistic-concurrency-conflict","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}