{"record":{"id":"e4a9baa867340634","repo":"gitbutlerapp/gitbutler","slug":"moving-a-non-empty-branch-in-single-branch-mode-is","errorCode":null,"errorMessage":"Moving a non-empty branch in single-branch mode is not yet supported","messagePattern":"Moving a non-empty branch in single-branch mode is not yet supported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/move_branch.rs","lineNumber":244,"sourceCode":"    /// The reordered chain is returned in [`Outcome::branch_stack_order`] for the caller to persist\n    /// (via [`RefMetadata::set_branch_stack_order`]) rather than being written here, so callers can\n    /// skip persistence for dry-run previews.\n    fn move_branch_in_single_branch_mode<'ws, 'meta, M: RefMetadata>(\n        mut successful_rebase: SuccessfulRebase<'ws, 'meta, M>,\n        workspace: but_graph::Workspace,\n        source: WorkspaceSegmentContext,\n        destination: WorkspaceSegmentContext,\n        subject_branch_name: &FullNameRef,\n        target_branch_name: &FullNameRef,\n    ) -> anyhow::Result<Outcome<'ws, 'meta, M>> {\n        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","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/move_branch.rs#L226-L262","documentation":"In an ad-hoc (single-branch mode) workspace, moving a branch that owns commits across stacks would change commit ownership and requires a real rebase, which this code path deliberately does not perform. The guard fires only when the subject segment is non-empty and source and destination are different stacks. Reordering within the same stack remains allowed.","triggerScenarios":"Calling move_branch on an ad-hoc workspace where subject_segment.commits is non-empty and same_stack(source_stack, destination_stack) is false — i.e., dragging a commit-owning virtual branch onto a branch in another stack.","commonSituations":"Users of the single-branch (Lite/ad-hoc) mode trying to reorganize branches across stacks in the UI; tests exercising cross-stack moves against ad-hoc fixtures.","solutions":["Switch the project to a managed workspace (workspace commit) where cross-stack moves are supported.","Move the commits out of the subject branch first (or integrate them), then move the now-empty branch.","Keep the move within the same stack if only reordering was intended."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let (source_stack, subject_segment) = source;\nlet (destination_stack, _) = destination;\nif !subject_segment.commits.is_empty()\n    && !same_stack(source_stack, destination_stack)\n{\n    // cross-stack move of a commit-owning branch: unsupported in single-branch mode\n    return show_mode_notice();\n}","typeGuard":null,"tryCatchPattern":"match move_branch(editor, &subject, &target) {\n    Err(err) if err.to_string().contains(\"non-empty branch in single-branch mode\") => {\n        ui::info(\"Switch to a managed workspace to move branches across stacks\");\n        Ok(default_outcome())\n    }\n    other => other,\n}","preventionTips":["Hide cross-stack move targets when the subject owns commits in ad-hoc mode.","Offer 'move commits' or 'integrate' instead of branch move in single-branch mode.","Test ad-hoc move flows with commit-owning branches to catch UX gaps."],"tags":["gitbutler","single-branch-mode","move-branch","unsupported","rust"],"backgroundTag":"unsupported-operation-mode","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}