Skip to content

Commit 5999a26

Browse files
committed
Optimizes git calls for focus view
Adds PageableResult class helper
1 parent 45ab95f commit 5999a26

File tree

4 files changed

+114
-41
lines changed

4 files changed

+114
-41
lines changed

src/git/models/branch.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { formatDate, fromNow } from '../../system/date';
55
import { debug } from '../../system/decorators/log';
66
import { memoize } from '../../system/decorators/memoize';
77
import { getLoggableName } from '../../system/logger';
8+
import { PageableResult } from '../../system/paging';
89
import { sortCompare } from '../../system/string';
910
import type { PullRequest, PullRequestState } from './pullRequest';
1011
import type { GitBranchReference, GitReference } from './reference';
@@ -291,6 +292,7 @@ export function sortBranches(branches: GitBranch[], options?: BranchSortOptions)
291292
export async function getLocalBranchByUpstream(
292293
repo: Repository,
293294
remoteBranchName: string,
295+
branches?: PageableResult<GitBranch>,
294296
): Promise<GitBranch | undefined> {
295297
let qualifiedRemoteBranchName;
296298
if (remoteBranchName.startsWith('remotes/')) {
@@ -300,19 +302,16 @@ export async function getLocalBranchByUpstream(
300302
qualifiedRemoteBranchName = `remotes/${remoteBranchName}`;
301303
}
302304

303-
let branches;
304-
do {
305-
branches = await repo.getBranches(branches != null ? { paging: branches.paging } : undefined);
306-
for (const branch of branches.values) {
307-
if (
308-
!branch.remote &&
309-
branch.upstream?.name != null &&
310-
(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName)
311-
) {
312-
return branch;
313-
}
305+
branches ??= new PageableResult<GitBranch>(p => repo.getBranches(p != null ? { paging: p } : undefined));
306+
for await (const branch of branches.values()) {
307+
if (
308+
!branch.remote &&
309+
branch.upstream?.name != null &&
310+
(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName)
311+
) {
312+
return branch;
314313
}
315-
} while (branches.paging?.more);
314+
}
316315

317316
return undefined;
318317
}

src/git/models/worktree.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Uri, WorkspaceFolder } from 'vscode';
22
import { workspace } from 'vscode';
33
import { Container } from '../../container';
44
import { memoize } from '../../system/decorators/memoize';
5+
import { PageableResult } from '../../system/paging';
56
import { normalizePath, relative } from '../../system/path';
67
import type { GitBranch } from './branch';
78
import { shortenRevision } from './reference';
@@ -84,26 +85,34 @@ export class GitWorktree {
8485
export async function getWorktreeForBranch(
8586
repo: Repository,
8687
branchName: string,
87-
upstreamNames?: string | string[],
88+
upstreamNames: string | string[],
89+
worktrees?: GitWorktree[],
90+
branches?: PageableResult<GitBranch>,
8891
): Promise<GitWorktree | undefined> {
8992
if (upstreamNames != null && !Array.isArray(upstreamNames)) {
9093
upstreamNames = [upstreamNames];
9194
}
9295

93-
const worktrees = await repo.getWorktrees();
96+
worktrees ??= await repo.getWorktrees();
9497
for (const worktree of worktrees) {
9598
if (worktree.branch === branchName) return worktree;
9699

97100
if (upstreamNames == null || worktree.branch == null) continue;
98101

99-
const branch = await repo.getBranch(worktree.branch);
100-
if (
101-
branch?.upstream?.name != null &&
102-
(upstreamNames.includes(branch.upstream.name) ||
103-
(branch.upstream.name.startsWith('remotes/') &&
104-
upstreamNames.includes(branch.upstream.name.substring(8))))
105-
) {
106-
return worktree;
102+
branches ??= new PageableResult<GitBranch>(p => repo.getBranches(p != null ? { paging: p } : undefined));
103+
for await (const branch of branches.values()) {
104+
if (branch.name === worktree.branch) {
105+
if (
106+
branch.upstream?.name != null &&
107+
(upstreamNames.includes(branch.upstream.name) ||
108+
(branch.upstream.name.startsWith('remotes/') &&
109+
upstreamNames.includes(branch.upstream.name.substring(8))))
110+
) {
111+
return worktree;
112+
}
113+
114+
break;
115+
}
107116
}
108117
}
109118

src/plus/webviews/focus/focusWebview.ts

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ import { createReference } from '../../../git/models/reference';
2020
import type { GitRemote } from '../../../git/models/remote';
2121
import type { Repository, RepositoryChangeEvent } from '../../../git/models/repository';
2222
import { RepositoryChange, RepositoryChangeComparisonMode } from '../../../git/models/repository';
23+
import type { GitWorktree } from '../../../git/models/worktree';
2324
import { getWorktreeForBranch } from '../../../git/models/worktree';
2425
import { parseGitRemoteUrl } from '../../../git/parsers/remoteParser';
2526
import type { RichRemoteProvider } from '../../../git/remotes/richRemoteProvider';
2627
import { executeCommand, registerCommand } from '../../../system/command';
2728
import { debug } from '../../../system/decorators/log';
29+
import { Logger } from '../../../system/logger';
30+
import { getLogScope } from '../../../system/logger.scope';
31+
import { PageableResult } from '../../../system/paging';
2832
import { getSettledValue } from '../../../system/promise';
2933
import type { IpcMessage } from '../../../webviews/protocol';
3034
import { onIpc } from '../../../webviews/protocol';
@@ -541,22 +545,30 @@ export class FocusWebviewProvider implements WebviewProvider<State> {
541545
richRepos: RepoWithRichRemote[],
542546
force?: boolean,
543547
): Promise<SearchedPullRequestWithRemote[]> {
548+
const scope = getLogScope();
549+
544550
if (force || this._pullRequests == null) {
545551
const allPrs: SearchedPullRequestWithRemote[] = [];
546-
for (const richRepo of richRepos) {
547-
const remote = richRepo.remote;
548-
const prs = await this.container.git.getMyPullRequests(remote);
549-
if (prs == null) {
550-
continue;
552+
553+
const branchesByRepo = new Map<Repository, PageableResult<GitBranch>>();
554+
const worktreesByRepo = new Map<Repository, GitWorktree[]>();
555+
556+
const queries = richRepos.map(r => [r, this.container.git.getMyPullRequests(r.remote)] as const);
557+
for (const [r, query] of queries) {
558+
let prs;
559+
try {
560+
prs = await query;
561+
} catch (ex) {
562+
Logger.error(ex, scope, `Failed to get prs for '${r.remote.url}'`);
551563
}
564+
if (prs == null) continue;
552565

553566
for (const pr of prs) {
554-
if (pr.reasons.length === 0) {
555-
continue;
556-
}
567+
if (pr.reasons.length === 0) continue;
568+
557569
const entry: SearchedPullRequestWithRemote = {
558570
...pr,
559-
repoAndRemote: richRepo,
571+
repoAndRemote: r,
560572
isCurrentWorktree: false,
561573
isCurrentBranch: false,
562574
rank: getPrRank(pr),
@@ -566,15 +578,32 @@ export class FocusWebviewProvider implements WebviewProvider<State> {
566578
entry.pullRequest.refs!.head.branch
567579
}`; // TODO@eamodio really need to check for upstream url rather than name
568580

581+
let branches = branchesByRepo.get(entry.repoAndRemote.repo);
582+
if (branches == null) {
583+
branches = new PageableResult<GitBranch>(paging =>
584+
entry.repoAndRemote.repo.getBranches(paging != null ? { paging: paging } : undefined),
585+
);
586+
branchesByRepo.set(entry.repoAndRemote.repo, branches);
587+
}
588+
589+
let worktrees = worktreesByRepo.get(entry.repoAndRemote.repo);
590+
if (worktrees == null) {
591+
worktrees = await entry.repoAndRemote.repo.getWorktrees();
592+
worktreesByRepo.set(entry.repoAndRemote.repo, worktrees);
593+
}
594+
569595
const worktree = await getWorktreeForBranch(
570596
entry.repoAndRemote.repo,
571597
entry.pullRequest.refs!.head.branch,
572598
remoteBranchName,
599+
worktrees,
600+
branches,
573601
);
602+
574603
entry.hasWorktree = worktree != null;
575604
entry.isCurrentWorktree = worktree?.opened === true;
576605

577-
const branch = await getLocalBranchByUpstream(richRepo.repo, remoteBranchName);
606+
const branch = await getLocalBranchByUpstream(r.repo, remoteBranchName, branches);
578607
if (branch) {
579608
entry.branch = branch;
580609
entry.hasLocalBranch = true;
@@ -601,22 +630,27 @@ export class FocusWebviewProvider implements WebviewProvider<State> {
601630

602631
@debug({ args: { 0: false } })
603632
private async getMyIssues(richRepos: RepoWithRichRemote[], force?: boolean): Promise<SearchedIssueWithRank[]> {
633+
const scope = getLogScope();
634+
604635
if (force || this._pullRequests == null) {
605636
const allIssues = [];
606-
for (const richRepo of richRepos) {
607-
const remote = richRepo.remote;
608-
const issues = await this.container.git.getMyIssues(remote);
609-
if (issues == null) {
610-
continue;
637+
638+
const queries = richRepos.map(r => [r, this.container.git.getMyIssues(r.remote)] as const);
639+
for (const [r, query] of queries) {
640+
let issues;
641+
try {
642+
issues = await query;
643+
} catch (ex) {
644+
Logger.error(ex, scope, `Failed to get issues for '${r.remote.url}'`);
611645
}
646+
if (issues == null) continue;
612647

613648
for (const issue of issues) {
614-
if (issue.reasons.length === 0) {
615-
continue;
616-
}
649+
if (issue.reasons.length === 0) continue;
650+
617651
allIssues.push({
618652
...issue,
619-
repoAndRemote: richRepo,
653+
repoAndRemote: r,
620654
rank: 0, // getIssueRank(issue),
621655
});
622656
}

src/system/paging.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { PagedResult } from '../git/gitProvider';
2+
3+
export class PageableResult<T> {
4+
private cached: Mutable<PagedResult<T>> | undefined;
5+
6+
constructor(private readonly fetch: (paging: PagedResult<T>['paging']) => Promise<PagedResult<T>>) {}
7+
8+
async *values(): AsyncIterable<NonNullable<T>> {
9+
if (this.cached != null) {
10+
for (const value of this.cached.values) {
11+
yield value;
12+
}
13+
}
14+
15+
let results = this.cached;
16+
while (results == null || results.paging?.more) {
17+
results = await this.fetch(results?.paging);
18+
19+
if (this.cached == null) {
20+
this.cached = results;
21+
} else {
22+
this.cached.values.push(...results.values);
23+
this.cached.paging = results.paging;
24+
}
25+
26+
for (const value of results.values) {
27+
yield value;
28+
}
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)