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
- Verify with git itself: `git merge-base a b` — if git also finds nothing, the histories are genuinely disjoint.
- For shallow clones, deepen before asking: `git fetch --unshallow` or `--depth=<n>`.
- Double-check each revision string resolves where you think (`git rev-parse <rev>`).
- 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
- Pre-check ancestry with `git merge-base a b` before scripting dependent logic.
- Unshallow clones before computing merge-bases across long histories.
- Reject cross-repository revision pairs at input validation time.
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
- No merge-base found between '{ref_name}' and its tracking br
- Failed to parse diff header
- BUG: It should not be possible to omit sources
- implement list and call recursively
- Refusing to install symlink onto existing non-symlink at '{U
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/cbfc09124feca2f7.
Report an issue: GitHub.