gitbutlerapp/gitbutler · error

Cannot cherry-pick above a reference

Error message

Cannot cherry-pick above a reference

What it means

cherry_pick_commits rejects the combination RelativeTo::Reference(_) with InsertSide::Above: inserting commits above a reference would require rewriting what the ref points at in a way the rebase editor model does not support for this operation. Inserting below a reference, or above/below a commit, is fine. The check runs before any selector resolution.

Source

Thrown at crates/but-workspace/src/commit/cherry_pick.rs:40

pub fn cherry_pick_commits<'ws, 'meta, M: RefMetadata>(
    mut editor: Editor<'ws, 'meta, M>,
    source_commits: impl IntoIterator<Item = gix::ObjectId>,
    relative_to: RelativeTo,
    side: InsertSide,
) -> anyhow::Result<(SuccessfulRebase<'ws, 'meta, M>, Vec<Selector>)> {
    let mut seen = HashSet::new();
    let sources = source_commits
        .into_iter()
        .filter(|id| seen.insert(*id))
        .collect::<Vec<_>>();
    if sources.is_empty() {
        bail!("No commits were provided to cherry-pick")
    }
    if matches!(
        (&relative_to, side),
        (RelativeTo::Reference(_), InsertSide::Above)
    ) {
        bail!("Cannot cherry-pick above a reference")
    }

    let target = relative_to.to_selector(&editor)?;

    let mut inserted_selectors = Vec::with_capacity(sources.len());
    let mut previous_selector = None;
    for source in sources {
        // Give the copy its own change ID, retaining all other metadata.
        let mut template = editor.find_commit(source)?;
        let mut headers = Headers::try_from_commit(&template.inner).unwrap_or_default();
        headers.change_id = Headers::from_config(&editor.repo().config_snapshot()).change_id;
        headers.set_in_commit(&mut template.inner);
        let template_id = editor.new_commit(template, DateMode::CommitterUpdateAuthorKeep)?;

        let (anchor, insert_side) = match previous_selector {
            Some(selector) => (selector, InsertSide::Above),
            None => (target, side),
        };

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Use InsertSide::Below when the anchor is a reference, or anchor Above on a commit instead.
  2. Disable the 'above' placement option in the UI when the drop target is a branch/reference.
  3. Validate the (relative_to, side) pair before invoking the API.

Example fix

// before
cherry_pick_commits(editor, ids, RelativeTo::Reference(target_ref), InsertSide::Above)?;

// after
cherry_pick_commits(editor, ids, RelativeTo::Reference(target_ref), InsertSide::Below)?;
Defensive patterns

Strategy: validation

Validate before calling

if matches!((&relative_to, side), (RelativeTo::Reference(_), InsertSide::Above)) {
    // invalid combination: clamp to Below or reject before calling
    return Err(anyhow!("cannot insert above a reference"));
}

Type guard

fn cherry_pick_args_valid(relative_to: &RelativeTo, side: InsertSide) -> bool {
    !matches!((relative_to, side), (RelativeTo::Reference(_), InsertSide::Above))
}

Prevention

When it happens

Trigger: Calling cherry_pick_commits(editor, ids, RelativeTo::Reference(r), InsertSide::Above) — any reference anchor with the Above side.

Common situations: UIs exposing 'insert above' generically for both commit and branch targets; parameter mapping bugs that reuse InsertSide::Above for a reference target.

Related errors


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