From 3c546ff4f87f91160f56e941a67fff4674d36ee8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:43:54 +0000 Subject: [PATCH 1/9] Initial plan From 62b51caffa8c03d58f1cf463002d67cf34ef0cce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:05:12 +0000 Subject: [PATCH 2/9] Add Ready & Merge button for Copilot PRs Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- src/github/pullRequestOverview.ts | 21 +++++++++++++++++ webviews/common/context.tsx | 2 ++ webviews/components/merge.tsx | 38 +++++++++++++++++++++++++++---- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/github/pullRequestOverview.ts b/src/github/pullRequestOverview.ts index de64cf068d..4c9f1017f1 100644 --- a/src/github/pullRequestOverview.ts +++ b/src/github/pullRequestOverview.ts @@ -364,6 +364,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel): Promise { + try { + // Step 1: Mark as ready for review + const readyResult = await this._item.setReadyForReview(); + + // Step 2: Approve the PR + await this._item.approve(this._folderRepositoryManager.repository, ''); + + // Step 3: Enable auto-merge + await this._item.enableAutoMerge(message.args.mergeMethod); + + // Return the ready result to update the UI + this._replyMessage(message, readyResult); + } catch (e) { + vscode.window.showErrorMessage(`Unable to mark ready for review and merge. ${formatError(e)}`); + this._throwError(message, ''); + } + } + private async checkoutDefaultBranch(message: IRequestMessage): Promise { try { const prBranch = this._folderRepositoryManager.repository.state.HEAD?.name; diff --git a/webviews/common/context.tsx b/webviews/common/context.tsx index df94d45c06..dd00c8076f 100644 --- a/webviews/common/context.tsx +++ b/webviews/common/context.tsx @@ -89,6 +89,8 @@ export class PRContext { public readyForReview = (): Promise => this.postMessage({ command: 'pr.readyForReview' }); + public readyForReviewAndMerge = (args: { mergeMethod: MergeMethod }): Promise => this.postMessage({ command: 'pr.readyForReviewAndMerge', args }); + public addReviewers = () => this.postMessage({ command: 'pr.change-reviewers' }); public changeProjects = (): Promise => this.postMessage({ command: 'pr.change-projects' }); public removeProject = (project: IProjectItem) => this.postMessage({ command: 'pr.remove-project', args: project }); diff --git a/webviews/components/merge.tsx b/webviews/components/merge.tsx index 7a6aee3d7b..4c78d07245 100644 --- a/webviews/components/merge.tsx +++ b/webviews/components/merge.tsx @@ -281,9 +281,10 @@ export const OfferToUpdate = ({ mergeable, isSimple, isCurrentlyCheckedOut, canU }; -export const ReadyForReview = ({ isSimple }: { isSimple: boolean }) => { +export const ReadyForReview = ({ isSimple, showMergeButton, mergeMethod }: { isSimple: boolean; showMergeButton?: boolean; mergeMethod?: MergeMethod }) => { const [isBusy, setBusy] = useState(false); - const { readyForReview, updatePR } = useContext(PullRequestContext); + const [isMergeBusy, setMergeBusy] = useState(false); + const { readyForReview, readyForReviewAndMerge, updatePR } = useContext(PullRequestContext); const markReadyForReview = useCallback(async () => { try { @@ -295,6 +296,16 @@ export const ReadyForReview = ({ isSimple }: { isSimple: boolean }) => { } }, [setBusy, readyForReview, updatePR]); + const markReadyAndMerge = useCallback(async () => { + try { + setMergeBusy(true); + const result = await readyForReviewAndMerge({ mergeMethod: mergeMethod || 'squash' }); + updatePR(result); + } finally { + setMergeBusy(false); + } + }, [setMergeBusy, readyForReviewAndMerge, updatePR, mergeMethod]); + return (
@@ -305,7 +316,18 @@ export const ReadyForReview = ({ isSimple }: { isSimple: boolean }) => {
- + + {showMergeButton && ( + + )}
); @@ -338,10 +360,16 @@ export const Merge = (pr: PullRequest) => { }; export const PrActions = ({ pr, isSimple }: { pr: PullRequest; isSimple: boolean }) => { - const { hasWritePermission, canEdit, isDraft, mergeable } = pr; + const { hasWritePermission, canEdit, isDraft, mergeable, isCopilotOnMyBehalf, mergeMethodsAvailability } = pr; if (isDraft) { // Only PR author and users with push rights can mark draft as ready for review - return canEdit ? : null; + if (!canEdit) { + return null; + } + + // For Copilot-created PRs, show the "Ready & Merge" button alongside the regular button + const showMergeButton = isCopilotOnMyBehalf && mergeMethodsAvailability.squash; + return ; } if (mergeable === PullRequestMergeability.Mergeable && hasWritePermission && !pr.mergeQueueEntry) { From 24a97b6ded83a6d6cbdac0b2d0436473f6cd6a8f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:07:59 +0000 Subject: [PATCH 3/9] Improve error handling for Ready & Merge button Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- src/github/pullRequestOverview.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/github/pullRequestOverview.ts b/src/github/pullRequestOverview.ts index 4c9f1017f1..e095bece31 100644 --- a/src/github/pullRequestOverview.ts +++ b/src/github/pullRequestOverview.ts @@ -657,15 +657,27 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel Date: Fri, 7 Nov 2025 16:10:12 +0100 Subject: [PATCH 4/9] move some stuff --- webviews/components/merge.tsx | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/webviews/components/merge.tsx b/webviews/components/merge.tsx index 4c78d07245..c01637b125 100644 --- a/webviews/components/merge.tsx +++ b/webviews/components/merge.tsx @@ -281,9 +281,8 @@ export const OfferToUpdate = ({ mergeable, isSimple, isCurrentlyCheckedOut, canU }; -export const ReadyForReview = ({ isSimple, showMergeButton, mergeMethod }: { isSimple: boolean; showMergeButton?: boolean; mergeMethod?: MergeMethod }) => { +export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: { isSimple: boolean; isCopilotOnMyBehalf?: boolean; mergeMethod: MergeMethod }) => { const [isBusy, setBusy] = useState(false); - const [isMergeBusy, setMergeBusy] = useState(false); const { readyForReview, readyForReviewAndMerge, updatePR } = useContext(PullRequestContext); const markReadyForReview = useCallback(async () => { @@ -298,13 +297,13 @@ export const ReadyForReview = ({ isSimple, showMergeButton, mergeMethod }: { isS const markReadyAndMerge = useCallback(async () => { try { - setMergeBusy(true); - const result = await readyForReviewAndMerge({ mergeMethod: mergeMethod || 'squash' }); + setBusy(true); + const result = await readyForReviewAndMerge({ mergeMethod: mergeMethod }); updatePR(result); } finally { - setMergeBusy(false); + setBusy(false); } - }, [setMergeBusy, readyForReviewAndMerge, updatePR, mergeMethod]); + }, [readyForReviewAndMerge, updatePR, mergeMethod]); return (
@@ -316,18 +315,18 @@ export const ReadyForReview = ({ isSimple, showMergeButton, mergeMethod }: { isS
- - {showMergeButton && ( + {isCopilotOnMyBehalf && ( )} +
); @@ -360,16 +359,14 @@ export const Merge = (pr: PullRequest) => { }; export const PrActions = ({ pr, isSimple }: { pr: PullRequest; isSimple: boolean }) => { - const { hasWritePermission, canEdit, isDraft, mergeable, isCopilotOnMyBehalf, mergeMethodsAvailability } = pr; + const { hasWritePermission, canEdit, isDraft, mergeable, isCopilotOnMyBehalf, defaultMergeMethod } = pr; if (isDraft) { // Only PR author and users with push rights can mark draft as ready for review if (!canEdit) { return null; } - - // For Copilot-created PRs, show the "Ready & Merge" button alongside the regular button - const showMergeButton = isCopilotOnMyBehalf && mergeMethodsAvailability.squash; - return ; + + return ; } if (mergeable === PullRequestMergeability.Mergeable && hasWritePermission && !pr.mergeQueueEntry) { From 23e3f7b39e4c76392ccdc5228f1feebb838f2304 Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Fri, 7 Nov 2025 16:14:07 +0100 Subject: [PATCH 5/9] Add check all icon --- resources/icons/codicons/check-all.svg | 1 + webviews/components/icon.tsx | 1 + 2 files changed, 2 insertions(+) create mode 100644 resources/icons/codicons/check-all.svg diff --git a/resources/icons/codicons/check-all.svg b/resources/icons/codicons/check-all.svg new file mode 100644 index 0000000000..3028a6c57e --- /dev/null +++ b/resources/icons/codicons/check-all.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webviews/components/icon.tsx b/webviews/components/icon.tsx index 45f2bb938e..67c7ee53aa 100644 --- a/webviews/components/icon.tsx +++ b/webviews/components/icon.tsx @@ -15,6 +15,7 @@ export default Icon; export const accountIcon = ; export const addIcon = ; export const checkIcon = ; +export const checkAllIcon = ; export const chevronDownIcon = ; export const circleFilledIcon = ; export const closeIcon = ; From 77c44531071818e14c28074801f6732be969cda6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:22:46 +0000 Subject: [PATCH 6/9] Use icon button with checkAllIcon for Ready & Merge action Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- webviews/components/merge.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webviews/components/merge.tsx b/webviews/components/merge.tsx index c01637b125..c9b3fabc9c 100644 --- a/webviews/components/merge.tsx +++ b/webviews/components/merge.tsx @@ -14,7 +14,7 @@ import React, { } from 'react'; import { AutoMerge, QueuedToMerge } from './automergeSelect'; import { Dropdown } from './dropdown'; -import { checkIcon, circleFilledIcon, closeIcon, gitMergeIcon, requestChangesIcon, skipIcon, warningIcon } from './icon'; +import { checkAllIcon, checkIcon, circleFilledIcon, closeIcon, gitMergeIcon, requestChangesIcon, skipIcon, warningIcon } from './icon'; import { nbsp } from './space'; import { Avatar } from './user'; import { EventType, ReviewEvent } from '../../src/common/timelineEvent'; @@ -317,13 +317,13 @@ export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: {
{isCopilotOnMyBehalf && ( )} From a0af253c59223d52815bac0a9115d20865973d0c Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:15:50 +0100 Subject: [PATCH 7/9] Fix styling --- webviews/editorWebview/index.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/webviews/editorWebview/index.css b/webviews/editorWebview/index.css index f73c8c39c3..4716e0152e 100644 --- a/webviews/editorWebview/index.css +++ b/webviews/editorWebview/index.css @@ -314,6 +314,12 @@ button.input-box { align-items: center; } +.ready-for-review-container .button-container { + flex-direction: row; + display: flex; + align-items: center; +} + .ready-for-review-icon { width: 16px; height: 16px; From 7c435564f1738a887818775e78f085eecbb4a088 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:24:51 +0000 Subject: [PATCH 8/9] Add loading spinner to Ready & Merge icon button Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- webviews/components/merge.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/webviews/components/merge.tsx b/webviews/components/merge.tsx index c9b3fabc9c..b438796dab 100644 --- a/webviews/components/merge.tsx +++ b/webviews/components/merge.tsx @@ -14,7 +14,7 @@ import React, { } from 'react'; import { AutoMerge, QueuedToMerge } from './automergeSelect'; import { Dropdown } from './dropdown'; -import { checkAllIcon, checkIcon, circleFilledIcon, closeIcon, gitMergeIcon, requestChangesIcon, skipIcon, warningIcon } from './icon'; +import { checkAllIcon, checkIcon, circleFilledIcon, closeIcon, gitMergeIcon, loadingIcon, requestChangesIcon, skipIcon, warningIcon } from './icon'; import { nbsp } from './space'; import { Avatar } from './user'; import { EventType, ReviewEvent } from '../../src/common/timelineEvent'; @@ -283,6 +283,7 @@ export const OfferToUpdate = ({ mergeable, isSimple, isCurrentlyCheckedOut, canU export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: { isSimple: boolean; isCopilotOnMyBehalf?: boolean; mergeMethod: MergeMethod }) => { const [isBusy, setBusy] = useState(false); + const [isMergeBusy, setMergeBusy] = useState(false); const { readyForReview, readyForReviewAndMerge, updatePR } = useContext(PullRequestContext); const markReadyForReview = useCallback(async () => { @@ -297,11 +298,11 @@ export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: { const markReadyAndMerge = useCallback(async () => { try { - setBusy(true); + setMergeBusy(true); const result = await readyForReviewAndMerge({ mergeMethod: mergeMethod }); updatePR(result); } finally { - setBusy(false); + setMergeBusy(false); } }, [readyForReviewAndMerge, updatePR, mergeMethod]); @@ -318,15 +319,15 @@ export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: { {isCopilotOnMyBehalf && ( )} - +
); From 0819bc31999a1dc8322dce35e51b638a280a3c6d Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:44:17 +0100 Subject: [PATCH 9/9] Clean up --- src/github/pullRequestOverview.ts | 4 ---- webviews/components/merge.tsx | 6 ++++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/github/pullRequestOverview.ts b/src/github/pullRequestOverview.ts index e095bece31..e7fb80cd68 100644 --- a/src/github/pullRequestOverview.ts +++ b/src/github/pullRequestOverview.ts @@ -653,10 +653,8 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel): Promise { try { - // Step 1: Mark as ready for review const readyResult = await this._item.setReadyForReview(); - // Step 2: Approve the PR try { await this._item.approve(this._folderRepositoryManager.repository, ''); } catch (e) { @@ -665,7 +663,6 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel { try { + setBusy(true); setMergeBusy(true); const result = await readyForReviewAndMerge({ mergeMethod: mergeMethod }); updatePR(result); } finally { + setBusy(false); setMergeBusy(false); } }, [readyForReviewAndMerge, updatePR, mergeMethod]); @@ -319,7 +321,7 @@ export const ReadyForReview = ({ isSimple, isCopilotOnMyBehalf, mergeMethod }: { {isCopilotOnMyBehalf && ( + );