diff --git a/CHANGELOG.md b/CHANGELOG.md index 453c88b..66b4b11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Fixed +- Fixes [#2125](https://github.com/gitkraken/vscode-gitlens/issues/2125) - "git log" command in version 12.x is very slow - Fixes [#2121](https://github.com/gitkraken/vscode-gitlens/issues/2121) - Typo in GitLens header — thanks to [PR #2122](https://github.com/gitkraken/vscode-gitlens/pull/2122) by Chase Knowlden ([@ChaseKnowlden](https://github.com/ChaseKnowlden)) - Fixes [#2082](https://github.com/gitkraken/vscode-gitlens/issues/2082) - GitLens Home view unreadable in certain themes - Fixes [#2070](https://github.com/gitkraken/vscode-gitlens/issues/2070) - Quoted HTML / JSX syntax is not escaped correctly diff --git a/src/env/node/git/git.ts b/src/env/node/git/git.ts index 5ffd82a..f296d41 100644 --- a/src/env/node/git/git.ts +++ b/src/env/node/git/git.ts @@ -875,29 +875,35 @@ export class Git { async log__file_recent( repoPath: string, fileName: string, - { - ordering, - ref, - similarityThreshold, - }: { ordering?: string | null; ref?: string; similarityThreshold?: number | null } = {}, + options?: { + ordering?: string | null; + ref?: string; + similarityThreshold?: number | null; + cancellation?: CancellationToken; + }, ) { const params = [ 'log', - `-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`, + `-M${options?.similarityThreshold == null ? '' : `${options?.similarityThreshold}%`}`, '-n1', '--format=%H', ]; - if (ordering) { - params.push(`--${ordering}-order`); + if (options?.ordering) { + params.push(`--${options?.ordering}-order`); } - if (ref) { - params.push(ref); + if (options?.ref) { + params.push(options?.ref); } const data = await this.git( - { cwd: repoPath, configs: ['-c', 'log.showSignature=false'], errors: GitErrorHandling.Ignore }, + { + cancellation: options?.cancellation, + cwd: repoPath, + configs: ['-c', 'log.showSignature=false'], + errors: GitErrorHandling.Ignore, + }, ...params, '--', fileName, @@ -911,7 +917,7 @@ export class Git { ref: string, ordering: string | null, file?: string, - token?: CancellationToken, + cancellation?: CancellationToken, ) { const params = ['log', '-n1', '--no-renames', '--format=%H', `--find-object=${objectId}`, ref]; @@ -925,7 +931,7 @@ export class Git { const data = await this.git( { - cancellationToken: token, + cancellation: cancellation, cwd: repoPath, configs: ['-c', 'log.showSignature=false'], errors: GitErrorHandling.Ignore, diff --git a/src/env/node/git/localGitProvider.ts b/src/env/node/git/localGitProvider.ts index 0352689..0dffcc2 100644 --- a/src/env/node/git/localGitProvider.ts +++ b/src/env/node/git/localGitProvider.ts @@ -3,7 +3,6 @@ import { homedir, hostname, userInfo } from 'os'; import { resolve as resolvePath } from 'path'; import { env as process_env } from 'process'; import { - CancellationTokenSource, Disposable, env, Event, @@ -114,6 +113,7 @@ import { LogCorrelationContext, Logger } from '../../../logger'; import { Messages } from '../../../messages'; import { WorkspaceStorageKeys } from '../../../storage'; import { countStringLength, filterMap } from '../../../system/array'; +import { TimedCancellationSource } from '../../../system/cancellation'; import { gate } from '../../../system/decorators/gate'; import { debug, log } from '../../../system/decorators/log'; import { filterMap as filterMapIterable, find, first, last, some } from '../../../system/iterable'; @@ -129,7 +129,7 @@ import { relative, splitPath, } from '../../../system/path'; -import { any, fastestSettled, PromiseOrValue } from '../../../system/promise'; +import { any, fastestSettled, getSettledValue, PromiseOrValue } from '../../../system/promise'; import { equalsIgnoreCase, getDurationMilliseconds, md5, splitSingle } from '../../../system/string'; import { PathTrie } from '../../../system/trie'; import { compare, fromString } from '../../../system/version'; @@ -3759,32 +3759,28 @@ export class LocalGitProvider implements GitProvider, Disposable { const relativePath = this.getRelativePath(pathOrUri, repoPath); - const blob = await this.git.rev_parse__verify(repoPath, ref, relativePath); - if (blob == null) return GitRevision.deletedOrMissing; - - let cancellation: CancellationTokenSource | undefined; - let timer: ReturnType | undefined; + let cancellation: TimedCancellationSource | undefined; if (options?.timeout != null) { - cancellation = new CancellationTokenSource(); - timer = setTimeout(() => cancellation?.cancel(), options.timeout); + cancellation = new TimedCancellationSource(options.timeout); } - ref = - (await this.git.log__find_object( - repoPath, - blob, - ref, - this.container.config.advanced.commitOrdering, - relativePath, - cancellation?.token, - )) ?? ref; + const [verifiedResult, resolvedResult] = await Promise.allSettled([ + this.git.rev_parse__verify(repoPath, ref, relativePath), + this.git.log__file_recent(repoPath, relativePath, { + ref: ref, + cancellation: cancellation?.token, + }), + ]); + + const verified = getSettledValue(verifiedResult); + if (verified == null) return GitRevision.deletedOrMissing; + const resolved = getSettledValue(resolvedResult); + + const cancelled = cancellation?.token.isCancellationRequested; cancellation?.dispose(); - if (timer != null) { - clearTimeout(timer); - } - return ref; + return cancelled ? ref : resolved ?? ref; } @log() diff --git a/src/env/node/git/shell.ts b/src/env/node/git/shell.ts index c7c2efb..19e1e9f 100644 --- a/src/env/node/git/shell.ts +++ b/src/env/node/git/shell.ts @@ -116,7 +116,7 @@ export function findExecutable(exe: string, args: string[]): { cmd: string; args } export interface RunOptions { - cancellationToken?: CancellationToken; + cancellation?: CancellationToken; cwd?: string; readonly env?: Record; readonly encoding?: TEncoding; @@ -256,7 +256,7 @@ export function run( ); }); - options?.cancellationToken?.onCancellationRequested(() => { + options?.cancellation?.onCancellationRequested(() => { const success = proc.kill(); killed = true; diff --git a/src/git/commandOptions.ts b/src/git/commandOptions.ts index ac02e3e..ac3e039 100644 --- a/src/git/commandOptions.ts +++ b/src/git/commandOptions.ts @@ -14,7 +14,7 @@ export interface GitCommandOptions { local?: boolean; // Below options comes from RunOptions - cancellationToken?: CancellationToken; + cancellation?: CancellationToken; cwd?: string; readonly env?: Record; readonly encoding?: BufferEncoding | 'buffer' | string; diff --git a/src/system/cancellation.ts b/src/system/cancellation.ts new file mode 100644 index 0000000..8c1c080 --- /dev/null +++ b/src/system/cancellation.ts @@ -0,0 +1,24 @@ +import { CancellationToken, CancellationTokenSource, Disposable } from 'vscode'; + +export class TimedCancellationSource implements CancellationTokenSource, Disposable { + private readonly cancellation = new CancellationTokenSource(); + private readonly timer: ReturnType; + + constructor(timeout: number) { + this.timer = setTimeout(() => this.cancellation.cancel(), timeout); + } + + dispose(): void { + clearTimeout(this.timer); + this.cancellation.dispose(); + } + + cancel(): void { + clearTimeout(this.timer); + this.cancellation.cancel(); + } + + get token(): CancellationToken { + return this.cancellation.token; + } +}