From c133ab7c39d924725045ded847a2981acd0645f0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:10:24 +0000 Subject: [PATCH 1/5] Initial plan From 48c1a4ee29174a12971fee2a499f47d33f013725 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:15:26 +0000 Subject: [PATCH 2/5] Initial plan for branch caching in PR view Co-authored-by: ulugbekna <16353531+ulugbekna@users.noreply.github.com> --- src/@types/vscode.proposed.chatSessionsProvider.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/@types/vscode.proposed.chatSessionsProvider.d.ts b/src/@types/vscode.proposed.chatSessionsProvider.d.ts index 69679ab35c..efc76e3cc7 100644 --- a/src/@types/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/@types/vscode.proposed.chatSessionsProvider.d.ts @@ -488,10 +488,11 @@ declare module 'vscode' { * * @param scheme The uri-scheme to register for. This must be unique. * @param provider The provider to register. + * @param defaultChatParticipant The default {@link ChatParticipant chat participant} used in sessions provided by this provider. * * @returns A disposable that unregisters the provider when disposed. */ - export function registerChatSessionContentProvider(scheme: string, provider: ChatSessionContentProvider, chatParticipant: ChatParticipant, capabilities?: ChatSessionCapabilities): Disposable; + export function registerChatSessionContentProvider(scheme: string, provider: ChatSessionContentProvider, defaultChatParticipant: ChatParticipant, capabilities?: ChatSessionCapabilities): Disposable; } export interface ChatContext { From 5253f7cf5d74d24faea7e1d36bd2e3098b7a0d17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:21:19 +0000 Subject: [PATCH 3/5] Add branch caching for faster branch picker in create PR view Co-authored-by: ulugbekna <16353531+ulugbekna@users.noreply.github.com> --- src/github/createPRViewProvider.ts | 22 ++++++++++++- src/github/githubRepository.ts | 14 +++++++++ src/github/quickPicks.ts | 39 ++++++++++++++++-------- src/test/github/githubRepository.test.ts | 20 ++++++++++++ 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/src/github/createPRViewProvider.ts b/src/github/createPRViewProvider.ts index e90e1fced2..3c06737ae2 100644 --- a/src/github/createPRViewProvider.ts +++ b/src/github/createPRViewProvider.ts @@ -15,7 +15,7 @@ import { IAccount, ILabel, IMilestone, IProject, isITeam, ITeam, MergeMethod, Re import { BaseBranchMetadata, PullRequestGitHelper } from './pullRequestGitHelper'; import { PullRequestModel } from './pullRequestModel'; import { getDefaultMergeMethod } from './pullRequestOverview'; -import { branchPicks, getAssigneesQuickPickItems, getLabelOptions, getMilestoneFromQuickPick, getProjectFromQuickPick, reviewersQuickPick } from './quickPicks'; +import { branchPicks, cachedBranchPicks, getAssigneesQuickPickItems, getLabelOptions, getMilestoneFromQuickPick, getProjectFromQuickPick, reviewersQuickPick } from './quickPicks'; import { ISSUE_EXPRESSION, parseIssueExpressionOutput, variableSubstitution } from './utils'; import { ChangeTemplateReply, DisplayLabel, PreReviewState } from './views'; import { RemoteInfo } from '../../common/types'; @@ -1010,9 +1010,22 @@ Don't forget to commit your template file to the repository so that it can be us const params = await super.getCreateParams(); this.model.baseOwner = params.defaultBaseRemote!.owner; this.model.baseBranch = params.defaultBaseBranch!; + // Pre-fetch branches so they're cached when the user opens the branch picker + this.prefetchBranches(params.defaultBaseRemote!); return params; } + private prefetchBranches(baseRemote: RemoteInfo): void { + const githubRepository = this._folderRepositoryManager.findRepo( + repo => repo.remote.owner === baseRemote.owner && repo.remote.repositoryName === baseRemote.repositoryName, + ); + if (githubRepository) { + githubRepository.listBranches(baseRemote.owner, baseRemote.repositoryName, undefined).catch(e => { + Logger.debug(`Pre-fetching branches failed: ${e}`, CreatePullRequestViewProvider.ID); + }); + } + } + private async remotePicks(isBase: boolean): Promise<(vscode.QuickPickItem & { remote?: RemoteInfo })[]> { const remotes = isBase ? await this._folderRepositoryManager.getActiveGitHubRemotes(await this._folderRepositoryManager.getGitHubRemotes()) : this._folderRepositoryManager.gitHubRepositories.map(repo => repo.remote); @@ -1141,6 +1154,13 @@ Don't forget to commit your template file to the repository so that it can be us quickPick.show(); quickPick.busy = true; if (githubRepository) { + // Show cached branches immediately if available, then refresh in the background + const cached = cachedBranchPicks(githubRepository, this._folderRepositoryManager, chooseDifferentRemote, isBase); + if (cached) { + quickPick.items = cached; + const activeItem = message.args.currentBranch ? quickPick.items.find(item => item.branch === message.args.currentBranch) : undefined; + quickPick.activeItems = activeItem ? [activeItem] : []; + } await updateItems(githubRepository, undefined); } else { quickPick.items = await this.remotePicks(isBase); diff --git a/src/github/githubRepository.ts b/src/github/githubRepository.ts index 7b0c608142..52fdcd21a4 100644 --- a/src/github/githubRepository.ts +++ b/src/github/githubRepository.ts @@ -212,6 +212,8 @@ export class GitHubRepository extends Disposable { private _areQueriesLimited: boolean = false; get areQueriesLimited(): boolean { return this._areQueriesLimited; } + private _branchesCache: Map = new Map(); + private _onDidAddPullRequest: vscode.EventEmitter = this._register(new vscode.EventEmitter()); public readonly onDidAddPullRequest: vscode.Event = this._onDidAddPullRequest.event; private _onDidChangePullRequests: vscode.EventEmitter = this._register(new vscode.EventEmitter()); @@ -1341,6 +1343,14 @@ export class GitHubRepository extends Disposable { return data.repository?.ref?.target.oid; } + private static branchesCacheKey(owner: string, repositoryName: string): string { + return `${owner}/${repositoryName}`; + } + + getCachedBranches(owner: string, repositoryName: string): string[] | undefined { + return this._branchesCache.get(GitHubRepository.branchesCacheKey(owner, repositoryName)); + } + async listBranches(owner: string, repositoryName: string, prefix: string | undefined): Promise { const { query, remote, schema } = await this.ensure(); Logger.debug(`List branches for ${owner}/${repositoryName} - enter`, this.id); @@ -1382,6 +1392,10 @@ export class GitHubRepository extends Disposable { if (!branches.includes(defaultBranch)) { branches.unshift(defaultBranch); } + // Cache results for unprefixed queries + if (!prefix) { + this._branchesCache.set(GitHubRepository.branchesCacheKey(owner, repositoryName), branches); + } return branches; } diff --git a/src/github/quickPicks.ts b/src/github/quickPicks.ts index 1da63ea1a1..52f681d4f4 100644 --- a/src/github/quickPicks.ts +++ b/src/github/quickPicks.ts @@ -482,19 +482,7 @@ function getRecentlyUsedBranches(folderRepoManager: FolderRepositoryManager, own return state.branches[repoKey] || []; } -export async function branchPicks(githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean, prefix: string | undefined): Promise<(vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[]> { - let branches: (string | Ref)[]; - if (isBase) { - // For the base, we only want to show branches from GitHub. - branches = await githubRepository.listBranches(githubRepository.remote.owner, githubRepository.remote.repositoryName, prefix); - } else { - // For the compare, we only want to show local branches. - branches = (await folderRepoManager.repository.getBranches({ remote: false })).filter(branch => branch.name); - } - - - const branchNames = branches.map(branch => typeof branch === 'string' ? branch : branch.name!); - +function buildBranchPickItems(branchNames: string[], githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean): (vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[] { // Get recently used branches for base branches only let recentBranches: string[] = []; let otherBranches: string[] = branchNames; @@ -554,4 +542,29 @@ export async function branchPicks(githubRepository: GitHubRepository, folderRepo }); } return branchPicks; +} + +export function cachedBranchPicks(githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean): (vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[] | undefined { + if (!isBase) { + return undefined; + } + const cached = githubRepository.getCachedBranches(githubRepository.remote.owner, githubRepository.remote.repositoryName); + if (!cached) { + return undefined; + } + return buildBranchPickItems(cached, githubRepository, folderRepoManager, changeRepoMessage, isBase); +} + +export async function branchPicks(githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean, prefix: string | undefined): Promise<(vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[]> { + let branches: (string | Ref)[]; + if (isBase) { + // For the base, we only want to show branches from GitHub. + branches = await githubRepository.listBranches(githubRepository.remote.owner, githubRepository.remote.repositoryName, prefix); + } else { + // For the compare, we only want to show local branches. + branches = (await folderRepoManager.repository.getBranches({ remote: false })).filter(branch => branch.name); + } + + const branchNames = branches.map(branch => typeof branch === 'string' ? branch : branch.name!); + return buildBranchPickItems(branchNames, githubRepository, folderRepoManager, changeRepoMessage, isBase); } \ No newline at end of file diff --git a/src/test/github/githubRepository.test.ts b/src/test/github/githubRepository.test.ts index 6d41977abf..778ea9c34c 100644 --- a/src/test/github/githubRepository.test.ts +++ b/src/test/github/githubRepository.test.ts @@ -52,4 +52,24 @@ describe('GitHubRepository', function () { // assert(! dotcomRepository.isGitHubDotCom); }); }); + + describe('getCachedBranches', function () { + it('returns undefined when no branches have been cached', function () { + const url = 'https://github.com/owner/repo'; + const remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom); + const rootUri = Uri.file('C:\\users\\test\\repo'); + const repo = new GitHubRepository(1, remote, rootUri, credentialStore, telemetry); + + assert.strictEqual(repo.getCachedBranches('owner', 'repo'), undefined); + }); + + it('returns undefined for a different owner/repo than what was cached', function () { + const url = 'https://github.com/owner/repo'; + const remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom); + const rootUri = Uri.file('C:\\users\\test\\repo'); + const repo = new GitHubRepository(1, remote, rootUri, credentialStore, telemetry); + + assert.strictEqual(repo.getCachedBranches('other-owner', 'other-repo'), undefined); + }); + }); }); From 8928cc88150226c74c97c055a250bdc03547438c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:23:40 +0000 Subject: [PATCH 4/5] Address review feedback: fix test description Co-authored-by: ulugbekna <16353531+ulugbekna@users.noreply.github.com> --- src/test/github/githubRepository.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/github/githubRepository.test.ts b/src/test/github/githubRepository.test.ts index 778ea9c34c..3dd56b711e 100644 --- a/src/test/github/githubRepository.test.ts +++ b/src/test/github/githubRepository.test.ts @@ -63,7 +63,7 @@ describe('GitHubRepository', function () { assert.strictEqual(repo.getCachedBranches('owner', 'repo'), undefined); }); - it('returns undefined for a different owner/repo than what was cached', function () { + it('returns undefined for an owner/repo with no cache entry', function () { const url = 'https://github.com/owner/repo'; const remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom); const rootUri = Uri.file('C:\\users\\test\\repo'); From cfa56bcf1a62bb3f01114d95052a41ace4edf72a Mon Sep 17 00:00:00 2001 From: Alex Ross <38270282+alexr00@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:39:13 +0100 Subject: [PATCH 5/5] nit + delete fairly useless test --- src/github/githubRepository.ts | 6 +++--- src/test/github/githubRepository.test.ts | 20 -------------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/github/githubRepository.ts b/src/github/githubRepository.ts index 52fdcd21a4..724f9e46a1 100644 --- a/src/github/githubRepository.ts +++ b/src/github/githubRepository.ts @@ -1343,12 +1343,12 @@ export class GitHubRepository extends Disposable { return data.repository?.ref?.target.oid; } - private static branchesCacheKey(owner: string, repositoryName: string): string { + private branchesCacheKey(owner: string, repositoryName: string): string { return `${owner}/${repositoryName}`; } getCachedBranches(owner: string, repositoryName: string): string[] | undefined { - return this._branchesCache.get(GitHubRepository.branchesCacheKey(owner, repositoryName)); + return this._branchesCache.get(this.branchesCacheKey(owner, repositoryName)); } async listBranches(owner: string, repositoryName: string, prefix: string | undefined): Promise { @@ -1394,7 +1394,7 @@ export class GitHubRepository extends Disposable { } // Cache results for unprefixed queries if (!prefix) { - this._branchesCache.set(GitHubRepository.branchesCacheKey(owner, repositoryName), branches); + this._branchesCache.set(this.branchesCacheKey(owner, repositoryName), branches); } return branches; } diff --git a/src/test/github/githubRepository.test.ts b/src/test/github/githubRepository.test.ts index 3dd56b711e..6d41977abf 100644 --- a/src/test/github/githubRepository.test.ts +++ b/src/test/github/githubRepository.test.ts @@ -52,24 +52,4 @@ describe('GitHubRepository', function () { // assert(! dotcomRepository.isGitHubDotCom); }); }); - - describe('getCachedBranches', function () { - it('returns undefined when no branches have been cached', function () { - const url = 'https://github.com/owner/repo'; - const remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom); - const rootUri = Uri.file('C:\\users\\test\\repo'); - const repo = new GitHubRepository(1, remote, rootUri, credentialStore, telemetry); - - assert.strictEqual(repo.getCachedBranches('owner', 'repo'), undefined); - }); - - it('returns undefined for an owner/repo with no cache entry', function () { - const url = 'https://github.com/owner/repo'; - const remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom); - const rootUri = Uri.file('C:\\users\\test\\repo'); - const repo = new GitHubRepository(1, remote, rootUri, credentialStore, telemetry); - - assert.strictEqual(repo.getCachedBranches('other-owner', 'other-repo'), undefined); - }); - }); });