{"record":{"id":"8fc52555e68ef7e0","repo":"gitbutlerapp/gitbutler","slug":"cannot-reorder-subject-branch-name-in-single-b","errorCode":null,"errorMessage":"Cannot reorder '{subject_branch_name}' in single-branch mode without branch order metadata","messagePattern":"Cannot reorder '(.+?)' in single-branch mode without branch order metadata","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/move_branch.rs","lineNumber":255,"sourceCode":"        let (source_stack, subject_segment) = &source;\n        let (destination_stack, _) = &destination;\n        let entrypoint = workspace.ref_name().map(ToOwned::to_owned);\n        // A branch that owns commits can only be reordered within its current stack in\n        // single-branch mode. Moving it across stacks would change commit ownership and needs a\n        // real rebase.\n        if !subject_segment.commits.is_empty() && !same_stack(source_stack, destination_stack) {\n            bail!(\"Moving a non-empty branch in single-branch mode is not yet supported\");\n        }\n        // Reordering same-target empty refs only changes which empty segment is displayed first.\n        // If their targets differ, however, the subject crosses commit-owning segments and its ref\n        // must move with it or those commits would be projected as belonging to the empty branch.\n        let move_requires_graph_update = !subject_segment.commits.is_empty()\n            || successful_rebase.reference_target(subject_branch_name)?\n                != successful_rebase.reference_target(target_branch_name)?;\n        let existing_order = {\n            let (_repo, meta) = successful_rebase.repo_and_meta_mut();\n            if !meta.can_persist_branch_stack_order() {\n                bail!(\n                    \"Cannot reorder '{subject_branch_name}' in single-branch mode without branch order metadata\"\n                );\n            }\n            // Reorder against the existing chain. A movable subject is always part of `branch_order`\n            // (that's what makes it a projected segment), so the first lookup normally succeeds. The\n            // target and entrypoint lookups are defensive fallbacks so that, should the projection ever\n            // surface a segment that isn't tracked yet, we extend the real chain instead of clobbering\n            // it down to just the moved refs.\n            match meta.branch_stack_order(subject_branch_name)? {\n                Some(order) => order,\n                None => match meta.branch_stack_order(target_branch_name)? {\n                    Some(order) => order,\n                    None => entrypoint\n                        .as_ref()\n                        .map(|entrypoint| meta.branch_stack_order(entrypoint.as_ref()))\n                        .transpose()?\n                        .flatten()\n                        .unwrap_or_else(|| stack_branch_order(source_stack)),","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/move_branch.rs#L237-L273","documentation":"In single-branch mode, reordering empty branches is persisted through branch-stack-order metadata. The guard fires when the RefMetadata implementation reports can_persist_branch_stack_order() == false, which for the legacy backend means the metadata database handle is absent or opened read-only. Without a writable order store the reorder would be silently lost, so the operation is refused.","triggerScenarios":"Calling the ad-hoc reorder path while meta is a read-only or database-less RefMetadata (but-meta legacy: db.is_none() || read_only), e.g. inspection tools, dry-run traversals, or tests with in-memory metadata.","commonSituations":"Opening a project in a read-only/preview context and then attempting a reorder; custom RefMetadata implementations that never implemented order persistence; unit tests using stub metadata hitting production code paths.","solutions":["Run the operation with a writable metadata backend (open the project database normally, not read-only).","If you implement RefMetadata yourself, implement the branch_stack_order family and return true from can_persist_branch_stack_order().","Skip reorder UI actions when the metadata handle is read-only."],"exampleFix":"// before\nlet outcome = move_branch(editor, &subject, &target)?;\n\n// after\nlet (_, meta) = successful_rebase.repo_and_meta_mut();\nensure!(meta.can_persist_branch_stack_order(), \"metadata read-only; reorder disabled\");\nlet outcome = move_branch(editor, &subject, &target)?;","handlingStrategy":"validation","validationCode":"let (_repo, meta) = successful_rebase.repo_and_meta_mut();\nif !meta.can_persist_branch_stack_order() {\n    // read-only or missing metadata backend: skip reorder actions\n    return Ok(disabled_outcome());\n}","typeGuard":null,"tryCatchPattern":"match move_branch(editor, &subject, &target) {\n    Err(err) if err.to_string().contains(\"without branch order metadata\") => {\n        Err(anyhow!(\"reorder requires a writable metadata store; reopen the project database\"))\n    }\n    other => other,\n}","preventionTips":["Open the project metadata read-write for any mutating operation.","If you implement RefMetadata, implement the branch_stack_order API surface before enabling reorders.","Gate reorder UI on can_persist_branch_stack_order()."],"tags":["gitbutler","single-branch-mode","metadata","read-only","rust"],"backgroundTag":"read-only-metadata-store","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}