diff --git a/src/env/node/git/git.ts b/src/env/node/git/git.ts index 7ea7b86..1016ab0 100644 --- a/src/env/node/git/git.ts +++ b/src/env/node/git/git.ts @@ -874,18 +874,17 @@ export class Git { ); } - async logStream( + async logStreamTo( repoPath: string, sha: string, limit: number, options?: { configs?: readonly string[]; stdin?: string }, ...args: string[] - ): Promise<[data: string, count: number]> { - const params = ['log']; + ): Promise<[data: string[], count: number]> { + const params = ['log', ...args]; if (options?.stdin) { params.push('--stdin'); } - params.push(...args); const proc = await this.gitSpawn( { cwd: repoPath, configs: options?.configs ?? gitLogDefaultConfigs, stdin: options?.stdin }, @@ -893,12 +892,12 @@ export class Git { '--', ); - const shaRegex = new RegExp(`(^${sha}\x00)|(\x00\x00${sha}\x00)`); + const shaRegex = new RegExp(`(?:^|\x00\x00)${sha}\x00`); let found = false; let count = 0; - return new Promise<[data: string, count: number]>((resolve, reject) => { + return new Promise<[data: string[], count: number]>((resolve, reject) => { const errData: string[] = []; const data: string[] = []; @@ -915,13 +914,13 @@ export class Git { reject(new Error(errData.join(''))); } - resolve([data.join(''), count]); + resolve([data, count]); } function onData(s: string) { data.push(s); // eslint-disable-next-line no-control-regex - count += (s.match(/\x00\x00[0-9a-f]{40}\x00/g)?.length ?? 0) + 1; + count += s.match(/(?:^|\x00\x00)[0-9a-f]{40}\x00/g)?.length ?? 0; if (!found && shaRegex.test(s)) { found = true; @@ -939,7 +938,7 @@ export class Git { proc.stderr!.removeListener('data', onErrData); proc.kill(); - resolve([data.join(''), count]); + resolve([data, count]); } proc.on('error', onError); diff --git a/src/env/node/git/localGitProvider.ts b/src/env/node/git/localGitProvider.ts index 48f7a0a..b0335ed 100644 --- a/src/env/node/git/localGitProvider.ts +++ b/src/env/node/git/localGitProvider.ts @@ -1632,7 +1632,6 @@ export class LocalGitProvider implements GitProvider, Disposable { this.getRemotes(repoPath), ]); - const limit = defaultLimit; const remotes = getSettledValue(remotesResult); const remoteMap = remotes != null ? new Map(remotes.map(r => [r.name, r])) : new Map(); const selectSha = first(refParser.parse(getSettledValue(refResult) ?? '')); @@ -1655,11 +1654,11 @@ export class LocalGitProvider implements GitProvider, Disposable { async function getCommitsForGraphCore( this: LocalGitProvider, limit: number, - shaOrCursor?: string | { sha: string; skip?: number }, + shaOrCursor?: string | { sha: string; skip: number }, ): Promise { iterations++; - let cursor: { sha: string; skip?: number } | undefined; + let cursor: { sha: string; skip: number } | undefined; let sha: string | undefined; if (shaOrCursor != null) { if (typeof shaOrCursor === 'string') { @@ -1669,7 +1668,7 @@ export class LocalGitProvider implements GitProvider, Disposable { } } - let log: string | undefined; + let log: string | string[] | undefined; let nextPageLimit = limit; let size; @@ -1678,7 +1677,7 @@ export class LocalGitProvider implements GitProvider, Disposable { let data; if (sha) { - [data, limit] = await this.git.logStream( + [data, limit] = await this.git.logStreamTo( repoPath, sha, limit, @@ -1692,38 +1691,36 @@ export class LocalGitProvider implements GitProvider, Disposable { } data = await this.git.log2(repoPath, stdin ? { stdin: stdin } : undefined, ...args); - } - if (cursor || sha) { - const cursorIndex = data.startsWith(`${cursor?.sha ?? sha}\x00`) - ? 0 - : data.indexOf(`\x00\x00${cursor?.sha ?? sha}\x00`); - if (cursorIndex === -1) { - // If we didn't find any new commits, we must have them all so return that we have everything - if (size === data.length) return { repoPath: repoPath, ids: ids, rows: [] }; - - size = data.length; - nextPageLimit = (nextPageLimit === 0 ? defaultPageLimit : nextPageLimit) * 2; - if (cursor?.skip) { + if (cursor) { + const cursorIndex = data.startsWith(`${cursor.sha}\x00`) + ? 0 + : data.indexOf(`\x00\x00${cursor.sha}\x00`); + if (cursorIndex === -1) { + // If we didn't find any new commits, we must have them all so return that we have everything + if (size === data.length) return { repoPath: repoPath, ids: ids, rows: [] }; + + size = data.length; + nextPageLimit = (nextPageLimit === 0 ? defaultPageLimit : nextPageLimit) * 2; cursor.skip -= Math.floor(cursor.skip * 0.1); - } - continue; - } + continue; + } - // if (cursorIndex > 0 && cursor != null) { - // const duplicates = data.substring(0, cursorIndex); - // if (data.length - duplicates.length < (size ?? data.length) / 4) { - // size = data.length; - // nextPageLimit = (nextPageLimit === 0 ? defaultPageLimit : nextPageLimit) * 2; - // continue; - // } + // if (cursorIndex > 0 && cursor != null) { + // const duplicates = data.substring(0, cursorIndex); + // if (data.length - duplicates.length < (size ?? data.length) / 4) { + // size = data.length; + // nextPageLimit = (nextPageLimit === 0 ? defaultPageLimit : nextPageLimit) * 2; + // continue; + // } - // // Substract out any duplicate commits (regex is faster than parsing and counting) - // nextPageLimit -= (duplicates.match(/\0\0[0-9a-f]{40}\0/g)?.length ?? 0) + 1; + // // Substract out any duplicate commits (regex is faster than parsing and counting) + // nextPageLimit -= (duplicates.match(/\0\0[0-9a-f]{40}\0/g)?.length ?? 0) + 1; - // data = data.substring(cursorIndex + 2); - // } + // data = data.substring(cursorIndex + 2); + // } + } } if (!data) return { repoPath: repoPath, ids: ids, rows: [] }; @@ -1867,7 +1864,7 @@ export class LocalGitProvider implements GitProvider, Disposable { }; } - return getCommitsForGraphCore.call(this, limit, selectSha); + return getCommitsForGraphCore.call(this, defaultLimit, selectSha); } @log() diff --git a/src/git/parsers/logParser.ts b/src/git/parsers/logParser.ts index 317ac10..6832957 100644 --- a/src/git/parsers/logParser.ts +++ b/src/git/parsers/logParser.ts @@ -68,7 +68,7 @@ interface LogEntry { export type Parser = { arguments: string[]; - parse: (data: string) => Generator; + parse: (data: string | string[]) => Generator; }; type ParsedEntryFile = { status: string; path: string; originalPath?: string }; @@ -142,7 +142,7 @@ export function createLogParser>( args.push(...options.additionalArgs); } - function* parse(data: string): Generator { + function* parse(data: string | string[]): Generator { let entry: T = {} as any; let fieldCount = 0; let field; @@ -179,7 +179,7 @@ export function createLogParserSingle(field: string): Parser { const format = field; const args = ['-z', `--format=${format}`]; - function* parse(data: string): Generator { + function* parse(data: string | string[]): Generator { let field; const fields = getLines(data, '\0'); diff --git a/src/system/string.ts b/src/system/string.ts index f5843d6..8872e39 100644 --- a/src/system/string.ts +++ b/src/system/string.ts @@ -121,16 +121,46 @@ export function getDurationMilliseconds(start: [number, number]) { return secs * 1000 + Math.floor(nanosecs / 1000000); } -export function* getLines(s: string, char: string = '\n'): IterableIterator { - let i = 0; - while (i < s.length) { - let j = s.indexOf(char, i); - if (j === -1) { - j = s.length; +export function* getLines(data: string | string[], char: string = '\n'): IterableIterator { + if (typeof data === 'string') { + let i = 0; + while (i < data.length) { + let j = data.indexOf(char, i); + if (j === -1) { + j = data.length; + } + + yield data.substring(i, j); + i = j + 1; + } + + return; + } + + let count = 0; + let leftover: string | undefined; + for (let s of data) { + count++; + if (leftover) { + s = leftover + s; + leftover = undefined; } - yield s.substring(i, j); - i = j + 1; + let i = 0; + while (i < s.length) { + let j = s.indexOf(char, i); + if (j === -1) { + if (count === data.length) { + j = s.length; + } else { + leftover = s.substring(i); + break; + } + } + + yield s.substring(i, j); + i = j + 1; + } } }