gitbutlerapp/gitbutler · error

commits cannot be moved to the merge base

Error message

commits cannot be moved to the merge base

What it means

In the status TUI's move mode (crates/but/src/command/legacy/status/tui/app/move_mode.rs:393), the selected target is converted to a move::MoveTarget; the merge-base lane (MoveTarget::MergeBase) is deliberately rejected because commits can only be moved onto a branch tip, a commit (with an insert side), or a linked-worktree tip — never onto the merge base itself.

Source

Thrown at crates/but/src/command/legacy/status/tui/app/move_mode.rs:393

        Ok(())
    }
}

fn move_target(
    target: MoveTarget<'_>,
    insert_side: InsertSide,
) -> anyhow::Result<r#move::MoveTarget> {
    Ok(match target {
        MoveTarget::Branch { name } => r#move::MoveTarget::BranchTip {
            name: Category::LocalBranch.to_full_name(name)?,
        },
        MoveTarget::Commit(commit) => r#move::MoveTarget::Commit {
            commit,
            side: targeting::Side::from(insert_side),
        },
        MoveTarget::WorktreeTip(name) => r#move::MoveTarget::BranchTip { name },
        MoveTarget::MergeBase => anyhow::bail!("commits cannot be moved to the merge base"),
    })
}

fn move_with(
    ctx: &mut Context,
    move_op: MoveOperation,
) -> anyhow::Result<Option<SelectAfterReload>> {
    let mut guard = ctx.exclusive_worktree_access();
    let mut meta = ctx.meta()?;
    let (outcome, _ws) = r#move::run(ctx, &mut meta, guard.write_permission(), move_op)?;

    Ok(match outcome {
        MoveOperationOutcome::Commits { moved_commits, .. } => {
            Some(SelectAfterReload::Commit(moved_commits.head.commit_id))
        }
        MoveOperationOutcome::Changes { new_commit, .. } => {
            Some(SelectAfterReload::Commit(new_commit.commit_id))
        }

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Choose a concrete commit or branch tip as the move target.
  2. To move a commit to the bottom of a stack, target the first commit of that stack instead.
  3. Fix the selection UI so the merge-base lane is non-selectable in move mode, preventing the error entirely.

Example fix

// before
let target = MoveTarget::MergeBase; // then converted -> bails

// after
let target = MoveTarget::Commit(first_commit_of_stack);
Defensive patterns

Strategy: validation

Validate before calling

if matches!(target, MoveTarget::MergeBase) {
    // exclude merge-base lanes from selectable move targets in the UI
    return Ok(());
}

Type guard

fn is_valid_move_target(target: &MoveTarget<'_>) -> bool {
    !matches!(target, MoveTarget::MergeBase)
}

Try / catch

if let Err(err) = move_with(&mut ctx, move_op) {
    if err.to_string().contains("merge base") {
        // re-enter move mode and prompt for a commit/branch-tip target
    } else { return Err(err); }
}

Prevention

When it happens

Trigger: Entering move mode and accepting the merge-base/merge-commit row as the destination; a keybinding or selection handler that allows the merge-base heading to be picked as a move target.

Common situations: Users trying to move a commit to the very bottom of a stack; new key handlers or fuzzy selectors that do not filter out the merge-base lane.

Related errors


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