From 1e682835658a0f88725d8aec25136f69caf2f692 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 25 May 2026 18:05:35 +0800 Subject: [PATCH] Allow PR merge when GitHub reports it ready (#1172) --- packages/app/src/git/policy.test.ts | 42 +++++++++++++++++++++++++++++ packages/app/src/git/policy.ts | 16 ++++++----- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/packages/app/src/git/policy.test.ts b/packages/app/src/git/policy.test.ts index 9552133b0..08da19407 100644 --- a/packages/app/src/git/policy.test.ts +++ b/packages/app/src/git/policy.test.ts @@ -226,6 +226,9 @@ describe("git-actions-policy", () => { "merge-from-base", "merge-branch", "pr", + "merge-pr-squash", + "merge-pr-merge", + "merge-pr-rebase", ]); expect( actions.secondary.some((action) => action.id === "pr" && action.label === "View PR"), @@ -324,6 +327,45 @@ describe("git-actions-policy", () => { }); }); + it("offers direct PR merge when GitHub says the PR is mergeable even if the local branch is behind", () => { + const actions = buildGitActions( + createInput({ + hasRemote: true, + isOnBaseBranch: false, + aheadCount: 2, + behindBaseCount: 3, + aheadOfOrigin: 0, + behindOfOrigin: 2, + hasPullRequest: true, + pullRequestUrl: "https://example.com/pr/456", + pullRequestState: "open", + pullRequestMergeable: "MERGEABLE", + pullRequestGithub: githubStatus({ mergeStateStatus: "CLEAN" }), + shipDefault: "pr", + }), + ); + + expect(actions.secondary).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: "merge-pr-squash", + disabled: false, + unavailableMessage: undefined, + }), + expect.objectContaining({ + id: "merge-pr-merge", + disabled: false, + unavailableMessage: undefined, + }), + expect.objectContaining({ + id: "merge-pr-rebase", + disabled: false, + unavailableMessage: undefined, + }), + ]), + ); + }); + it("respects the ship preference when choosing between PR merge and local merge", () => { const prPreferredActions = buildGitActions( createInput({ diff --git a/packages/app/src/git/policy.ts b/packages/app/src/git/policy.ts index 9133cfee1..5410bbacb 100644 --- a/packages/app/src/git/policy.ts +++ b/packages/app/src/git/policy.ts @@ -504,7 +504,7 @@ function canMergeFromBase(input: BuildGitActionsInput): boolean { function canMergePr(input: BuildGitActionsInput): boolean { const github = input.pullRequestGithub; - const canMergeFromTopLevelStatus = + const canMergeFromPullRequestStatus = input.githubFeaturesEnabled && input.hasPullRequest && input.pullRequestState === "open" && @@ -512,17 +512,19 @@ function canMergePr(input: BuildGitActionsInput): boolean { !input.pullRequestIsMerged && input.pullRequestMergeable !== "CONFLICTING" && input.aheadCount > 0 && - !input.hasUncommittedChanges && - input.behindOfOrigin === 0 && - input.aheadOfOrigin === 0 && - !canMergeFromBase(input); + !input.hasUncommittedChanges; - if (!canMergeFromTopLevelStatus) { + if (!canMergeFromPullRequestStatus) { return false; } if (!hasPullRequestGithubFacts(github)) { - return input.pullRequestMergeable === "MERGEABLE"; + return ( + input.pullRequestMergeable === "MERGEABLE" && + input.behindOfOrigin === 0 && + input.aheadOfOrigin === 0 && + !canMergeFromBase(input) + ); } return (