From 7ae6f7347813d05bee5e65f913eabfeb34b86e3c Mon Sep 17 00:00:00 2001 From: Victor Vogel Date: Tue, 10 Nov 2026 04:18:10 +0000 Subject: [PATCH] fix(forgejo): a fork's same-named branch no longer hides the branch's own pull request Co-Authored-By: Claude Opus 5.5 --- .../ForgejoSourceControlProvider.test.ts | 57 +++++++++++++++++++ .../server/ForgejoSourceControlProvider.ts | 5 ++ 2 files changed, 62 insertions(+) diff --git a/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.test.ts b/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.test.ts index fbfe278b32ba4..e78898a258107 100644 --- a/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.test.ts +++ b/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.test.ts @@ -1913,3 +1913,60 @@ it.effect( } }), ); + +it.effect("looks up a branch's pull requests past a fork's newer same-named branch", () => { + const pull = (number: number, owner: string) => ({ + number, + title: `PR ${number}`, + html_url: `https://forgejo.test/maria/project/pulls/${number}`, + state: "open", + merged: false, + head: { + ref: "feature", + sha: `head-${number}`, + repo: { full_name: `${owner}/project`, owner: { login: owner } }, + }, + base: { + ref: "main", + sha: "base", + repo: { full_name: "maria/project", owner: { login: "maria" } }, + }, + }); + // Forgejo's `head` filter matches the branch name in every fork, newest first. + const page = [pull(9, "octocat"), pull(4, "maria")]; + return Effect.gen(function* () { + const provider = yield* ForgejoSourceControlProvider.make; + // GitManager reads one open result when a host gives no probe. + const limit = + provider.headBranchProbe?.({ headSelectors: ["feature"], state: "open" }).limit ?? 1; + const listed = yield* provider.listChangeRequests({ + cwd: "/repo", + headSelector: "feature", + state: "open", + limit, + }); + assert.deepStrictEqual( + listed.map((pullRequest) => pullRequest.number), + [9, 4], + ); + }).pipe( + Effect.provide( + Layer.mergeAll( + Layer.succeed(FileSystem.FileSystem, FileSystem.makeNoop({})), + TestSourceControlHost.layer(), + Layer.mock(ForgejoCli.ForgejoCli)({ + resolveRepository: () => + Effect.succeed({ + login: "forgejo.test", + repository: "maria/project", + baseUrl: "https://forgejo.test", + }), + api: (input) => + encodeJsonEffect( + new URLSearchParams(input.path.split("?")[1]).get("page") === "1" ? page : [], + ).pipe(Effect.orDie, Effect.map(processOutput)), + }), + ), + ), + ); +}); diff --git a/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.ts b/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.ts index 155d9734bb915..a6e20cb9ba1c1 100644 --- a/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.ts +++ b/packages/source-control-forgejo/src/server/ForgejoSourceControlProvider.ts @@ -327,6 +327,11 @@ export const make = Effect.gen(function* () { repositoryNameFromRemoteUrl, refineRepositoryIdentity, resolveRemoteWebHost, + // Forgejo's `head` filter matches the branch name in every fork, so read a full page. + headBranchProbe: ({ headSelectors }) => ({ + headSelectors: headSelectors.filter((selector) => !selector.includes(":")), + limit: 100, + }), listChangeRequests: Effect.fn("ForgejoSourceControlProvider.listChangeRequests")( function* (input) { const repo = yield* cli.resolveRepository(input);