Browse Source

Optimizes git calls for focus view

Adds PageableResult class helper
main
Eric Amodio 1 year ago
parent
commit
5999a261c8
4 changed files with 114 additions and 41 deletions
  1. +11
    -12
      src/git/models/branch.ts
  2. +19
    -10
      src/git/models/worktree.ts
  3. +53
    -19
      src/plus/webviews/focus/focusWebview.ts
  4. +31
    -0
      src/system/paging.ts

+ 11
- 12
src/git/models/branch.ts View File

@ -5,6 +5,7 @@ import { formatDate, fromNow } from '../../system/date';
import { debug } from '../../system/decorators/log';
import { memoize } from '../../system/decorators/memoize';
import { getLoggableName } from '../../system/logger';
import { PageableResult } from '../../system/paging';
import { sortCompare } from '../../system/string';
import type { PullRequest, PullRequestState } from './pullRequest';
import type { GitBranchReference, GitReference } from './reference';
@ -291,6 +292,7 @@ export function sortBranches(branches: GitBranch[], options?: BranchSortOptions)
export async function getLocalBranchByUpstream(
repo: Repository,
remoteBranchName: string,
branches?: PageableResult<GitBranch>,
): Promise<GitBranch | undefined> {
let qualifiedRemoteBranchName;
if (remoteBranchName.startsWith('remotes/')) {
@ -300,19 +302,16 @@ export async function getLocalBranchByUpstream(
qualifiedRemoteBranchName = `remotes/${remoteBranchName}`;
}
let branches;
do {
branches = await repo.getBranches(branches != null ? { paging: branches.paging } : undefined);
for (const branch of branches.values) {
if (
!branch.remote &&
branch.upstream?.name != null &&
(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName)
) {
return branch;
}
branches ??= new PageableResult<GitBranch>(p => repo.getBranches(p != null ? { paging: p } : undefined));
for await (const branch of branches.values()) {
if (
!branch.remote &&
branch.upstream?.name != null &&
(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName)
) {
return branch;
}
} while (branches.paging?.more);
}
return undefined;
}

+ 19
- 10
src/git/models/worktree.ts View File

@ -2,6 +2,7 @@ import type { Uri, WorkspaceFolder } from 'vscode';
import { workspace } from 'vscode';
import { Container } from '../../container';
import { memoize } from '../../system/decorators/memoize';
import { PageableResult } from '../../system/paging';
import { normalizePath, relative } from '../../system/path';
import type { GitBranch } from './branch';
import { shortenRevision } from './reference';
@ -84,26 +85,34 @@ export class GitWorktree {
export async function getWorktreeForBranch(
repo: Repository,
branchName: string,
upstreamNames?: string | string[],
upstreamNames: string | string[],
worktrees?: GitWorktree[],
branches?: PageableResult<GitBranch>,
): Promise<GitWorktree | undefined> {
if (upstreamNames != null && !Array.isArray(upstreamNames)) {
upstreamNames = [upstreamNames];
}
const worktrees = await repo.getWorktrees();
worktrees ??= await repo.getWorktrees();
for (const worktree of worktrees) {
if (worktree.branch === branchName) return worktree;
if (upstreamNames == null || worktree.branch == null) continue;
const branch = await repo.getBranch(worktree.branch);
if (
branch?.upstream?.name != null &&
(upstreamNames.includes(branch.upstream.name) ||
(branch.upstream.name.startsWith('remotes/') &&
upstreamNames.includes(branch.upstream.name.substring(8))))
) {
return worktree;
branches ??= new PageableResult<GitBranch>(p => repo.getBranches(p != null ? { paging: p } : undefined));
for await (const branch of branches.values()) {
if (branch.name === worktree.branch) {
if (
branch.upstream?.name != null &&
(upstreamNames.includes(branch.upstream.name) ||
(branch.upstream.name.startsWith('remotes/') &&
upstreamNames.includes(branch.upstream.name.substring(8))))
) {
return worktree;
}
break;
}
}
}

+ 53
- 19
src/plus/webviews/focus/focusWebview.ts View File

@ -20,11 +20,15 @@ import { createReference } from '../../../git/models/reference';
import type { GitRemote } from '../../../git/models/remote';
import type { Repository, RepositoryChangeEvent } from '../../../git/models/repository';
import { RepositoryChange, RepositoryChangeComparisonMode } from '../../../git/models/repository';
import type { GitWorktree } from '../../../git/models/worktree';
import { getWorktreeForBranch } from '../../../git/models/worktree';
import { parseGitRemoteUrl } from '../../../git/parsers/remoteParser';
import type { RichRemoteProvider } from '../../../git/remotes/richRemoteProvider';
import { executeCommand, registerCommand } from '../../../system/command';
import { debug } from '../../../system/decorators/log';
import { Logger } from '../../../system/logger';
import { getLogScope } from '../../../system/logger.scope';
import { PageableResult } from '../../../system/paging';
import { getSettledValue } from '../../../system/promise';
import type { IpcMessage } from '../../../webviews/protocol';
import { onIpc } from '../../../webviews/protocol';
@ -541,22 +545,30 @@ export class FocusWebviewProvider implements WebviewProvider {
richRepos: RepoWithRichRemote[],
force?: boolean,
): Promise<SearchedPullRequestWithRemote[]> {
const scope = getLogScope();
if (force || this._pullRequests == null) {
const allPrs: SearchedPullRequestWithRemote[] = [];
for (const richRepo of richRepos) {
const remote = richRepo.remote;
const prs = await this.container.git.getMyPullRequests(remote);
if (prs == null) {
continue;
const branchesByRepo = new Map<Repository, PageableResult<GitBranch>>();
const worktreesByRepo = new Map<Repository, GitWorktree[]>();
const queries = richRepos.map(r => [r, this.container.git.getMyPullRequests(r.remote)] as const);
for (const [r, query] of queries) {
let prs;
try {
prs = await query;
} catch (ex) {
Logger.error(ex, scope, `Failed to get prs for '${r.remote.url}'`);
}
if (prs == null) continue;
for (const pr of prs) {
if (pr.reasons.length === 0) {
continue;
}
if (pr.reasons.length === 0) continue;
const entry: SearchedPullRequestWithRemote = {
...pr,
repoAndRemote: richRepo,
repoAndRemote: r,
isCurrentWorktree: false,
isCurrentBranch: false,
rank: getPrRank(pr),
@ -566,15 +578,32 @@ export class FocusWebviewProvider implements WebviewProvider {
entry.pullRequest.refs!.head.branch
}`; // TODO@eamodio really need to check for upstream url rather than name
let branches = branchesByRepo.get(entry.repoAndRemote.repo);
if (branches == null) {
branches = new PageableResult<GitBranch>(paging =>
entry.repoAndRemote.repo.getBranches(paging != null ? { paging: paging } : undefined),
);
branchesByRepo.set(entry.repoAndRemote.repo, branches);
}
let worktrees = worktreesByRepo.get(entry.repoAndRemote.repo);
if (worktrees == null) {
worktrees = await entry.repoAndRemote.repo.getWorktrees();
worktreesByRepo.set(entry.repoAndRemote.repo, worktrees);
}
const worktree = await getWorktreeForBranch(
entry.repoAndRemote.repo,
entry.pullRequest.refs!.head.branch,
remoteBranchName,
worktrees,
branches,
);
entry.hasWorktree = worktree != null;
entry.isCurrentWorktree = worktree?.opened === true;
const branch = await getLocalBranchByUpstream(richRepo.repo, remoteBranchName);
const branch = await getLocalBranchByUpstream(r.repo, remoteBranchName, branches);
if (branch) {
entry.branch = branch;
entry.hasLocalBranch = true;
@ -601,22 +630,27 @@ export class FocusWebviewProvider implements WebviewProvider {
@debug({ args: { 0: false } })
private async getMyIssues(richRepos: RepoWithRichRemote[], force?: boolean): Promise<SearchedIssueWithRank[]> {
const scope = getLogScope();
if (force || this._pullRequests == null) {
const allIssues = [];
for (const richRepo of richRepos) {
const remote = richRepo.remote;
const issues = await this.container.git.getMyIssues(remote);
if (issues == null) {
continue;
const queries = richRepos.map(r => [r, this.container.git.getMyIssues(r.remote)] as const);
for (const [r, query] of queries) {
let issues;
try {
issues = await query;
} catch (ex) {
Logger.error(ex, scope, `Failed to get issues for '${r.remote.url}'`);
}
if (issues == null) continue;
for (const issue of issues) {
if (issue.reasons.length === 0) {
continue;
}
if (issue.reasons.length === 0) continue;
allIssues.push({
...issue,
repoAndRemote: richRepo,
repoAndRemote: r,
rank: 0, // getIssueRank(issue),
});
}

+ 31
- 0
src/system/paging.ts View File

@ -0,0 +1,31 @@
import type { PagedResult } from '../git/gitProvider';
export class PageableResult<T> {
private cached: Mutable<PagedResult<T>> | undefined;
constructor(private readonly fetch: (paging: PagedResult<T>['paging']) => Promise<PagedResult<T>>) {}
async *values(): AsyncIterable<NonNullable<T>> {
if (this.cached != null) {
for (const value of this.cached.values) {
yield value;
}
}
let results = this.cached;
while (results == null || results.paging?.more) {
results = await this.fetch(results?.paging);
if (this.cached == null) {
this.cached = results;
} else {
this.cached.values.push(...results.values);
this.cached.paging = results.paging;
}
for (const value of results.values) {
yield value;
}
}
}
}

Loading…
Cancel
Save