Cover every remote in each candidate's rev list

Cursor and Greptile converged on one root cause from the previous round:
candidate revs stopped at origin (develop tried only develop and
origin/develop; a remote-default entry carried only its own rev), so the
name-level dedup discarded a same-name base living on another remote. A
fork-parent layout with develop only as upstream/develop, or a pruned
origin/main beside a live upstream/main, lost its base entirely.

revsFor(name) now expands to the local branch plus <remote>/<name> for
every remote (origin first), and all named candidates use it, which is
exactly what makes the dedup safe. Two failing-first tests cover the
upstream-only develop and the pruned-origin/live-upstream main shapes.

Prepared with AI assistance (Claude Code), directed by @pbakaus.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-07-26 18:23:56 -07:00
co-authored by Claude Code
parent 46f29ca8b3
commit 386d3e7051
2 changed files with 56 additions and 3 deletions
+10 -3
View File
@@ -131,6 +131,13 @@ function gitSignals(cwd) {
let baseRev = null;
if (!onIntegrationBranch) {
const upstream = asUpstream(run(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']));
// Every named candidate tries the local branch first, then that name on
// every remote (origin first). Covering all remotes up front is what
// makes the name-level dedup below safe: a develop or main that exists
// only as upstream/<name> still resolves even though origin's candidate
// claimed the name first.
const remoteOrder = ['origin', ...remotes.filter((name) => name !== 'origin')];
const revsFor = (name) => [name, ...remoteOrder.map((r) => `${r}/${name}`)];
const candidates = [];
const seen = new Set();
const addCandidate = (name, revs) => {
@@ -145,9 +152,9 @@ function gitSignals(cwd) {
// even when the platform default (origin/HEAD) was never flipped off
// main; an existing develop therefore outranks the remote default. This
// is #302's own repro shape, and repos without develop are unaffected.
addCandidate('develop', ['develop', 'origin/develop']);
for (const head of remoteHeads) addCandidate(head.name, [head.name, head.rev]);
for (const name of ['main', 'master']) addCandidate(name, [name, `origin/${name}`]);
addCandidate('develop', revsFor('develop'));
for (const head of remoteHeads) addCandidate(head.name, revsFor(head.name));
for (const name of ['main', 'master']) addCandidate(name, revsFor(name));
for (const c of candidates) {
const rev = c.revs.find((r) => run(['rev-parse', '--verify', '--quiet', r]) !== null);
if (rev) {