alibaba/open-code-review · error
cannot find merge-base between %s and %s
Error message
cannot find merge-base between %s and %s
What it means
In ModeRange, GetDiff first computes the merge-base of p.from and p.to via MergeBase; if git returns no merge-base (empty string), the range diff is meaningless and this error is returned. It means the two revisions share no common ancestor, so 'from..to' style comparison cannot be performed.
Source
Thrown at internal/diff/git.go:181
// MergeBase returns the computed merge-base commit hash for range mode.
func (p *Provider) MergeBase(ctx context.Context) string {
if p.mode != ModeRange || p.mergeBase != "" {
return p.mergeBase
}
p.mergeBase = p.computeMergeBase(ctx, p.from, p.to)
return p.mergeBase
}
// GetDiff returns all changes as parsed model.Diff structs.
func (p *Provider) GetDiff(ctx context.Context) ([]model.Diff, error) {
var combined strings.Builder
switch p.mode {
case ModeRange:
base := p.MergeBase(ctx)
if base == "" {
return nil, fmt.Errorf("cannot find merge-base between %s and %s", p.from, p.to)
}
out, stderr, err := p.runGitSplit(ctx, "-c", "core.quotepath=false", "diff", "--no-ext-diff", "--no-textconv", "--find-renames", "--src-prefix=a/", "--dst-prefix=b/", "--no-color", "-U"+fmt.Sprint(DiffContextLines), "--end-of-options", base, p.to, "--")
if err != nil {
return nil, gitFailure("git diff", stderr, err)
}
combined.WriteString(out)
case ModeCommit:
// --diff-merges=first-parent: for merge commits, plain `git show`
// emits a combined diff ("diff --cc"), which ParseDiffText cannot
// parse — the commit would silently yield zero reviewable diffs.
// Diffs against the first parent instead, in regular unified format.
out, stderr, err := p.runGitSplit(ctx, "-c", "core.quotepath=false", "show", "--no-ext-diff", "--no-textconv", "--find-renames", "--src-prefix=a/", "--dst-prefix=b/", "--no-color", "--diff-merges=first-parent", "-U"+fmt.Sprint(DiffContextLines), "--end-of-options", p.commit)
if err != nil {
return nil, gitFailure("git show", stderr, err)
}
combined.WriteString(out)
View on GitHub (pinned to 5cf97d0d15)
Solutions
- Check both refs exist (git rev-parse <from> <to>) and are spelled correctly
- Use refs that share history, or unshallow the clone (git fetch --unshallow)
- Switch the diff mode to a workspace or commit diff instead of a range diff
- Verify ancestry with git merge-base <from> <to> before invoking
Example fix
// before
base := p.MergeBase(ctx); if base == "" { return nil, ... } // unrelated refs
// after
// pick a common ancestor
params.from = "main"; params.to = "feature" // both on shared history
base := p.MergeBase(ctx); if base == "" { return nil, ... } Defensive patterns
Strategy: validation
Validate before calling
func haveMergeBase(ctx context.Context, dir, from, to string) bool {
out, err := exec.CommandContext(ctx, "git", "-C", dir, "merge-base", from, to).Output()
return err == nil && len(bytes.TrimSpace(out)) > 0
} Try / catch
d, err := differ.GetDiff(ctx, p)
if err != nil && strings.Contains(err.Error(), "cannot find merge-base") {
// fall back to two-dot compare or workspace diff
} Prevention
- Unshallow CI clones (fetch-depth: 0) so merge-bases resolve
- Verify both refs share history before requesting a range diff
- Sanitize/refuse user-supplied refs that fail git rev-parse
When it happens
Trigger: Calling GetDiff on a params with mode=ModeRange where from and to are unrelated histories (e.g. two orphan branches), or where a revision name is invalid enough that git merge-base silently returns nothing.
Common situations: Comparing a fresh orphan branch against main; typo'd ref names; comparing across repos or after history rewrites that severed ancestry; CI shallow clones missing shared history.
Related errors
- preview failed: %w
- load diffs: %w
- get diffs: %w
- resolve merge-base between %q and %q
- untracked file diff failed: %w
AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02).
Data as JSON: /api/errors/63fba6fd4717072e.
Report an issue: GitHub.