{"record":{"id":"656a0e1102452159","repo":"alibaba/open-code-review","slug":"resolve-merge-base-between-q-and-q","errorCode":null,"errorMessage":"resolve merge-base between %q and %q","messagePattern":"resolve merge-base between %q and %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/identity.go","lineNumber":87,"sourceCode":"\tswitch {\n\tcase args.Commit != \"\":\n\t\thead, err := resolveCommitHead(ctx, args, args.Commit)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &diff.InputResolution{ResolvedHead: head}, nil\n\tcase args.From != \"\" && args.To != \"\":\n\t\tfrom, err := resolveCommitHead(ctx, args, args.From)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\thead, err := resolveCommitHead(ctx, args, args.To)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresolved := diff.NewProvider(args.RepoDir, from, head, args.GitRunner).ResolveInput(ctx)\n\t\tif resolved.ResolvedBase == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"resolve merge-base between %q and %q\", args.From, args.To)\n\t\t}\n\t\treturn &resolved, nil\n\tdefault:\n\t\treturn nil, nil\n\t}\n}\n\nfunc resolveCommitHead(ctx context.Context, args Args, ref string) (string, error) {\n\thead := diff.NewCommitProvider(args.RepoDir, ref, args.GitRunner).ResolveInput(ctx).ResolvedHead\n\tif head == \"\" {\n\t\treturn \"\", fmt.Errorf(\"resolve commit %q\", ref)\n\t}\n\treturn head, nil\n}\n\n// runIdentity reads the identity off the agent's current selection.\n//\n// It is only meaningful once the selection is final: sourceArtifactSHA256 hashes","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/agent/identity.go#L69-L105","documentation":"resolveInputBeforeDiff throws this when, in From/To range mode, the merge-base of the two resolved commits comes back empty from diff.Provider.ResolveInput. An empty ResolvedBase means git merge-base found no common ancestor, so no reviewable range exists between From and To. This happens before diffs are loaded, during resume/admission identity resolution.","triggerScenarios":"args.From and args.To resolve to commits on unrelated histories (no common ancestor) — e.g. comparing a commit from an orphan branch or two unrelated roots; also possible if the provider silently failed to run merge-base.","commonSituations":"Reviewing a PR between a squashed-rebase orphan and main; repos created by subtree merge with unrelated roots; --from/--to pointing at tags on diverged root commits after history rewrites.","solutions":["Verify a common ancestor exists: 'git merge-base <from> <to>' must succeed in RepoDir","Choose From/To that share history (e.g. merge-base with main, or use main as From)","If histories are intentionally unrelated, run the review in commit mode (args.Commit) instead of range mode","Fetch all relevant branches so the merge-base commit is not missing locally"],"exampleFix":"// before\nresolved, err := agent.ResolveIdentity(ctx, agent.Args{From: \"orphan-root\", To: \"main\"})\n// after: use the real merge point as From\nresolved, err := agent.ResolveIdentity(ctx, agent.Args{From: \"main~1\", To: \"main\"})","handlingStrategy":"validation","validationCode":"func hasCommonAncestor(repoDir, from, to string) bool {\n    return execGit(repoDir, \"merge-base\", \"--is-ancestor\", from, to) == nil ||\n           execGit(repoDir, \"merge-base\", from, to) == nil\n}","typeGuard":null,"tryCatchPattern":"sealed, err := agent.ResolveIdentity(ctx, args)\nif err != nil && strings.HasPrefix(err.Error(), \"resolve merge-base\") {\n    log.Warnf(\"unrelated histories for %q..%q; falling back to commit mode\", args.From, args.To)\n    args.Commit = args.To\n    sealed, err = agent.ResolveIdentity(ctx, args)\n}","preventionTips":["Run 'git merge-base <from> <to>' before range mode to confirm shared history","Don't mix orphan-branch commits into range reviews","Fetch all relevant branches so merge-base objects exist locally","Prefer commit mode for reviews of rewritten/unrelated histories"],"tags":["git","merge-base","range-review"],"backgroundTag":"git-no-common-ancestor","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}