Skip to content

Commit b13d195

Browse files
Copilotalexr00
andauthored
Add options to checkout PR base branch after merge/branch delete (#8250)
* Initial plan * Changes before error encountered Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Plan to add PR base branch checkout options to postDone setting Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Add checkoutPullRequestBaseBranch options to postDone setting Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Remove unrelated changes from chatSessionsProvider Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Update button text to show target branch when postDone setting uses PR base branch Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Simplify UI logic by computing doneCheckoutBranch on backend Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com> * Clean up * Fix focus view * Fix if the branch doesn't exist locally --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: alexr00 <38270282+alexr00@users.noreply.github.com>
1 parent cc737a6 commit b13d195

File tree

13 files changed

+87
-35
lines changed

13 files changed

+87
-35
lines changed

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,13 +492,17 @@
492492
"type": "string",
493493
"enum": [
494494
"checkoutDefaultBranch",
495-
"checkoutDefaultBranchAndPull"
495+
"checkoutDefaultBranchAndPull",
496+
"checkoutPullRequestBaseBranch",
497+
"checkoutPullRequestBaseBranchAndPull"
496498
],
497499
"description": "%githubPullRequests.postDone.description%",
498500
"default": "checkoutDefaultBranch",
499501
"enumDescriptions": [
500502
"%githubPullRequests.postDone.checkoutDefaultBranch%",
501-
"%githubPullRequests.postDone.checkoutDefaultBranchAndPull%"
503+
"%githubPullRequests.postDone.checkoutDefaultBranchAndPull%",
504+
"%githubPullRequests.postDone.checkoutPullRequestBaseBranch%",
505+
"%githubPullRequests.postDone.checkoutPullRequestBaseBranchAndPull%"
502506
]
503507
},
504508
"githubPullRequests.defaultCommentType": {

package.nls.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@
8686
"githubPullRequests.postDone.description": "The action to take after using the 'checkout default branch' or 'delete branch' actions on a currently checked out pull request.",
8787
"githubPullRequests.postDone.checkoutDefaultBranch": "Checkout the default branch of the repository.",
8888
"githubPullRequests.postDone.checkoutDefaultBranchAndPull": "Checkout the default branch of the repository and pull the latest changes.",
89+
"githubPullRequests.postDone.checkoutPullRequestBaseBranch": "Checkout the pull request's base branch",
90+
"githubPullRequests.postDone.checkoutPullRequestBaseBranchAndPull": "Checkout the pull request's base branch and pull the latest changes",
8991
"githubPullRequests.defaultCommentType.description": "The default comment type to use when submitting a comment and there is no active review.",
9092
"githubPullRequests.defaultCommentType.single": "Submits the comment as a single comment that will be immediately visible to other users.",
9193
"githubPullRequests.defaultCommentType.review": "Submits the comment as a review comment that will be visible to other users once the review is submitted.",

src/commands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ export function registerCommands(
809809
const manager = reposManager.getManagerForIssueModel(pullRequestModel);
810810
if (manager) {
811811
const prBranch = manager.repository.state.HEAD?.name;
812-
await manager.checkoutDefaultBranch(branch);
812+
await manager.checkoutDefaultBranch(branch, pullRequestModel);
813813
if (prBranch) {
814814
await manager.cleanupAfterPullRequest(prBranch, pullRequestModel!);
815815
}

src/common/settingKeys.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export const NOTIFICATION_SETTING = 'notifications';
2323
export type NotificationVariants = 'off' | 'pullRequests';
2424
export const POST_CREATE = 'postCreate';
2525
export const POST_DONE = 'postDone';
26+
export const CHECKOUT_PULL_REQUEST_BASE_BRANCH = 'checkoutPullRequestBaseBranch';
27+
export const CHECKOUT_DEFAULT_BRANCH = 'checkoutDefaultBranch';
2628
export const QUERIES = 'queries';
2729
export const PULL_REQUEST_LABELS = 'labelCreated';
2830
export const FOCUSED_MODE = 'focusedMode';

src/github/activityBarViewProvider.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { MergeArguments, PullRequest, ReviewType } from './views';
1515
import { IComment } from '../common/comment';
1616
import { emojify, ensureEmojis } from '../common/emoji';
1717
import { disposeAll } from '../common/lifecycle';
18-
import { DELETE_BRANCH_AFTER_MERGE, PR_SETTINGS_NAMESPACE } from '../common/settingKeys';
18+
import { CHECKOUT_DEFAULT_BRANCH, CHECKOUT_PULL_REQUEST_BASE_BRANCH, DELETE_BRANCH_AFTER_MERGE, POST_DONE, PR_SETTINGS_NAMESPACE } from '../common/settingKeys';
1919
import { ReviewEvent } from '../common/timelineEvent';
2020
import { formatError } from '../common/utils';
2121
import { generateUuid } from '../common/uuid';
@@ -233,6 +233,11 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
233233
const continueOnGitHub = !!(isCrossRepository && isInCodespaces());
234234
const reviewState = this.getCurrentUserReviewState(this._existingReviewers, currentUser);
235235

236+
const postDoneAction = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<string>(POST_DONE, CHECKOUT_DEFAULT_BRANCH);
237+
const doneCheckoutBranch = postDoneAction.startsWith(CHECKOUT_PULL_REQUEST_BASE_BRANCH)
238+
? pullRequest.base.ref
239+
: defaultBranch;
240+
236241
const context: Partial<PullRequest> = {
237242
number: pullRequest.number,
238243
title: pullRequest.title,
@@ -268,6 +273,7 @@ export class PullRequestViewProvider extends WebviewViewBase implements vscode.W
268273
mergeMethodsAvailability,
269274
defaultMergeMethod,
270275
repositoryDefaultBranch: defaultBranch,
276+
doneCheckoutBranch,
271277
isIssue: false,
272278
isAuthor: currentUser.login === pullRequest.author.login,
273279
reviewers: this._existingReviewers,

src/github/folderRepositoryManager.ts

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import { GitHubRemote, parseRemote, parseRepositoryRemotes, parseRepositoryRemot
4141
import {
4242
ALLOW_FETCH,
4343
AUTO_STASH,
44+
CHECKOUT_DEFAULT_BRANCH,
45+
CHECKOUT_PULL_REQUEST_BASE_BRANCH,
4446
GIT,
4547
POST_DONE,
4648
PR_SETTINGS_NAMESPACE,
@@ -2526,21 +2528,29 @@ export class FolderRepositoryManager extends Disposable {
25262528
}
25272529
}
25282530

2529-
public async checkoutDefaultBranch(branch: string): Promise<void> {
2530-
const CHECKOUT_DEFAULT_BRANCH = 'checkoutDefaultBranch';
2531-
const CHECKOUT_DEFAULT_BRANCH_AND_PULL = 'checkoutDefaultBranchAndPull';
2531+
public async checkoutDefaultBranch(branch: string, pullRequestModel: PullRequestModel | undefined): Promise<void> {
2532+
const AND_PULL = 'AndPull';
25322533

2533-
const postDoneAction = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<typeof CHECKOUT_DEFAULT_BRANCH | typeof CHECKOUT_DEFAULT_BRANCH_AND_PULL>(POST_DONE, CHECKOUT_DEFAULT_BRANCH);
2534+
const postDoneAction = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<string>(POST_DONE, CHECKOUT_DEFAULT_BRANCH);
25342535

2535-
if (postDoneAction === CHECKOUT_DEFAULT_BRANCH_AND_PULL) {
2536-
await this.checkoutDefaultBranchAndPull(branch);
2536+
// Determine which branch to checkout
2537+
let targetBranch = branch;
2538+
let remoteName: string | undefined = undefined;
2539+
if (pullRequestModel && postDoneAction.startsWith(CHECKOUT_PULL_REQUEST_BASE_BRANCH)) {
2540+
// Use the PR's base branch if the setting specifies it
2541+
targetBranch = pullRequestModel.base.ref;
2542+
remoteName = pullRequestModel.remote.remoteName;
2543+
}
2544+
2545+
if (postDoneAction.endsWith(AND_PULL)) {
2546+
await this.checkoutDoneBranchAndPull(targetBranch, remoteName);
25372547
} else {
2538-
await this.checkoutDefaultBranchOnly(branch);
2548+
await this.checkoutDoneBranchOnly(targetBranch, remoteName);
25392549
}
25402550
}
25412551

2542-
private async checkoutDefaultBranchAndPull(branch: string): Promise<void> {
2543-
await this.checkoutDefaultBranchOnly(branch);
2552+
private async checkoutDoneBranchAndPull(branch: string, remoteName?: string): Promise<void> {
2553+
await this.checkoutDoneBranchOnly(branch, remoteName);
25442554
// After checking out, pull the latest changes if the branch has an upstream
25452555
try {
25462556
const branchObj = await this.repository.getBranch(branch);
@@ -2554,8 +2564,28 @@ export class FolderRepositoryManager extends Disposable {
25542564
}
25552565
}
25562566

2557-
private async checkoutDefaultBranchOnly(branch: string): Promise<void> {
2567+
private async fetchBranch(branch: string, remoteName: string) {
2568+
try {
2569+
await this.repository.fetch({ remote: remoteName, ref: branch });
2570+
await this.repository.createBranch(branch, false);
2571+
await this.repository.setBranchUpstream(branch, `refs/remotes/${remoteName}/${branch}`);
2572+
} catch (e) {
2573+
Logger.error(`Failed to fetch branch ${branch}: ${e}`, this.id);
2574+
vscode.window.showErrorMessage(vscode.l10n.t('Failed to create branch {0}: {1}', branch, formatError(e)));
2575+
return;
2576+
}
2577+
}
2578+
2579+
private async checkoutDoneBranchOnly(branch: string, remoteName?: string): Promise<void> {
25582580
let branchObj: Branch | undefined;
2581+
try {
2582+
branchObj = await this.repository.getBranch(branch);
2583+
} catch (e) {
2584+
if (e.message?.includes('No such branch') && remoteName) {
2585+
await this.fetchBranch(branch, remoteName);
2586+
}
2587+
}
2588+
25592589
try {
25602590
branchObj = await this.repository.getBranch(branch);
25612591

src/github/pullRequestOverview.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { COPILOT_REVIEWER, COPILOT_REVIEWER_ACCOUNT, COPILOT_SWE_AGENT, copilotE
3131
import { commands, contexts } from '../common/executeCommands';
3232
import { disposeAll } from '../common/lifecycle';
3333
import Logger from '../common/logger';
34-
import { DEFAULT_MERGE_METHOD, DELETE_BRANCH_AFTER_MERGE, PR_SETTINGS_NAMESPACE } from '../common/settingKeys';
34+
import { CHECKOUT_DEFAULT_BRANCH, CHECKOUT_PULL_REQUEST_BASE_BRANCH, DEFAULT_MERGE_METHOD, DELETE_BRANCH_AFTER_MERGE, POST_DONE, PR_SETTINGS_NAMESPACE } from '../common/settingKeys';
3535
import { ITelemetry } from '../common/telemetry';
3636
import { EventType, ReviewEvent, SessionLinkInfo, TimelineEvent } from '../common/timelineEvent';
3737
import { asPromise, formatError } from '../common/utils';
@@ -313,6 +313,11 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
313313

314314
this.preLoadInfoNotRequiredForOverview(pullRequest);
315315

316+
const postDoneAction = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<string>(POST_DONE, CHECKOUT_DEFAULT_BRANCH);
317+
const doneCheckoutBranch = postDoneAction.startsWith(CHECKOUT_PULL_REQUEST_BASE_BRANCH)
318+
? pullRequest.base.ref
319+
: defaultBranch;
320+
316321
const context: Partial<PullRequest> = {
317322
...baseContext,
318323
canRequestCopilotReview: copilotUser !== undefined && !isCopilotAlreadyReviewer,
@@ -323,6 +328,7 @@ export class PullRequestOverviewPanel extends IssueOverviewPanel<PullRequestMode
323328
isLocalHeadDeleted: !branchInfo,
324329
head: pullRequest.head ? `${pullRequest.head.owner}/${pullRequest.remote.repositoryName}:${pullRequest.head.ref}` : '',
325330
repositoryDefaultBranch: defaultBranch,
331+
doneCheckoutBranch: doneCheckoutBranch,
326332
status: status[0],
327333
reviewRequirement: status[1],
328334
canUpdateBranch: pullRequest.item.viewerCanUpdate && !isBranchUpToDateWithBase && isUpdateBranchWithGitHubEnabled,

src/github/pullRequestReviewCommon.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export namespace PullRequestReviewCommon {
146146
export async function checkoutDefaultBranch(ctx: ReviewContext, message: IRequestMessage<string>): Promise<void> {
147147
try {
148148
const prBranch = ctx.folderRepositoryManager.repository.state.HEAD?.name;
149-
await ctx.folderRepositoryManager.checkoutDefaultBranch(message.args);
149+
await ctx.folderRepositoryManager.checkoutDefaultBranch(message.args, ctx.item);
150150
if (prBranch) {
151151
await ctx.folderRepositoryManager.cleanupAfterPullRequest(prBranch, ctx.item);
152152
}
@@ -407,7 +407,7 @@ export namespace PullRequestReviewCommon {
407407
return;
408408
}
409409
}
410-
await folderRepositoryManager.checkoutDefaultBranch(defaultBranch);
410+
await folderRepositoryManager.checkoutDefaultBranch(defaultBranch, item);
411411
}
412412
await folderRepositoryManager.repository.deleteBranch(branchInfo!.branch, true);
413413
return deletedBranchTypes.push(action.type);

src/github/views.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export interface PullRequest extends Issue {
8383
commitsCount: number;
8484
projectItems: IProjectItem[] | undefined;
8585
repositoryDefaultBranch: string;
86+
doneCheckoutBranch: string;
8687
emailForCommit?: string;
8788
pendingReviewType?: ReviewType;
8889
status: PullRequestChecks | null;

src/view/reviewManager.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,14 +1189,14 @@ export class ReviewManager extends Disposable {
11891189
const defaultBranch = await this._folderRepoManager.getPullRequestRepositoryDefaultBranch(createdPR);
11901190
if (defaultBranch) {
11911191
if (postCreate === 'checkoutDefaultBranch') {
1192-
await this._folderRepoManager.checkoutDefaultBranch(defaultBranch);
1192+
await this._folderRepoManager.checkoutDefaultBranch(defaultBranch, undefined);
11931193
} if (postCreate === 'checkoutDefaultBranchAndShow') {
11941194
await commands.executeCommand('pr:github.focus');
1195-
await this._folderRepoManager.checkoutDefaultBranch(defaultBranch);
1195+
await this._folderRepoManager.checkoutDefaultBranch(defaultBranch, undefined);
11961196
await this._pullRequestsTree.expandPullRequest(createdPR);
11971197
} else if (postCreate === 'checkoutDefaultBranchAndCopy') {
11981198
await Promise.all([
1199-
this._folderRepoManager.checkoutDefaultBranch(defaultBranch),
1199+
this._folderRepoManager.checkoutDefaultBranch(defaultBranch, undefined),
12001200
vscode.env.clipboard.writeText(createdPR.html_url)
12011201
]);
12021202
}

0 commit comments

Comments
 (0)