Skip to content

Commit 5f5047d

Browse files
Copilotulugbeknaalexr00
authored
Cache branch names for faster target branch picker in Create PR view (#8597)
* Initial plan * Initial plan for branch caching in PR view Co-authored-by: ulugbekna <16353531+ulugbekna@users.noreply.github.com> * Add branch caching for faster branch picker in create PR view Co-authored-by: ulugbekna <16353531+ulugbekna@users.noreply.github.com> * Address review feedback: fix test description Co-authored-by: ulugbekna <16353531+ulugbekna@users.noreply.github.com> * nit + delete fairly useless test --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ulugbekna <16353531+ulugbekna@users.noreply.github.com> Co-authored-by: Alex Ross <38270282+alexr00@users.noreply.github.com>
1 parent e65ec30 commit 5f5047d

File tree

3 files changed

+61
-14
lines changed

3 files changed

+61
-14
lines changed

src/github/createPRViewProvider.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { IAccount, ILabel, IMilestone, IProject, isITeam, ITeam, MergeMethod, Re
1515
import { BaseBranchMetadata, PullRequestGitHelper } from './pullRequestGitHelper';
1616
import { PullRequestModel } from './pullRequestModel';
1717
import { getDefaultMergeMethod } from './pullRequestOverview';
18-
import { branchPicks, getAssigneesQuickPickItems, getLabelOptions, getMilestoneFromQuickPick, getProjectFromQuickPick, reviewersQuickPick } from './quickPicks';
18+
import { branchPicks, cachedBranchPicks, getAssigneesQuickPickItems, getLabelOptions, getMilestoneFromQuickPick, getProjectFromQuickPick, reviewersQuickPick } from './quickPicks';
1919
import { ISSUE_EXPRESSION, parseIssueExpressionOutput, variableSubstitution } from './utils';
2020
import { ChangeTemplateReply, DisplayLabel, PreReviewState } from './views';
2121
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
10101010
const params = await super.getCreateParams();
10111011
this.model.baseOwner = params.defaultBaseRemote!.owner;
10121012
this.model.baseBranch = params.defaultBaseBranch!;
1013+
// Pre-fetch branches so they're cached when the user opens the branch picker
1014+
this.prefetchBranches(params.defaultBaseRemote!);
10131015
return params;
10141016
}
10151017

1018+
private prefetchBranches(baseRemote: RemoteInfo): void {
1019+
const githubRepository = this._folderRepositoryManager.findRepo(
1020+
repo => repo.remote.owner === baseRemote.owner && repo.remote.repositoryName === baseRemote.repositoryName,
1021+
);
1022+
if (githubRepository) {
1023+
githubRepository.listBranches(baseRemote.owner, baseRemote.repositoryName, undefined).catch(e => {
1024+
Logger.debug(`Pre-fetching branches failed: ${e}`, CreatePullRequestViewProvider.ID);
1025+
});
1026+
}
1027+
}
1028+
10161029

10171030
private async remotePicks(isBase: boolean): Promise<(vscode.QuickPickItem & { remote?: RemoteInfo })[]> {
10181031
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
11411154
quickPick.show();
11421155
quickPick.busy = true;
11431156
if (githubRepository) {
1157+
// Show cached branches immediately if available, then refresh in the background
1158+
const cached = cachedBranchPicks(githubRepository, this._folderRepositoryManager, chooseDifferentRemote, isBase);
1159+
if (cached) {
1160+
quickPick.items = cached;
1161+
const activeItem = message.args.currentBranch ? quickPick.items.find(item => item.branch === message.args.currentBranch) : undefined;
1162+
quickPick.activeItems = activeItem ? [activeItem] : [];
1163+
}
11441164
await updateItems(githubRepository, undefined);
11451165
} else {
11461166
quickPick.items = await this.remotePicks(isBase);

src/github/githubRepository.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,8 @@ export class GitHubRepository extends Disposable {
212212
private _areQueriesLimited: boolean = false;
213213
get areQueriesLimited(): boolean { return this._areQueriesLimited; }
214214

215+
private _branchesCache: Map<string, string[]> = new Map();
216+
215217
private _onDidAddPullRequest: vscode.EventEmitter<PullRequestModel> = this._register(new vscode.EventEmitter());
216218
public readonly onDidAddPullRequest: vscode.Event<PullRequestModel> = this._onDidAddPullRequest.event;
217219
private _onDidChangePullRequests: vscode.EventEmitter<PullRequestChangeEvent[]> = this._register(new vscode.EventEmitter());
@@ -1341,6 +1343,14 @@ export class GitHubRepository extends Disposable {
13411343
return data.repository?.ref?.target.oid;
13421344
}
13431345

1346+
private branchesCacheKey(owner: string, repositoryName: string): string {
1347+
return `${owner}/${repositoryName}`;
1348+
}
1349+
1350+
getCachedBranches(owner: string, repositoryName: string): string[] | undefined {
1351+
return this._branchesCache.get(this.branchesCacheKey(owner, repositoryName));
1352+
}
1353+
13441354
async listBranches(owner: string, repositoryName: string, prefix: string | undefined): Promise<string[]> {
13451355
const { query, remote, schema } = await this.ensure();
13461356
Logger.debug(`List branches for ${owner}/${repositoryName} - enter`, this.id);
@@ -1382,6 +1392,10 @@ export class GitHubRepository extends Disposable {
13821392
if (!branches.includes(defaultBranch)) {
13831393
branches.unshift(defaultBranch);
13841394
}
1395+
// Cache results for unprefixed queries
1396+
if (!prefix) {
1397+
this._branchesCache.set(this.branchesCacheKey(owner, repositoryName), branches);
1398+
}
13851399
return branches;
13861400
}
13871401

src/github/quickPicks.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -482,19 +482,7 @@ function getRecentlyUsedBranches(folderRepoManager: FolderRepositoryManager, own
482482
return state.branches[repoKey] || [];
483483
}
484484

485-
export async function branchPicks(githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean, prefix: string | undefined): Promise<(vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[]> {
486-
let branches: (string | Ref)[];
487-
if (isBase) {
488-
// For the base, we only want to show branches from GitHub.
489-
branches = await githubRepository.listBranches(githubRepository.remote.owner, githubRepository.remote.repositoryName, prefix);
490-
} else {
491-
// For the compare, we only want to show local branches.
492-
branches = (await folderRepoManager.repository.getBranches({ remote: false })).filter(branch => branch.name);
493-
}
494-
495-
496-
const branchNames = branches.map(branch => typeof branch === 'string' ? branch : branch.name!);
497-
485+
function buildBranchPickItems(branchNames: string[], githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean): (vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[] {
498486
// Get recently used branches for base branches only
499487
let recentBranches: string[] = [];
500488
let otherBranches: string[] = branchNames;
@@ -554,4 +542,29 @@ export async function branchPicks(githubRepository: GitHubRepository, folderRepo
554542
});
555543
}
556544
return branchPicks;
545+
}
546+
547+
export function cachedBranchPicks(githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean): (vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[] | undefined {
548+
if (!isBase) {
549+
return undefined;
550+
}
551+
const cached = githubRepository.getCachedBranches(githubRepository.remote.owner, githubRepository.remote.repositoryName);
552+
if (!cached) {
553+
return undefined;
554+
}
555+
return buildBranchPickItems(cached, githubRepository, folderRepoManager, changeRepoMessage, isBase);
556+
}
557+
558+
export async function branchPicks(githubRepository: GitHubRepository, folderRepoManager: FolderRepositoryManager, changeRepoMessage: string | undefined, isBase: boolean, prefix: string | undefined): Promise<(vscode.QuickPickItem & { remote?: RemoteInfo, branch?: string })[]> {
559+
let branches: (string | Ref)[];
560+
if (isBase) {
561+
// For the base, we only want to show branches from GitHub.
562+
branches = await githubRepository.listBranches(githubRepository.remote.owner, githubRepository.remote.repositoryName, prefix);
563+
} else {
564+
// For the compare, we only want to show local branches.
565+
branches = (await folderRepoManager.repository.getBranches({ remote: false })).filter(branch => branch.name);
566+
}
567+
568+
const branchNames = branches.map(branch => typeof branch === 'string' ? branch : branch.name!);
569+
return buildBranchPickItems(branchNames, githubRepository, folderRepoManager, changeRepoMessage, isBase);
557570
}

0 commit comments

Comments
 (0)