Skip to content

Commit 453efcf

Browse files
theCodeDriftclaude
andcommitted
fix(cli): keep every url of a precedence remote in owner resolution
listRemoteOwnerUrls used find() to pull origin/upstream, taking only the first remote.<name>.url. Git allows several urls per remote (e.g. `git remote set-url --add`), so a second origin/upstream url was dropped and could miss a matching org owner. Collect all urls per precedence remote with flatMap; dedup already handles repeats. Reported by Copilot review on #55. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 79ba8c8 commit 453efcf

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

‎packages/cli/src/util/git-remote.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,12 @@ const REMOTE_PRECEDENCE = ["origin", "upstream"];
142142
export async function listRemoteOwnerUrls(cwd: string): Promise<string[]> {
143143
const remotes = await listRemoteConfig(cwd);
144144
const ordered = [
145-
...REMOTE_PRECEDENCE.map((name) =>
146-
remotes.find((remote) => remote.name === name)
147-
).filter((remote) => remote !== undefined),
145+
// A remote can carry several `remote.<name>.url` entries (e.g. `git remote
146+
// set-url --add`), so collect ALL of a precedence remote's urls, not just
147+
// the first — otherwise a second origin/upstream url is dropped entirely.
148+
...REMOTE_PRECEDENCE.flatMap((name) =>
149+
remotes.filter((remote) => remote.name === name)
150+
),
148151
...remotes.filter((remote) => !REMOTE_PRECEDENCE.includes(remote.name)),
149152
];
150153

‎packages/cli/test/git-remote.test.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,31 @@ describe("listRemoteOwnerUrls", () => {
128128
expect(await listRemoteOwnerUrls(directory)).toEqual([]);
129129
});
130130

131+
it("keeps every url of a precedence remote (multi-url origin)", async () => {
132+
// `git remote set-url --add` gives origin a second url; both owners must be
133+
// considered, in order, rather than the first winning outright.
134+
await execFileAsync(
135+
"git",
136+
["remote", "add", "origin", "git@github.com:primary/widgets.git"],
137+
{ cwd: directory }
138+
);
139+
await execFileAsync(
140+
"git",
141+
[
142+
"remote",
143+
"set-url",
144+
"--add",
145+
"origin",
146+
"https://github.com/secondary/widgets.git",
147+
],
148+
{ cwd: directory }
149+
);
150+
expect(await listRemoteOwnerUrls(directory)).toEqual([
151+
"https://github.com/primary",
152+
"https://github.com/secondary",
153+
]);
154+
});
155+
131156
it("matches a github remote given in ssh:// url form", async () => {
132157
// Regression guard: the old regex canonicalizer threw on ssh:// remotes, so
133158
// this repo would have resolved to no current-org context.

0 commit comments

Comments
 (0)