gitbutlerapp/gitbutler · error
Moving a non-empty branch in single-branch mode is not yet s
Error message
Moving a non-empty branch in single-branch mode is not yet supported
What it means
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.
Source
Thrown at crates/but-workspace/src/branch/move_branch.rs:244
/// The reordered chain is returned in [`Outcome::branch_stack_order`] for the caller to persist
/// (via [`RefMetadata::set_branch_stack_order`]) rather than being written here, so callers can
/// skip persistence for dry-run previews.
fn move_branch_in_single_branch_mode<'ws, 'meta, M: RefMetadata>(
mut successful_rebase: SuccessfulRebase<'ws, 'meta, M>,
workspace: but_graph::Workspace,
source: WorkspaceSegmentContext,
destination: WorkspaceSegmentContext,
subject_branch_name: &FullNameRef,
target_branch_name: &FullNameRef,
) -> anyhow::Result<Outcome<'ws, 'meta, M>> {
let (source_stack, subject_segment) = &source;
let (destination_stack, _) = &destination;
let entrypoint = workspace.ref_name().map(ToOwned::to_owned);
// A branch that owns commits can only be reordered within its current stack in
// single-branch mode. Moving it across stacks would change commit ownership and needs a
// real rebase.
if !subject_segment.commits.is_empty() && !same_stack(source_stack, destination_stack) {
bail!("Moving a non-empty branch in single-branch mode is not yet supported");
}
// Reordering same-target empty refs only changes which empty segment is displayed first.
// If their targets differ, however, the subject crosses commit-owning segments and its ref
// must move with it or those commits would be projected as belonging to the empty branch.
let move_requires_graph_update = !subject_segment.commits.is_empty()
|| successful_rebase.reference_target(subject_branch_name)?
!= successful_rebase.reference_target(target_branch_name)?;
let existing_order = {
let (_repo, meta) = successful_rebase.repo_and_meta_mut();
if !meta.can_persist_branch_stack_order() {
bail!(
"Cannot reorder '{subject_branch_name}' in single-branch mode without branch order metadata"
);
}
// Reorder against the existing chain. A movable subject is always part of `branch_order`
// (that's what makes it a projected segment), so the first lookup normally succeeds. The
// target and entrypoint lookups are defensive fallbacks so that, should the projection ever
// surface a segment that isn't tracked yet, we extend the real chain instead of clobberingView on GitHub (pinned to caf1f223d3)
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.
Defensive patterns
Strategy: validation
Validate before calling
let (source_stack, subject_segment) = source;
let (destination_stack, _) = destination;
if !subject_segment.commits.is_empty()
&& !same_stack(source_stack, destination_stack)
{
// cross-stack move of a commit-owning branch: unsupported in single-branch mode
return show_mode_notice();
} Try / catch
match move_branch(editor, &subject, &target) {
Err(err) if err.to_string().contains("non-empty branch in single-branch mode") => {
ui::info("Switch to a managed workspace to move branches across stacks");
Ok(default_outcome())
}
other => other,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Tearing off a branch requires a workspace common base
- Cannot move branch {subject_branch_name} onto itself
- Cannot reorder '{subject_branch_name}' in single-branch mode
- Couldn't find target branch to move in workspace with refere
- Failed to communicate with LM Studio server: ${error instanc
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/e4a9baa867340634.
Report an issue: GitHub.