gitbutlerapp/gitbutler · error · anyhow::Error

No merge-base found for revisions: {}

Error message

No merge-base found for revisions: {}

What it means

`but revision merge-base` computes an octopus merge-base from a commit graph built from the given revisions and their tips; if the graph yields no common ancestor commit (`merge_base` is None), it bails listing the revisions. In practice this means the revisions share no history — unrelated root commits — or the graph traversal couldn't connect them via the configured tips.

Source

Thrown at crates/but-debug/src/command/revision.rs:139

            .entered();
        graph
            .find_merge_base_octopus(segments)
            .map(|segment_id| {
                graph
                    .tip_skip_empty(segment_id)
                    .map(|commit| commit.id)
                    .with_context(|| {
                        format!(
                            "BUG: Segment {segment_id:?} does not contain a reachable tip commit"
                        )
                    })
            })
            .transpose()
            .context("Failed to compute octopus merge-base from graph")?
    };

    let Some(merge_base) = merge_base else {
        bail!(
            "No merge-base found for revisions: {}",
            merge_base_args.revisions.join(", ")
        );
    };
    writeln!(out, "{merge_base}")?;

    Ok(())
}

fn args_to_tips(repo: &gix::Repository, graph_args: &RevisionGraphArgs) -> Result<Vec<Tip>> {
    let mut tips = Vec::new();

    if let Some(tip) = graph_args
        .target_ref
        .as_deref()
        .map(|target_ref| {
            let mut reference = repo
                .find_reference(target_ref)

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Verify with git itself: `git merge-base a b` — if git also finds nothing, the histories are genuinely disjoint.
  2. For shallow clones, deepen before asking: `git fetch --unshallow` or `--depth=<n>`.
  3. Double-check each revision string resolves where you think (`git rev-parse <rev>`).
  4. If histories truly share nothing, skip merge-base-dependent logic for these revisions (guard on the error).

Example fix

# before
but revision merge-base feature-a repo-b-branch   # unrelated histories

# after — verify, then decide
git merge-base feature-a repo-b-branch || echo 'no common ancestor: disjoint histories'
git fetch --unshallow && but revision merge-base feature-a repo-b-branch
Defensive patterns

Strategy: try-catch

Validate before calling

# Shell — pre-check with git; empty output means no merge-base exists
git merge-base -- "$a" "$b" >/dev/null 2>&1 || { echo 'no common ancestor' >&2; exit 0; }
but revision merge-base "$a" "$b"

Try / catch

match merge_base_cmd(&repo, &[&a, &b], &mut out) {
    Ok(()) => {}
    Err(err) if err.to_string().contains("No merge-base found") => {
        // disjoint histories: skip merge-base-dependent behavior for this pair
        tracing::info!("{a} and {b} share no ancestry; skipping");
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Running `but revision merge-base a b` where `a` and `b` have disjoint histories (separate roots, e.g. repositories merged with `--allow-unrelated-histories` splits, grafted/shallow clones missing the connecting history, or substituted refs from different repos).

Common situations: Shallow clones where the common ancestor is outside the shallow boundary; grafts/replace refs hiding the real ancestry; comparing branches from unrelated repositories; typo'd revisions that parse but refer to unrelated objects.

Related errors


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