gitbutlerapp/gitbutler · error

Visible worktree {worktree_name} HEAD changed shape during t

Error message

Visible worktree {worktree_name} HEAD changed shape during the edit: expected {}, got {}

What it means

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.

Source

Thrown at crates/but-rebase/src/graph_rebase/materialize.rs:151

    /// one, validated against the shape recorded at editor creation.
    pub(super) fn linked_checkout_specs(&self) -> Result<Vec<LinkedCheckoutSpec>> {
        let mut specs = Vec::new();
        for checkout in &self.checkouts {
            let Checkout::Worktree {
                worktree_name,
                selector,
                ref_name: expected_ref,
                initial_head,
                merge_base_override,
            } = checkout
            else {
                continue;
            };
            let (target, actual_ref) = self
                .checkout_target(*selector)?
                .with_context(|| format!("Visible worktree {worktree_name} HEAD was removed"))?;
            if actual_ref.as_ref() != expected_ref.as_ref() {
                bail!(
                    "Visible worktree {worktree_name} HEAD changed shape during the edit: \
                     expected {}, got {}",
                    expected_ref
                        .as_ref()
                        .map_or_else(|| "detached".into(), ToString::to_string),
                    actual_ref
                        .as_ref()
                        .map_or_else(|| "detached".into(), ToString::to_string)
                );
            }
            specs.push(LinkedCheckoutSpec {
                name: worktree_name.clone(),
                initial_head: *initial_head,
                ref_name: expected_ref.clone(),
                target,
                merge_base_override: *merge_base_override,
            });
        }

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Recreate the editor session after HEAD shape stabilizes
  2. Avoid detaching or switching HEAD in linked worktrees while a graph edit is in progress
  3. If the detached state is intentional, rebuild the plan against the current (detached) HEAD

Example fix

// before
editor.materialize(...)?; // bails on attached/detached mismatch

// after: detect the shape change before materializing
let (actual_ref, _) = editor.checkout_target(*selector)?;
if actual_ref.as_ref() != expected_ref.as_ref() {
    return rebuild_editor_and_retry(); // one is detached, the other is not
}
editor.materialize(...)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: detect attached/detached shape changes before materializing
let (actual_ref, _) = editor.checkout_target(*selector)?;
if actual_ref.as_ref() != expected_ref.as_ref() {
    // one side is a branch ref, the other detached — rebuild the editor
    return recreate_editor();
}

Try / catch

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.

Prevention

When it happens

Trigger: 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.

Common situations: Users experimenting in a linked worktree while an edit session is open; scripts detaching HEAD for inspection; tooling that checks out commits by id.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/668d6005e363d87e. Report an issue: GitHub.