Browse Source

Updates branches & tags caching

Avoids default params to reduce garbage
main
Eric Amodio 3 years ago
parent
commit
f894aceb63
1 changed files with 125 additions and 137 deletions
  1. +125
    -137
      src/env/node/git/localGitProvider.ts

+ 125
- 137
src/env/node/git/localGitProvider.ts View File

@ -104,7 +104,8 @@ import { Git, GitErrors, maxGitCliLength } from './git';
import { findGitPath, GitLocation, InvalidGitConfigError, UnableToFindGitError } from './locator'; import { findGitPath, GitLocation, InvalidGitConfigError, UnableToFindGitError } from './locator';
import { fsExists, RunError } from './shell'; import { fsExists, RunError } from './shell';
const emptyStr = '';
const emptyPromise: Promise<GitBlame | GitDiff | GitLog | undefined> = Promise.resolve(undefined);
const emptyPagedResult: PagedResult<any> = Object.freeze({ values: [] });
const RepoSearchWarnings = { const RepoSearchWarnings = {
doesNotExist: /no such file or directory/i, doesNotExist: /no such file or directory/i,
@ -115,7 +116,6 @@ const driveLetterRegex = /(?<=^\/?)([a-zA-Z])(?=:\/)/;
const userConfigRegex = /^user\.(name|email) (.*)$/gm; const userConfigRegex = /^user\.(name|email) (.*)$/gm;
const mappedAuthorRegex = /(.+)\s<(.+)>/; const mappedAuthorRegex = /(.+)\s<(.+)>/;
const emptyPromise: Promise<GitBlame | GitDiff | GitLog | undefined> = Promise.resolve(undefined);
const reflogCommands = ['merge', 'pull']; const reflogCommands = ['merge', 'pull'];
export class LocalGitProvider implements GitProvider, Disposable { export class LocalGitProvider implements GitProvider, Disposable {
@ -126,13 +126,13 @@ export class LocalGitProvider implements GitProvider, Disposable {
return this._onDidChangeRepository.event; return this._onDidChangeRepository.event;
} }
private readonly _branchesCache = new Map<string, Promise<GitBranch[]>>();
private readonly _branchesCache = new Map<string, Promise<PagedResult<GitBranch>>>();
private readonly _contributorsCache = new Map<string, Promise<GitContributor[]>>(); private readonly _contributorsCache = new Map<string, Promise<GitContributor[]>>();
private readonly _mergeStatusCache = new Map<string, GitMergeStatus | null>(); private readonly _mergeStatusCache = new Map<string, GitMergeStatus | null>();
private readonly _rebaseStatusCache = new Map<string, GitRebaseStatus | null>(); private readonly _rebaseStatusCache = new Map<string, GitRebaseStatus | null>();
private readonly _remotesWithApiProviderCache = new Map<string, GitRemote<RichRemoteProvider> | null>(); private readonly _remotesWithApiProviderCache = new Map<string, GitRemote<RichRemoteProvider> | null>();
private readonly _stashesCache = new Map<string, GitStash | null>(); private readonly _stashesCache = new Map<string, GitStash | null>();
private readonly _tagsCache = new Map<string, Promise<GitTag[]>>();
private readonly _tagsCache = new Map<string, Promise<PagedResult<GitTag>>>();
private readonly _trackedCache = new Map<string, PromiseOrValue<boolean>>(); private readonly _trackedCache = new Map<string, PromiseOrValue<boolean>>();
private readonly _userMapCache = new Map<string, GitUser | null>(); private readonly _userMapCache = new Map<string, GitUser | null>();
@ -339,7 +339,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
prefix: (context, folder) => `${context.prefix}(${folder.uri.fsPath})`, prefix: (context, folder) => `${context.prefix}(${folder.uri.fsPath})`,
exit: result => exit: result =>
`returned ${result.length} repositories${ `returned ${result.length} repositories${
result.length !== 0 ? ` (${result.map(r => r.path).join(', ')})` : emptyStr
result.length !== 0 ? ` (${result.map(r => r.path).join(', ')})` : ''
}`, }`,
}) })
private async repositorySearch(folder: WorkspaceFolder): Promise<Repository[]> { private async repositorySearch(folder: WorkspaceFolder): Promise<Repository[]> {
@ -382,9 +382,9 @@ export class LocalGitProvider implements GitProvider, Disposable {
try { try {
repoPaths = await this.repositorySearchCore(uri.fsPath, depth, excludes); repoPaths = await this.repositorySearchCore(uri.fsPath, depth, excludes);
} catch (ex) { } catch (ex) {
const msg: string = ex?.toString() ?? emptyStr;
const msg: string = ex?.toString() ?? '';
if (RepoSearchWarnings.doesNotExist.test(msg)) { if (RepoSearchWarnings.doesNotExist.test(msg)) {
Logger.log(cc, `FAILED${msg ? ` Error: ${msg}` : emptyStr}`);
Logger.log(cc, `FAILED${msg ? ` Error: ${msg}` : ''}`);
} else { } else {
Logger.error(ex, cc, 'FAILED'); Logger.error(ex, cc, 'FAILED');
} }
@ -480,7 +480,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
patch = await Git.diff(uri.repoPath, uri.fsPath, ref1, ref2); patch = await Git.diff(uri.repoPath, uri.fsPath, ref1, ref2);
void (await Git.apply(uri.repoPath, patch)); void (await Git.apply(uri.repoPath, patch));
} catch (ex) { } catch (ex) {
const msg: string = ex?.toString() ?? emptyStr;
const msg: string = ex?.toString() ?? '';
if (patch && /patch does not apply/i.test(msg)) { if (patch && /patch does not apply/i.test(msg)) {
const result = await window.showWarningMessage( const result = await window.showWarningMessage(
'Unable to apply changes cleanly. Retry and allow conflicts?', 'Unable to apply changes cleanly. Retry and allow conflicts?',
@ -525,7 +525,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
try { try {
await Git.checkout(repoPath, ref, options); await Git.checkout(repoPath, ref, options);
} catch (ex) { } catch (ex) {
const msg: string = ex?.toString() ?? emptyStr;
const msg: string = ex?.toString() ?? '';
if (/overwritten by checkout/i.test(msg)) { if (/overwritten by checkout/i.test(msg)) {
void Messages.showGenericErrorMessage( void Messages.showGenericErrorMessage(
`Unable to checkout '${ref}'. Please commit or stash your changes before switching branches`, `Unable to checkout '${ref}'. Please commit or stash your changes before switching branches`,
@ -592,9 +592,9 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log() @log()
async fetch( async fetch(
repoPath: string, repoPath: string,
options: { all?: boolean; branch?: GitBranchReference; prune?: boolean; pull?: boolean; remote?: string } = {},
options?: { all?: boolean; branch?: GitBranchReference; prune?: boolean; pull?: boolean; remote?: string },
): Promise<void> { ): Promise<void> {
const { branch: branchRef, ...opts } = options;
const { branch: branchRef, ...opts } = options ?? {};
if (GitReference.isBranch(branchRef)) { if (GitReference.isBranch(branchRef)) {
const repo = await this.container.git.getRepository(repoPath); const repo = await this.container.git.getRepository(repoPath);
const branch = await repo?.getBranch(branchRef?.name); const branch = await repo?.getBranch(branchRef?.name);
@ -604,7 +604,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
branch: branch.getNameWithoutRemote(), branch: branch.getNameWithoutRemote(),
remote: branch.getRemoteName()!, remote: branch.getRemoteName()!,
upstream: branch.getTrackingWithoutRemote()!, upstream: branch.getTrackingWithoutRemote()!,
pull: options.pull,
pull: options?.pull,
}); });
} }
@ -1010,16 +1010,16 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log({ args: { 1: false } }) @log({ args: { 1: false } })
async getBranches( async getBranches(
repoPath: string | undefined, repoPath: string | undefined,
options: {
options?: {
filter?: (b: GitBranch) => boolean; filter?: (b: GitBranch) => boolean;
sort?: boolean | BranchSortOptions; sort?: boolean | BranchSortOptions;
} = {},
},
): Promise<PagedResult<GitBranch>> { ): Promise<PagedResult<GitBranch>> {
if (repoPath == null) return { values: [] };
if (repoPath == null) return emptyPagedResult;
let branchesPromise = this.useCaching ? this._branchesCache.get(repoPath) : undefined;
if (branchesPromise == null) {
async function load(this: LocalGitProvider) {
let resultsPromise = this.useCaching ? this._branchesCache.get(repoPath) : undefined;
if (resultsPromise == null) {
async function load(this: LocalGitProvider): Promise<PagedResult<GitBranch>> {
try { try {
const data = await Git.for_each_ref__branch(repoPath!, { all: true }); const data = await Git.for_each_ref__branch(repoPath!, { all: true });
// If we don't get any data, assume the repo doesn't have any commits yet so check if we have a current branch // If we don't get any data, assume the repo doesn't have any commits yet so check if we have a current branch
@ -1052,21 +1052,21 @@ export class LocalGitProvider implements GitProvider, Disposable {
); );
} }
return current != null ? [current] : [];
return current != null ? { values: [current] } : emptyPagedResult;
} }
return GitBranchParser.parse(data, repoPath!);
return { values: GitBranchParser.parse(data, repoPath!) };
} catch (ex) { } catch (ex) {
this._branchesCache.delete(repoPath!); this._branchesCache.delete(repoPath!);
return [];
return emptyPagedResult;
} }
} }
branchesPromise = load.call(this);
resultsPromise = load.call(this);
if (this.useCaching) { if (this.useCaching) {
this._branchesCache.set(repoPath, branchesPromise);
this._branchesCache.set(repoPath, resultsPromise);
if (!(await this.container.git.getRepository(repoPath))?.supportsChangeEvents) { if (!(await this.container.git.getRepository(repoPath))?.supportsChangeEvents) {
this._branchesCache.delete(repoPath); this._branchesCache.delete(repoPath);
@ -1074,17 +1074,20 @@ export class LocalGitProvider implements GitProvider, Disposable {
} }
} }
let branches = await branchesPromise;
if (options.filter != null) {
branches = branches.filter(options.filter);
let result = await resultsPromise;
if (options?.filter != null) {
result = {
...result,
values: result.values.filter(options.filter),
};
} }
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (options.sort) {
GitBranch.sort(branches, typeof options.sort === 'boolean' ? undefined : options.sort);
if (options?.sort) {
GitBranch.sort(result.values, typeof options.sort === 'boolean' ? undefined : options.sort);
} }
return { values: branches };
return result;
} }
// @log() // @log()
@ -1187,23 +1190,26 @@ export class LocalGitProvider implements GitProvider, Disposable {
async getCommitForFile( async getCommitForFile(
repoPath: string | undefined, repoPath: string | undefined,
uri: Uri, uri: Uri,
options: { ref?: string; firstIfNotFound?: boolean; range?: Range; reverse?: boolean } = {},
options?: { ref?: string; firstIfNotFound?: boolean; range?: Range; reverse?: boolean },
): Promise<GitLogCommit | undefined> { ): Promise<GitLogCommit | undefined> {
const cc = Logger.getCorrelationContext(); const cc = Logger.getCorrelationContext();
try { try {
const log = await this.getLogForFile(repoPath, uri.fsPath, { const log = await this.getLogForFile(repoPath, uri.fsPath, {
limit: 2, limit: 2,
ref: options.ref,
range: options.range,
reverse: options.reverse,
ref: options?.ref,
range: options?.range,
reverse: options?.reverse,
}); });
if (log == null) return undefined; if (log == null) return undefined;
const commit = options.ref ? log.commits.get(options.ref) : undefined;
if (commit == null && !options.firstIfNotFound && options.ref) {
// If the ref isn't a valid sha we will never find it, so let it fall through so we return the first
if (GitRevision.isSha(options.ref) || GitRevision.isUncommitted(options.ref)) return undefined;
let commit;
if (options?.ref) {
const commit = log.commits.get(options.ref);
if (commit == null && !options?.firstIfNotFound) {
// If the ref isn't a valid sha we will never find it, so let it fall through so we return the first
if (GitRevision.isSha(options.ref) || GitRevision.isUncommitted(options.ref)) return undefined;
}
} }
return commit ?? Iterables.first(log.commits.values()); return commit ?? Iterables.first(log.commits.values());
@ -1581,7 +1587,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
repoPath: string, repoPath: string,
ref1?: string, ref1?: string,
ref2?: string, ref2?: string,
options: { filters?: GitDiffFilter[]; similarityThreshold?: number } = {},
options?: { filters?: GitDiffFilter[]; similarityThreshold?: number },
): Promise<GitFile[] | undefined> { ): Promise<GitFile[] | undefined> {
try { try {
const data = await Git.diff__name_status(repoPath, ref1, ref2, { const data = await Git.diff__name_status(repoPath, ref1, ref2, {
@ -1611,10 +1617,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log() @log()
async getLog( async getLog(
repoPath: string, repoPath: string,
{
ref,
...options
}: {
options?: {
all?: boolean; all?: boolean;
authors?: string[]; authors?: string[];
limit?: number; limit?: number;
@ -1623,16 +1626,16 @@ export class LocalGitProvider implements GitProvider, Disposable {
ref?: string; ref?: string;
reverse?: boolean; reverse?: boolean;
since?: string; since?: string;
} = {},
},
): Promise<GitLog | undefined> { ): Promise<GitLog | undefined> {
const limit = options.limit ?? this.container.config.advanced.maxListItems ?? 0;
const limit = options?.limit ?? this.container.config.advanced.maxListItems ?? 0;
try { try {
const data = await Git.log(repoPath, ref, {
const data = await Git.log(repoPath, options?.ref, {
...options, ...options,
limit: limit, limit: limit,
merges: options.merges == null ? true : options.merges,
ordering: options.ordering ?? this.container.config.advanced.commitOrdering,
merges: options?.merges == null ? true : options.merges,
ordering: options?.ordering ?? this.container.config.advanced.commitOrdering,
similarityThreshold: this.container.config.advanced.similarityThreshold, similarityThreshold: this.container.config.advanced.similarityThreshold,
}); });
const log = GitLogParser.parse( const log = GitLogParser.parse(
@ -1640,18 +1643,17 @@ export class LocalGitProvider implements GitProvider, Disposable {
GitCommitType.Log, GitCommitType.Log,
repoPath, repoPath,
undefined, undefined,
ref,
options?.ref,
await this.getCurrentUser(repoPath), await this.getCurrentUser(repoPath),
limit, limit,
options.reverse!,
options?.reverse ?? false,
undefined, undefined,
); );
if (log != null) { if (log != null) {
const opts = { ...options, ref: ref };
log.query = (limit: number | undefined) => this.getLog(repoPath, { ...opts, limit: limit });
log.query = (limit: number | undefined) => this.getLog(repoPath, { ...options, limit: limit });
if (log.hasMore) { if (log.hasMore) {
log.more = this.getLogMoreFn(log, opts);
log.more = this.getLogMoreFn(log, options);
} }
} }
@ -1664,10 +1666,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log() @log()
async getLogRefsOnly( async getLogRefsOnly(
repoPath: string, repoPath: string,
{
ref,
...options
}: {
options?: {
authors?: string[]; authors?: string[];
limit?: number; limit?: number;
merges?: boolean; merges?: boolean;
@ -1675,20 +1674,20 @@ export class LocalGitProvider implements GitProvider, Disposable {
ref?: string; ref?: string;
reverse?: boolean; reverse?: boolean;
since?: string; since?: string;
} = {},
},
): Promise<Set<string> | undefined> { ): Promise<Set<string> | undefined> {
const limit = options.limit ?? this.container.config.advanced.maxListItems ?? 0;
const limit = options?.limit ?? this.container.config.advanced.maxListItems ?? 0;
try { try {
const data = await Git.log(repoPath, ref, {
authors: options.authors,
const data = await Git.log(repoPath, options?.ref, {
authors: options?.authors,
format: 'refs', format: 'refs',
limit: limit, limit: limit,
merges: options.merges == null ? true : options.merges,
reverse: options.reverse,
merges: options?.merges == null ? true : options.merges,
reverse: options?.reverse,
similarityThreshold: this.container.config.advanced.similarityThreshold, similarityThreshold: this.container.config.advanced.similarityThreshold,
since: options.since,
ordering: options.ordering ?? this.container.config.advanced.commitOrdering,
since: options?.since,
ordering: options?.ordering ?? this.container.config.advanced.commitOrdering,
}); });
const commits = GitLogParser.parseRefsOnly(data); const commits = GitLogParser.parseRefsOnly(data);
return new Set(commits); return new Set(commits);
@ -1699,7 +1698,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
private getLogMoreFn( private getLogMoreFn(
log: GitLog, log: GitLog,
options: {
options?: {
authors?: string[]; authors?: string[];
limit?: number; limit?: number;
merges?: boolean; merges?: boolean;
@ -1722,7 +1721,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (GitRevision.isRange(log.sha)) { if (GitRevision.isRange(log.sha)) {
const moreLog = await this.getLog(log.repoPath, { const moreLog = await this.getLog(log.repoPath, {
...options, ...options,
limit: moreLimit === 0 ? 0 : (options.limit ?? 0) + moreLimit,
limit: moreLimit === 0 ? 0 : (options?.limit ?? 0) + moreLimit,
}); });
// If we can't find any more, assume we have everything // If we can't find any more, assume we have everything
if (moreLog == null) return { ...log, hasMore: false }; if (moreLog == null) return { ...log, hasMore: false };
@ -1773,12 +1772,12 @@ export class LocalGitProvider implements GitProvider, Disposable {
async getLogForSearch( async getLogForSearch(
repoPath: string, repoPath: string,
search: SearchPattern, search: SearchPattern,
options: { limit?: number; ordering?: string | null; skip?: number } = {},
options?: { limit?: number; ordering?: string | null; skip?: number },
): Promise<GitLog | undefined> { ): Promise<GitLog | undefined> {
search = { matchAll: false, matchCase: false, matchRegex: true, ...search }; search = { matchAll: false, matchCase: false, matchRegex: true, ...search };
try { try {
const limit = options.limit ?? this.container.config.advanced.maxSearchItems ?? 0;
const limit = options?.limit ?? this.container.config.advanced.maxSearchItems ?? 0;
const similarityThreshold = this.container.config.advanced.similarityThreshold; const similarityThreshold = this.container.config.advanced.similarityThreshold;
const operations = SearchPattern.parseSearchOperations(search.pattern); const operations = SearchPattern.parseSearchOperations(search.pattern);
@ -1893,7 +1892,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
private getLogForSearchMoreFn( private getLogForSearchMoreFn(
log: GitLog, log: GitLog,
search: SearchPattern, search: SearchPattern,
options: { limit?: number; ordering?: string | null },
options?: { limit?: number; ordering?: string | null },
): (limit: number | undefined) => Promise<GitLog> { ): (limit: number | undefined) => Promise<GitLog> {
return async (limit: number | undefined) => { return async (limit: number | undefined) => {
limit = limit ?? this.container.config.advanced.maxSearchItems ?? 0; limit = limit ?? this.container.config.advanced.maxSearchItems ?? 0;
@ -1942,7 +1941,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
async getLogForFile( async getLogForFile(
repoPath: string | undefined, repoPath: string | undefined,
fileName: string, fileName: string,
options: {
options?: {
all?: boolean; all?: boolean;
limit?: number; limit?: number;
ordering?: string | null; ordering?: string | null;
@ -1952,7 +1951,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
reverse?: boolean; reverse?: boolean;
since?: string; since?: string;
skip?: number; skip?: number;
} = {},
},
): Promise<GitLog | undefined> { ): Promise<GitLog | undefined> {
if (repoPath != null && repoPath === Strings.normalizePath(fileName)) { if (repoPath != null && repoPath === Strings.normalizePath(fileName)) {
throw new Error(`File name cannot match the repository path; fileName=${fileName}`); throw new Error(`File name cannot match the repository path; fileName=${fileName}`);
@ -2011,7 +2010,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
if (options.ref != null || options.limit != null) { if (options.ref != null || options.limit != null) {
// Since we are looking for partial log, see if we have the log of the whole file // Since we are looking for partial log, see if we have the log of the whole file
const cachedLog = doc.state.get<CachedLog>( const cachedLog = doc.state.get<CachedLog>(
`log${options.renames ? ':follow' : emptyStr}${options.reverse ? ':reverse' : emptyStr}`,
`log${options.renames ? ':follow' : ''}${options.reverse ? ':reverse' : ''}`,
); );
if (cachedLog != null) { if (cachedLog != null) {
if (options.ref == null) { if (options.ref == null) {
@ -2033,12 +2032,12 @@ export class LocalGitProvider implements GitProvider, Disposable {
log.commits.entries(), log.commits.entries(),
([ref, c]) => { ([ref, c]) => {
if (skip) { if (skip) {
if (ref !== options.ref) return undefined;
if (ref !== options?.ref) return undefined;
skip = false; skip = false;
} }
i++; i++;
if (options.limit != null && i > options.limit) {
if (options?.limit != null && i > options.limit) {
return undefined; return undefined;
} }
@ -2199,10 +2198,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
ref: options.all ? undefined : moreUntil == null ? `${ref}^` : `${moreUntil}^..${ref}^`, ref: options.all ? undefined : moreUntil == null ? `${ref}^` : `${moreUntil}^..${ref}^`,
skip: options.all ? log.count : undefined, skip: options.all ? log.count : undefined,
}); });
if (moreLog == null) {
// If we can't find any more, assume we have everything
return { ...log, hasMore: false };
}
// If we can't find any more, assume we have everything
if (moreLog == null) return { ...log, hasMore: false };
// Merge authors // Merge authors
const authors = new Map([...log.authors]); const authors = new Map([...log.authors]);
@ -2247,7 +2244,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
} }
@log() @log()
async getMergeBase(repoPath: string, ref1: string, ref2: string, options: { forkPoint?: boolean } = {}) {
async getMergeBase(repoPath: string, ref1: string, ref2: string, options?: { forkPoint?: boolean }) {
const cc = Logger.getCorrelationContext(); const cc = Logger.getCorrelationContext();
try { try {
@ -2708,7 +2705,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
startLine: editorLine != null ? editorLine + 1 : undefined, startLine: editorLine != null ? editorLine + 1 : undefined,
}); });
} catch (ex) { } catch (ex) {
const msg: string = ex?.toString() ?? emptyStr;
const msg: string = ex?.toString() ?? '';
// If the line count is invalid just fallback to the most recent commit // If the line count is invalid just fallback to the most recent commit
if ((ref == null || GitRevision.isUncommittedStaged(ref)) && GitErrors.invalidLineCount.test(msg)) { if ((ref == null || GitRevision.isUncommittedStaged(ref)) && GitErrors.invalidLineCount.test(msg)) {
if (ref == null) { if (ref == null) {
@ -2809,7 +2806,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
async getPullRequestForCommit( async getPullRequestForCommit(
ref: string, ref: string,
remoteOrProvider: GitRemote | RichRemoteProvider, remoteOrProvider: GitRemote | RichRemoteProvider,
{ timeout }: { timeout?: number } = {},
options?: { timeout?: number },
): Promise<PullRequest | undefined> { ): Promise<PullRequest | undefined> {
if (GitRevision.isUncommitted(ref)) return undefined; if (GitRevision.isUncommitted(ref)) return undefined;
@ -2826,8 +2823,8 @@ export class LocalGitProvider implements GitProvider, Disposable {
return promiseOrPR; return promiseOrPR;
} }
if (timeout != null && timeout > 0) {
promiseOrPR = Promises.cancellable(promiseOrPR, timeout);
if (options?.timeout != null && options.timeout > 0) {
promiseOrPR = Promises.cancellable(promiseOrPR, options.timeout);
} }
try { try {
@ -2844,14 +2841,11 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log() @log()
async getIncomingActivity( async getIncomingActivity(
repoPath: string, repoPath: string,
{
limit,
...options
}: { all?: boolean; branch?: string; limit?: number; ordering?: string | null; skip?: number } = {},
options?: { all?: boolean; branch?: string; limit?: number; ordering?: string | null; skip?: number },
): Promise<GitReflog | undefined> { ): Promise<GitReflog | undefined> {
const cc = Logger.getCorrelationContext(); const cc = Logger.getCorrelationContext();
limit = limit ?? this.container.config.advanced.maxListItems ?? 0;
const limit = options?.limit ?? this.container.config.advanced.maxListItems ?? 0;
try { try {
// Pass a much larger limit to reflog, because we aggregate the data and we won't know how many lines we'll need // Pass a much larger limit to reflog, because we aggregate the data and we won't know how many lines we'll need
const data = await Git.reflog(repoPath, { const data = await Git.reflog(repoPath, {
@ -2875,7 +2869,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
private getReflogMoreFn( private getReflogMoreFn(
reflog: GitReflog, reflog: GitReflog,
options: { all?: boolean; branch?: string; limit?: number; ordering?: string | null; skip?: number },
options?: { all?: boolean; branch?: string; limit?: number; ordering?: string | null; skip?: number },
): (limit: number) => Promise<GitReflog> { ): (limit: number) => Promise<GitReflog> {
return async (limit: number | undefined) => { return async (limit: number | undefined) => {
limit = limit ?? this.container.config.advanced.maxSearchItems ?? 0; limit = limit ?? this.container.config.advanced.maxSearchItems ?? 0;
@ -2926,7 +2920,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
}) })
async getRichRemoteProvider( async getRichRemoteProvider(
remotesOrRepoPath: GitRemote[] | string | undefined, remotesOrRepoPath: GitRemote[] | string | undefined,
{ includeDisconnected }: { includeDisconnected?: boolean } = {},
options?: { includeDisconnected?: boolean },
): Promise<GitRemote<RichRemoteProvider> | undefined> { ): Promise<GitRemote<RichRemoteProvider> | undefined> {
if (remotesOrRepoPath == null) return undefined; if (remotesOrRepoPath == null) return undefined;
@ -2934,9 +2928,9 @@ export class LocalGitProvider implements GitProvider, Disposable {
let richRemote = this._remotesWithApiProviderCache.get(cacheKey); let richRemote = this._remotesWithApiProviderCache.get(cacheKey);
if (richRemote != null) return richRemote; if (richRemote != null) return richRemote;
if (richRemote === null && !includeDisconnected) return undefined;
if (richRemote === null && !options?.includeDisconnected) return undefined;
if (includeDisconnected) {
if (options?.includeDisconnected) {
richRemote = this._remotesWithApiProviderCache.get(`disconnected|${cacheKey}`); richRemote = this._remotesWithApiProviderCache.get(`disconnected|${cacheKey}`);
if (richRemote !== undefined) return richRemote ?? undefined; if (richRemote !== undefined) return richRemote ?? undefined;
} }
@ -2999,23 +2993,20 @@ export class LocalGitProvider implements GitProvider, Disposable {
this._remotesWithApiProviderCache.set(cacheKey, null); this._remotesWithApiProviderCache.set(cacheKey, null);
this._remotesWithApiProviderCache.set(`disconnected|${cacheKey}`, remote); this._remotesWithApiProviderCache.set(`disconnected|${cacheKey}`, remote);
if (!includeDisconnected) return undefined;
if (!options?.includeDisconnected) return undefined;
} }
return remote; return remote;
} }
@log() @log()
async getRemotes(
repoPath: string | undefined,
options: { sort?: boolean } = {},
): Promise<GitRemote<RemoteProvider>[]> {
async getRemotes(repoPath: string | undefined, options?: { sort?: boolean }): Promise<GitRemote<RemoteProvider>[]> {
if (repoPath == null) return []; if (repoPath == null) return [];
const repository = await this.container.git.getRepository(repoPath); const repository = await this.container.git.getRepository(repoPath);
const remotes = await (repository != null const remotes = await (repository != null
? repository.getRemotes({ sort: options.sort })
: this.getRemotesCore(repoPath, undefined, { sort: options.sort }));
? repository.getRemotes(options)
: this.getRemotesCore(repoPath, undefined, options));
return remotes.filter(r => r.provider != null) as GitRemote<RemoteProvider>[]; return remotes.filter(r => r.provider != null) as GitRemote<RemoteProvider>[];
} }
@ -3023,7 +3014,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
async getRemotesCore( async getRemotesCore(
repoPath: string | undefined, repoPath: string | undefined,
providers?: RemoteProviders, providers?: RemoteProviders,
options: { sort?: boolean } = {},
options?: { sort?: boolean },
): Promise<GitRemote[]> { ): Promise<GitRemote[]> {
if (repoPath == null) return []; if (repoPath == null) return [];
@ -3034,7 +3025,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
const remotes = GitRemoteParser.parse(data, repoPath, RemoteProviderFactory.factory(providers)); const remotes = GitRemoteParser.parse(data, repoPath, RemoteProviderFactory.factory(providers));
if (remotes == null) return []; if (remotes == null) return [];
if (options.sort) {
if (options?.sort) {
GitRemote.sort(remotes); GitRemote.sort(remotes);
} }
@ -3244,27 +3235,27 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log({ args: { 1: false } }) @log({ args: { 1: false } })
async getTags( async getTags(
repoPath: string | undefined, repoPath: string | undefined,
options: { filter?: (t: GitTag) => boolean; sort?: boolean | TagSortOptions } = {},
options?: { filter?: (t: GitTag) => boolean; sort?: boolean | TagSortOptions },
): Promise<PagedResult<GitTag>> { ): Promise<PagedResult<GitTag>> {
if (repoPath == null) return { values: [] };
if (repoPath == null) return emptyPagedResult;
let tagsPromise = this.useCaching ? this._tagsCache.get(repoPath) : undefined;
if (tagsPromise == null) {
async function load(this: LocalGitProvider) {
let resultsPromise = this.useCaching ? this._tagsCache.get(repoPath) : undefined;
if (resultsPromise == null) {
async function load(this: LocalGitProvider): Promise<PagedResult<GitTag>> {
try { try {
const data = await Git.tag(repoPath!); const data = await Git.tag(repoPath!);
return GitTagParser.parse(data, repoPath!) ?? [];
return { values: GitTagParser.parse(data, repoPath!) ?? [] };
} catch (ex) { } catch (ex) {
this._tagsCache.delete(repoPath!); this._tagsCache.delete(repoPath!);
return [];
return emptyPagedResult;
} }
} }
tagsPromise = load.call(this);
resultsPromise = load.call(this);
if (this.useCaching) { if (this.useCaching) {
this._tagsCache.set(repoPath, tagsPromise);
this._tagsCache.set(repoPath, resultsPromise);
if (!(await this.container.git.getRepository(repoPath))?.supportsChangeEvents) { if (!(await this.container.git.getRepository(repoPath))?.supportsChangeEvents) {
this._tagsCache.delete(repoPath); this._tagsCache.delete(repoPath);
@ -3272,17 +3263,20 @@ export class LocalGitProvider implements GitProvider, Disposable {
} }
} }
let tags = await tagsPromise;
if (options.filter != null) {
tags = tags.filter(options.filter);
let result = await resultsPromise;
if (options?.filter != null) {
result = {
...result,
values: result.values.filter(options.filter),
};
} }
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (options.sort) {
GitTag.sort(tags, typeof options.sort === 'boolean' ? undefined : options.sort);
if (options?.sort) {
GitTag.sort(result.values, typeof options.sort === 'boolean' ? undefined : options.sort);
} }
return { values: tags };
return result;
} }
@log() @log()
@ -3382,19 +3376,17 @@ export class LocalGitProvider implements GitProvider, Disposable {
@log({ args: { 1: false } }) @log({ args: { 1: false } })
async hasBranchOrTag( async hasBranchOrTag(
repoPath: string | undefined, repoPath: string | undefined,
{
filter,
}: {
options?: {
filter?: { branches?: (b: GitBranch) => boolean; tags?: (t: GitTag) => boolean }; filter?: { branches?: (b: GitBranch) => boolean; tags?: (t: GitTag) => boolean };
} = {},
},
) { ) {
const [{ values: branches }, { values: tags }] = await Promise.all([ const [{ values: branches }, { values: tags }] = await Promise.all([
this.getBranches(repoPath, { this.getBranches(repoPath, {
filter: filter?.branches,
filter: options?.filter?.branches,
sort: false, sort: false,
}), }),
this.getTags(repoPath, { this.getTags(repoPath, {
filter: filter?.tags,
filter: options?.filter?.tags,
sort: false, sort: false,
}), }),
]); ]);
@ -3477,7 +3469,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
let tracked = this._trackedCache.get(cacheKey); let tracked = this._trackedCache.get(cacheKey);
if (tracked != null) return tracked; if (tracked != null) return tracked;
tracked = this.isTrackedCore(relativeFilePath, repoPath ?? emptyStr, ref);
tracked = this.isTrackedCore(relativeFilePath, repoPath ?? '', ref);
this._trackedCache.set(cacheKey, tracked); this._trackedCache.set(cacheKey, tracked);
tracked = await tracked; tracked = await tracked;
@ -3518,20 +3510,20 @@ export class LocalGitProvider implements GitProvider, Disposable {
async openDiffTool( async openDiffTool(
repoPath: string, repoPath: string,
uri: Uri, uri: Uri,
options: { ref1?: string; ref2?: string; staged?: boolean; tool?: string } = {},
options?: { ref1?: string; ref2?: string; staged?: boolean; tool?: string },
): Promise<void> { ): Promise<void> {
try { try {
if (!options.tool) {
let tool = options?.tool;
if (!tool) {
const cc = Logger.getCorrelationContext(); const cc = Logger.getCorrelationContext();
options.tool = this.container.config.advanced.externalDiffTool || (await this.getDiffTool(repoPath));
if (options.tool == null) throw new Error('No diff tool found');
tool = this.container.config.advanced.externalDiffTool || (await this.getDiffTool(repoPath));
if (tool == null) throw new Error('No diff tool found');
Logger.log(cc, `Using tool=${options.tool}`);
Logger.log(cc, `Using tool=${tool}`);
} }
const { tool, ...opts } = options;
await Git.difftool(repoPath, uri.fsPath, tool, opts);
await Git.difftool(repoPath, uri.fsPath, tool, options);
} catch (ex) { } catch (ex) {
const msg: string = ex?.toString() ?? ''; const msg: string = ex?.toString() ?? '';
if (msg === 'No diff tool found' || /Unknown .+? tool/.test(msg)) { if (msg === 'No diff tool found' || /Unknown .+? tool/.test(msg)) {
@ -3689,13 +3681,9 @@ export class LocalGitProvider implements GitProvider, Disposable {
} }
@log() @log()
async stashApply(
repoPath: string,
stashName: string,
{ deleteAfter }: { deleteAfter?: boolean } = {},
): Promise<void> {
async stashApply(repoPath: string, stashName: string, options?: { deleteAfter?: boolean }): Promise<void> {
try { try {
await Git.stash__apply(repoPath, stashName, Boolean(deleteAfter));
await Git.stash__apply(repoPath, stashName, Boolean(options?.deleteAfter));
} catch (ex) { } catch (ex) {
if (ex instanceof Error) { if (ex instanceof Error) {
const msg: string = ex.message ?? ''; const msg: string = ex.message ?? '';
@ -3739,7 +3727,7 @@ export class LocalGitProvider implements GitProvider, Disposable {
repoPath: string, repoPath: string,
message?: string, message?: string,
uris?: Uri[], uris?: Uri[],
options: { includeUntracked?: boolean; keepIndex?: boolean } = {},
options?: { includeUntracked?: boolean; keepIndex?: boolean },
): Promise<void> { ): Promise<void> {
if (uris == null) return Git.stash__push(repoPath, message, options); if (uris == null) return Git.stash__push(repoPath, message, options);

Loading…
Cancel
Save