Ver código fonte

Improves startup performance

Remembers & attempts restore of last known git path (per workspace)
Races a git path search while getting the path from SCM
main
Eric Amodio 2 anos atrás
pai
commit
db703217a1
5 arquivos alterados com 85 adições e 38 exclusões
  1. +1
    -0
      src/constants.ts
  2. +1
    -3
      src/container.ts
  3. +29
    -13
      src/env/node/git/localGitProvider.ts
  4. +17
    -7
      src/env/node/git/locator.ts
  5. +37
    -15
      src/system/promise.ts

+ 1
- 0
src/constants.ts Ver arquivo

@ -258,6 +258,7 @@ export interface Usage {
export const enum WorkspaceState {
AssumeRepositoriesOnStartup = 'gitlens:assumeRepositoriesOnStartup',
GitPath = 'gitlens:gitPath',
BranchComparisons = 'gitlens:branch:comparisons',
ConnectedPrefix = 'gitlens:connected:',

+ 1
- 3
src/container.ts Ver arquivo

@ -139,9 +139,7 @@ export class Container {
this._ready = true;
this.registerGitProviders();
queueMicrotask(() => {
this._onReady.fire();
});
queueMicrotask(() => this._onReady.fire());
}
@log()

+ 29
- 13
src/env/node/git/localGitProvider.ts Ver arquivo

@ -23,7 +23,7 @@ import type {
GitExtension,
} from '../../../@types/vscode.git';
import { configuration } from '../../../configuration';
import { BuiltInGitConfiguration, DocumentSchemes, GlyphChars } from '../../../constants';
import { BuiltInGitConfiguration, DocumentSchemes, GlyphChars, WorkspaceState } from '../../../constants';
import type { Container } from '../../../container';
import { StashApplyError, StashApplyErrorReason } from '../../../git/errors';
import {
@ -94,7 +94,7 @@ import { LogCorrelationContext, Logger } from '../../../logger';
import { Messages } from '../../../messages';
import { Arrays, debug, Functions, gate, Iterables, log, Promises, Strings, Versions } from '../../../system';
import { isFolderGlob, normalizePath, splitPath } from '../../../system/path';
import { PromiseOrValue } from '../../../system/promise';
import { any, PromiseOrValue } from '../../../system/promise';
import {
CachedBlame,
CachedDiff,
@ -215,16 +215,6 @@ export class LocalGitProvider implements GitProvider, Disposable {
const scmPromise = this.getScmGitApi();
// Try to use the same git as the built-in vscode git extension, but only wait for a bit
const timeout = 100;
let gitPath;
try {
const gitApi = await Promises.cancellable(scmPromise, timeout);
gitPath = gitApi?.git.path;
} catch {
Logger.log(cc, `Stopped waiting for built-in Git, after ${timeout} ms...`);
}
async function subscribeToScmOpenCloseRepository(
container: Container,
apiPromise: Promise<BuiltInGitApi | undefined>,
@ -249,8 +239,34 @@ export class LocalGitProvider implements GitProvider, Disposable {
}
void subscribeToScmOpenCloseRepository(this.container, scmPromise);
const potentialGitPaths =
configuration.getAny<string | string[]>('git.path') ??
this.container.context.workspaceState.get(WorkspaceState.GitPath, undefined);
const start = hrtime();
const location = await findGitPath(gitPath ?? configuration.getAny<string | string[]>('git.path'));
const findGitPromise = findGitPath(potentialGitPaths);
// Try to use the same git as the built-in vscode git extension, but don't wait for it if we find something faster
const findGitFromSCMPromise = scmPromise.then(gitApi => {
const path = gitApi?.git.path;
if (!path) return findGitPromise;
if (potentialGitPaths != null) {
if (typeof potentialGitPaths === 'string') {
if (path === potentialGitPaths) return findGitPromise;
} else if (potentialGitPaths.includes(path)) {
return findGitPromise;
}
}
return findGitPath(path, false);
});
const location = await any<GitLocation>(findGitPromise, findGitFromSCMPromise);
// Save the found git path, but let things settle first to not impact startup performance
setTimeout(() => {
void this.container.context.workspaceState.update(WorkspaceState.GitPath, location.path);
}, 1000);
if (cc != null) {
cc.exitDetails = ` ${GlyphChars.Dot} Git found (${Strings.getDurationMilliseconds(start)} ms): ${

+ 17
- 7
src/env/node/git/locator.ts Ver arquivo

@ -3,6 +3,7 @@ import { join as joinPaths } from 'path';
import { GlyphChars } from '../../../constants';
import { LogLevel } from '../../../logger';
import { Stopwatch } from '../../../system';
import { any } from '../../../system/promise';
import { findExecutable, run } from './shell';
export class UnableToFindGitError extends Error {
@ -103,20 +104,29 @@ function findGitWin32(): Promise {
.then(null, () => findSpecificGit('git'));
}
export async function findGitPath(paths?: string | string[]): Promise<GitLocation> {
export async function findGitPath(
paths: string | string[] | null | undefined,
search: boolean = true,
): Promise<GitLocation> {
try {
if (paths == null || typeof paths === 'string') {
return await findSpecificGit(paths ?? 'git');
}
for (const path of paths) {
try {
return await findSpecificGit(path);
} catch {}
try {
return any(...paths.map(p => findSpecificGit(p)));
} catch (ex) {
throw new UnableToFindGitError(ex);
}
} catch (ex) {
if (!search) {
return Promise.reject(
ex instanceof InvalidGitConfigError || ex instanceof UnableToFindGitError
? ex
: new UnableToFindGitError(ex),
);
}
throw new UnableToFindGitError();
} catch {
try {
switch (process.platform) {
case 'darwin':

+ 37
- 15
src/system/promise.ts Ver arquivo

@ -4,6 +4,35 @@ import { map } from './iterable';
export type PromiseOrValue<T> = Promise<T> | T;
export function any<T>(...promises: Promise<T>[]): Promise<T> {
return new Promise<T>((resolve, reject) => {
const errors: Error[] = [];
let settled = false;
for (const promise of promises) {
// eslint-disable-next-line no-loop-func
void (async () => {
try {
const result = await promise;
if (settled) return;
resolve(result);
settled = true;
} catch (ex) {
errors.push(ex);
} finally {
if (!settled) {
if (promises.length - errors.length < 1) {
reject(new AggregateError(errors));
settled = true;
}
}
}
})();
}
});
}
export class CancellationError<T extends Promise<any> = Promise<any>> extends Error {
constructor(public readonly promise: T, message: string) {
super(message);
@ -73,21 +102,6 @@ export function cancellable(
});
}
export function first<T>(promises: Promise<T>[], predicate: (value: T) => boolean): Promise<T | undefined> {
const newPromises: Promise<T | undefined>[] = promises.map(
p =>
new Promise<T>((resolve, reject) =>
p.then(value => {
if (predicate(value)) {
resolve(value);
}
}, reject),
),
);
newPromises.push(Promise.all(promises).then(() => undefined));
return Promise.race(newPromises);
}
export function is<T>(obj: PromiseLike<T> | T): obj is Promise<T> {
return obj instanceof Promise || typeof (obj as PromiseLike<T>)?.then === 'function';
}
@ -154,3 +168,11 @@ export async function raceAll(
),
);
}
export class AggregateError extends Error {
constructor(readonly errors: Error[]) {
super(`AggregateError(${errors.length})\n${errors.map(e => `\t${String(e)}`).join('\n')}`);
Error.captureStackTrace?.(this, AggregateError);
}
}

Carregando…
Cancelar
Salvar