From 75c421ad9965b5621f82702c4fb0d770fcfd2200 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:21:03 +0000 Subject: [PATCH 1/6] Initial plan From b39c55779e80f6287a6ce1abe644083d403a9eb4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:42:55 +0000 Subject: [PATCH 2/6] Prompt for local branch cleanup after merge Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- src/github/activityBarViewProvider.ts | 15 ++++---- src/github/pullRequestOverview.ts | 14 +++---- src/github/pullRequestReviewCommon.ts | 17 ++++++++- src/test/github/pullRequestOverview.test.ts | 41 ++++++++++++++++++++- 4 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/github/activityBarViewProvider.ts b/src/github/activityBarViewProvider.ts index 03ffb923a7..7b6c8f1b55 100644 --- a/src/github/activityBarViewProvider.ts +++ b/src/github/activityBarViewProvider.ts @@ -17,7 +17,7 @@ import { IComment } from '../common/comment'; import { emojify, ensureEmojis } from '../common/emoji'; import { disposeAll } from '../common/lifecycle'; import Logger from '../common/logger'; -import { CHECKOUT_DEFAULT_BRANCH, CHECKOUT_PULL_REQUEST_BASE_BRANCH, DELETE_BRANCH_AFTER_MERGE, POST_DONE, PR_SETTINGS_NAMESPACE } from '../common/settingKeys'; +import { CHECKOUT_DEFAULT_BRANCH, CHECKOUT_PULL_REQUEST_BASE_BRANCH, POST_DONE, PR_SETTINGS_NAMESPACE } from '../common/settingKeys'; import { ReviewEvent, TimelineEvent } from '../common/timelineEvent'; import { formatError } from '../common/utils'; import { generateUuid } from '../common/uuid'; @@ -486,18 +486,17 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W if (!result.merged) { vscode.window.showErrorMessage(vscode.l10n.t('Merging pull request failed: {0}', result?.message ?? '')); - } else { - // Check if auto-delete branch setting is enabled - const deleteBranchAfterMerge = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get(DELETE_BRANCH_AFTER_MERGE, false); - if (deleteBranchAfterMerge) { - // Automatically delete the branch after successful merge - await PullRequestReviewCommon.autoDeleteBranchesAfterMerge(this._folderRepositoryManager, this._item); - } } this._replyMessage(message, { state: result.merged ? GithubItemStateEnum.Merged : GithubItemStateEnum.Open, }); + if (result.merged) { + const branchDeletionResult = await PullRequestReviewCommon.handleBranchDeletionAfterMerge(this._folderRepositoryManager, this._item); + if (branchDeletionResult && !branchDeletionResult.isReply) { + this._postMessage(branchDeletionResult.message); + } + } } catch (e) { vscode.window.showErrorMessage(vscode.l10n.t('Unable to merge pull request. {0}', formatError(e))); diff --git a/src/github/pullRequestOverview.ts b/src/github/pullRequestOverview.ts index c39e55f05d..d3c7b55baa 100644 --- a/src/github/pullRequestOverview.ts +++ b/src/github/pullRequestOverview.ts @@ -857,13 +857,6 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel(DELETE_BRANCH_AFTER_MERGE, false); - if (deleteBranchAfterMerge) { - // Automatically delete the branch after successful merge - await PullRequestReviewCommon.autoDeleteBranchesAfterMerge(this._folderRepositoryManager, this._item); - } } const mergeResult: MergeResult = { @@ -872,6 +865,13 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel { + try { + const deleteBranchAfterMerge = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get(DELETE_BRANCH_AFTER_MERGE, false); + if (deleteBranchAfterMerge) { + await autoDeleteBranchesAfterMerge(folderRepositoryManager, item); + } else if ((await item.githubRepository.getMetadata()).delete_branch_on_merge) { + const result = await deleteBranch(folderRepositoryManager, item); + return result; + } + } catch (e) { + Logger.error(`Branch cleanup after merge failed: ${formatError(e)}`, 'PullRequestReviewCommon'); + } + } + async function performBranchDeletion(folderRepositoryManager: FolderRepositoryManager, item: PullRequestModel, defaultBranch: string, branchInfo: BranchInfo, selectedActions: SelectedAction[]): Promise { const isBranchActive = item.equals(folderRepositoryManager.activePullRequest) || (folderRepositoryManager.repository.state.HEAD?.name && folderRepositoryManager.repository.state.HEAD.name === branchInfo?.branch); const deletedBranchTypes: string[] = []; diff --git a/src/test/github/pullRequestOverview.test.ts b/src/test/github/pullRequestOverview.test.ts index d0469014fa..67ce2fee76 100644 --- a/src/test/github/pullRequestOverview.test.ts +++ b/src/test/github/pullRequestOverview.test.ts @@ -23,7 +23,7 @@ import { GitApiImpl } from '../../api/api1'; import { CredentialStore } from '../../github/credentials'; import { GitHubServerType } from '../../common/authentication'; import { GitHubRemote } from '../../common/remote'; -import { CheckState } from '../../github/interface'; +import { CheckState, GithubItemStateEnum } from '../../github/interface'; import { CreatePullRequestHelper } from '../../view/createPullRequestHelper'; import { RepositoriesManager } from '../../github/repositoriesManager'; import { MockThemeWatcher } from '../mocks/mockThemeWatcher'; @@ -189,4 +189,43 @@ describe('PullRequestOverview', function () { assert.strictEqual(panel1!.getCurrentTitle(), '#2000 New feature'); }); }); + + describe('mergePullRequest', function () { + it('prompts to delete the local branch when GitHub deletes branches after merge', async function () { + repo.buildMetadata(repository => repository.delete_branch_on_merge!(true)); + repo.addGraphQLPullRequest(builder => { + builder.pullRequest(response => { + response.repository(r => { + r.pullRequest(pr => pr.number(1000)); + }); + }); + }); + + const prItem = convertRESTPullRequestToRawPullRequest(new PullRequestBuilder().number(1000).build(), repo); + const prModel = new PullRequestModel(credentialStore, telemetry, repo, remote, prItem); + const identity = { owner: prModel.remote.owner, repo: prModel.remote.repositoryName, number: prModel.number }; + await PullRequestOverviewPanel.createOrShow(telemetry, EXTENSION_URI, pullRequestManager, identity, prModel); + + const panel = PullRequestOverviewPanel.findPanel(identity.owner, identity.repo, identity.number)!; + sinon.stub(prModel, 'merge').resolves({ merged: true, message: '', timeline: [] }); + sinon.stub(pullRequestManager, 'getBranchNameForPullRequest').resolves({ + branch: 'new-feature', + createdForPullRequest: false, + }); + sinon.stub(pullRequestManager, 'getPullRequestRepositoryDefaultBranch').resolves('main'); + const showQuickPick = sinon.stub(vscode.window, 'showQuickPick').resolves(undefined); + const replyMessage = sinon.stub(panel as any, '_replyMessage'); + + await (panel as any).mergePullRequest({ + command: 'pr.merge', + args: { title: '', description: '', method: 'squash' }, + }); + + assert.strictEqual(showQuickPick.calledOnce, true); + const actions = showQuickPick.firstCall.args[0] as readonly (vscode.QuickPickItem & { type: string })[]; + assert.strictEqual(actions.some(action => action.type === 'local'), true); + assert.strictEqual(replyMessage.firstCall.args[1].state, GithubItemStateEnum.Merged); + sinon.assert.callOrder(replyMessage, showQuickPick); + }); + }); }); From 2083170b3581e4bd9c56d37319e6a370ea3c0673 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:45:10 +0000 Subject: [PATCH 3/6] Clarify post-merge cleanup messages Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- src/github/activityBarViewProvider.ts | 6 +++--- src/github/pullRequestOverview.ts | 6 +++--- src/github/pullRequestReviewCommon.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/github/activityBarViewProvider.ts b/src/github/activityBarViewProvider.ts index 7b6c8f1b55..7595d5111e 100644 --- a/src/github/activityBarViewProvider.ts +++ b/src/github/activityBarViewProvider.ts @@ -492,9 +492,9 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W state: result.merged ? GithubItemStateEnum.Merged : GithubItemStateEnum.Open, }); if (result.merged) { - const branchDeletionResult = await PullRequestReviewCommon.handleBranchDeletionAfterMerge(this._folderRepositoryManager, this._item); - if (branchDeletionResult && !branchDeletionResult.isReply) { - this._postMessage(branchDeletionResult.message); + const branchDeletionMessage = await PullRequestReviewCommon.handleBranchDeletionAfterMerge(this._folderRepositoryManager, this._item); + if (branchDeletionMessage) { + this._postMessage(branchDeletionMessage); } } diff --git a/src/github/pullRequestOverview.ts b/src/github/pullRequestOverview.ts index d3c7b55baa..0e012f17c1 100644 --- a/src/github/pullRequestOverview.ts +++ b/src/github/pullRequestOverview.ts @@ -866,10 +866,10 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel { + export async function handleBranchDeletionAfterMerge(folderRepositoryManager: FolderRepositoryManager, item: PullRequestModel): Promise<{ command: string, branchTypes: string[] } | undefined> { try { const deleteBranchAfterMerge = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get(DELETE_BRANCH_AFTER_MERGE, false); if (deleteBranchAfterMerge) { await autoDeleteBranchesAfterMerge(folderRepositoryManager, item); } else if ((await item.githubRepository.getMetadata()).delete_branch_on_merge) { const result = await deleteBranch(folderRepositoryManager, item); - return result; + return result.isReply ? undefined : result.message; } } catch (e) { Logger.error(`Branch cleanup after merge failed: ${formatError(e)}`, 'PullRequestReviewCommon'); From 155118b2b19abe54f292cf2e582bbab23397a32f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 11:48:51 +0000 Subject: [PATCH 4/6] Apply remaining changes Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- ...scode.proposed.chatParticipantAdditions.d.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts index 7fe34db11d..46b9977968 100644 --- a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts @@ -573,19 +573,14 @@ declare module 'vscode' { } /** - * Represents an auto-mode model routing resolution. Displayed as a collapsible - * widget in the chat stream showing which model was selected and why. + * Explains what the "Auto" model routed a turn to, as a single status line. + * Push a part without a model for the in-flight state, then a resolved one. + * Auto may route several times in a turn; each route gets its own row. */ export class ChatResponseAutoModeResolutionPart { - /** The model ID that was selected by the router */ - resolvedModel: string; - /** The user-facing display name of the resolved model */ - resolvedModelName: string; - /** The router's classification label */ - predictedLabel: string; - /** Confidence score (0-1) from the router */ - confidence: number; - constructor(resolvedModel: string, resolvedModelName: string, predictedLabel: string, confidence: number); + /** The model the router picked, or `undefined` while routing is in flight. */ + resolvedModel: { id: string; name: string } | undefined; + constructor(resolvedModel?: { id: string; name: string }); } export interface ChatResponseStream { From 494f5925ff1fa897a5cf2d5b2cf9ba03732faad2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Aug 2026 15:02:33 +0000 Subject: [PATCH 5/6] Prompt for cleanup after every merge Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> --- ...scode.proposed.chatParticipantAdditions.d.ts | 17 +++++++++++------ src/github/pullRequestReviewCommon.ts | 2 +- src/test/github/pullRequestOverview.test.ts | 3 +-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts index 46b9977968..7fe34db11d 100644 --- a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts @@ -573,14 +573,19 @@ declare module 'vscode' { } /** - * Explains what the "Auto" model routed a turn to, as a single status line. - * Push a part without a model for the in-flight state, then a resolved one. - * Auto may route several times in a turn; each route gets its own row. + * Represents an auto-mode model routing resolution. Displayed as a collapsible + * widget in the chat stream showing which model was selected and why. */ export class ChatResponseAutoModeResolutionPart { - /** The model the router picked, or `undefined` while routing is in flight. */ - resolvedModel: { id: string; name: string } | undefined; - constructor(resolvedModel?: { id: string; name: string }); + /** The model ID that was selected by the router */ + resolvedModel: string; + /** The user-facing display name of the resolved model */ + resolvedModelName: string; + /** The router's classification label */ + predictedLabel: string; + /** Confidence score (0-1) from the router */ + confidence: number; + constructor(resolvedModel: string, resolvedModelName: string, predictedLabel: string, confidence: number); } export interface ChatResponseStream { diff --git a/src/github/pullRequestReviewCommon.ts b/src/github/pullRequestReviewCommon.ts index 952a7bc48a..58d7878091 100644 --- a/src/github/pullRequestReviewCommon.ts +++ b/src/github/pullRequestReviewCommon.ts @@ -412,7 +412,7 @@ export namespace PullRequestReviewCommon { const deleteBranchAfterMerge = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get(DELETE_BRANCH_AFTER_MERGE, false); if (deleteBranchAfterMerge) { await autoDeleteBranchesAfterMerge(folderRepositoryManager, item); - } else if ((await item.githubRepository.getMetadata()).delete_branch_on_merge) { + } else { const result = await deleteBranch(folderRepositoryManager, item); return result.isReply ? undefined : result.message; } diff --git a/src/test/github/pullRequestOverview.test.ts b/src/test/github/pullRequestOverview.test.ts index 67ce2fee76..0833fcad66 100644 --- a/src/test/github/pullRequestOverview.test.ts +++ b/src/test/github/pullRequestOverview.test.ts @@ -191,8 +191,7 @@ describe('PullRequestOverview', function () { }); describe('mergePullRequest', function () { - it('prompts to delete the local branch when GitHub deletes branches after merge', async function () { - repo.buildMetadata(repository => repository.delete_branch_on_merge!(true)); + it('prompts to delete the local branch after merge', async function () { repo.addGraphQLPullRequest(builder => { builder.pullRequest(response => { response.repository(r => { From 570f7fbde9a946192102d237831275c2c86cd44e Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:16:47 +0200 Subject: [PATCH 6/6] Revert "Prompt for cleanup after every merge" This reverts commit 494f5925ff1fa897a5cf2d5b2cf9ba03732faad2. --- ...scode.proposed.chatParticipantAdditions.d.ts | 17 ++++++----------- src/github/pullRequestReviewCommon.ts | 2 +- src/test/github/pullRequestOverview.test.ts | 3 ++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts index 7fe34db11d..46b9977968 100644 --- a/src/@types/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/@types/vscode.proposed.chatParticipantAdditions.d.ts @@ -573,19 +573,14 @@ declare module 'vscode' { } /** - * Represents an auto-mode model routing resolution. Displayed as a collapsible - * widget in the chat stream showing which model was selected and why. + * Explains what the "Auto" model routed a turn to, as a single status line. + * Push a part without a model for the in-flight state, then a resolved one. + * Auto may route several times in a turn; each route gets its own row. */ export class ChatResponseAutoModeResolutionPart { - /** The model ID that was selected by the router */ - resolvedModel: string; - /** The user-facing display name of the resolved model */ - resolvedModelName: string; - /** The router's classification label */ - predictedLabel: string; - /** Confidence score (0-1) from the router */ - confidence: number; - constructor(resolvedModel: string, resolvedModelName: string, predictedLabel: string, confidence: number); + /** The model the router picked, or `undefined` while routing is in flight. */ + resolvedModel: { id: string; name: string } | undefined; + constructor(resolvedModel?: { id: string; name: string }); } export interface ChatResponseStream { diff --git a/src/github/pullRequestReviewCommon.ts b/src/github/pullRequestReviewCommon.ts index 58d7878091..952a7bc48a 100644 --- a/src/github/pullRequestReviewCommon.ts +++ b/src/github/pullRequestReviewCommon.ts @@ -412,7 +412,7 @@ export namespace PullRequestReviewCommon { const deleteBranchAfterMerge = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get(DELETE_BRANCH_AFTER_MERGE, false); if (deleteBranchAfterMerge) { await autoDeleteBranchesAfterMerge(folderRepositoryManager, item); - } else { + } else if ((await item.githubRepository.getMetadata()).delete_branch_on_merge) { const result = await deleteBranch(folderRepositoryManager, item); return result.isReply ? undefined : result.message; } diff --git a/src/test/github/pullRequestOverview.test.ts b/src/test/github/pullRequestOverview.test.ts index 0833fcad66..67ce2fee76 100644 --- a/src/test/github/pullRequestOverview.test.ts +++ b/src/test/github/pullRequestOverview.test.ts @@ -191,7 +191,8 @@ describe('PullRequestOverview', function () { }); describe('mergePullRequest', function () { - it('prompts to delete the local branch after merge', async function () { + it('prompts to delete the local branch when GitHub deletes branches after merge', async function () { + repo.buildMetadata(repository => repository.delete_branch_on_merge!(true)); repo.addGraphQLPullRequest(builder => { builder.pullRequest(response => { response.repository(r => {