diff --git a/apps/server/src/git/GitManager.test.ts b/apps/server/src/git/GitManager.test.ts index 530e0488cf3..3a1e30868ed 100644 --- a/apps/server/src/git/GitManager.test.ts +++ b/apps/server/src/git/GitManager.test.ts @@ -2388,6 +2388,80 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { }), ); + it.effect("creates fork PRs from origin branches against an upstream base repo", () => + Effect.gen(function* () { + const repoDir = yield* makeTempDir("t3code-git-manager-"); + yield* initRepo(repoDir); + const forkDir = yield* createBareRemote(); + const upstreamDir = yield* createBareRemote(); + yield* runGit(repoDir, ["remote", "add", "origin", forkDir]); + yield* runGit(repoDir, ["remote", "add", "upstream", upstreamDir]); + yield* runGit(repoDir, ["checkout", "-b", "felix/integrate-pr-2305-1003"]); + fs.writeFileSync(path.join(repoDir, "changes.txt"), "change\n"); + yield* runGit(repoDir, ["add", "changes.txt"]); + yield* runGit(repoDir, ["commit", "-m", "Feature commit"]); + yield* runGit(repoDir, ["push", "-u", "origin", "felix/integrate-pr-2305-1003"]); + yield* configureVisibleRemoteUrlWithLocalRewrite( + repoDir, + "origin", + "git@github.com:felixfong227/t3code.git", + forkDir, + ); + yield* configureVisibleRemoteUrlWithLocalRewrite( + repoDir, + "upstream", + "git@github.com:pingdotgg/t3code.git", + upstreamDir, + ); + + const { manager, ghCalls } = yield* makeManager({ + ghScenario: { + prListSequenceByHeadSelector: { + "felixfong227:felix/integrate-pr-2305-1003": [ + // @effect-diagnostics-next-line preferSchemaOverJson:off + JSON.stringify([]), + // @effect-diagnostics-next-line preferSchemaOverJson:off + JSON.stringify([ + { + number: 2305, + title: "Add diff context comment drafts and collapsible sidebar", + url: "https://github.com/pingdotgg/t3code/pull/2305", + baseRefName: "main", + headRefName: "felix/integrate-pr-2305-1003", + state: "OPEN", + isCrossRepository: true, + headRepository: { + nameWithOwner: "felixfong227/t3code", + }, + headRepositoryOwner: { + login: "felixfong227", + }, + }, + ]), + ], + // @effect-diagnostics-next-line preferSchemaOverJson:off + "origin:felix/integrate-pr-2305-1003": [JSON.stringify([])], + // @effect-diagnostics-next-line preferSchemaOverJson:off + "felix/integrate-pr-2305-1003": [JSON.stringify([])], + }, + }, + }); + + const result = yield* runStackedAction(manager, { + cwd: repoDir, + action: "commit_push_pr", + }); + + expect(result.pr.status).toBe("created"); + expect(result.pr.number).toBe(2305); + expect( + ghCalls.some((call) => + call.includes("pr create --base main --head felixfong227:felix/integrate-pr-2305-1003"), + ), + ).toBe(true); + }), + ); + it.effect("rejects push/pr actions from detached HEAD", () => Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); diff --git a/apps/server/src/git/GitManager.ts b/apps/server/src/git/GitManager.ts index 8dfb957b89d..17d8ef5a425 100644 --- a/apps/server/src/git/GitManager.ts +++ b/apps/server/src/git/GitManager.ts @@ -830,19 +830,22 @@ export const makeGitManager = Effect.fn("makeGitManager")(function* () { const shouldProbeLocalBranchSelector = headBranchFromUpstream.length === 0 || headBranch === details.branch; - const [remoteRepository, originRepository] = yield* Effect.all( + const [remoteRepository, originRepository, upstreamRepository] = yield* Effect.all( [ resolveRemoteRepositoryContext(cwd, remoteName), resolveRemoteRepositoryContext(cwd, "origin"), + resolveRemoteRepositoryContext(cwd, "upstream"), ], { concurrency: "unbounded" }, ); + const targetRepository = + upstreamRepository.repositoryNameWithOwner !== null ? upstreamRepository : originRepository; const isCrossRepository = remoteRepository.repositoryNameWithOwner !== null && - originRepository.repositoryNameWithOwner !== null + targetRepository.repositoryNameWithOwner !== null ? remoteRepository.repositoryNameWithOwner.toLowerCase() !== - originRepository.repositoryNameWithOwner.toLowerCase() + targetRepository.repositoryNameWithOwner.toLowerCase() : remoteName !== null && remoteName !== "origin" && remoteRepository.repositoryNameWithOwner !== null;