From a284728137ade929564a128a0d61d46dfc53fa99 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Sun, 16 Sep 2018 00:44:09 -0400 Subject: [PATCH] Renames sha to ref in more places Renames path to paths Reworks sha regex & methods --- src/commands/showCommitSearch.ts | 2 +- src/git/git.ts | 91 ++++++++++++------------ src/git/gitService.ts | 150 +++++++++++++++++++-------------------- 3 files changed, 121 insertions(+), 122 deletions(-) diff --git a/src/commands/showCommitSearch.ts b/src/commands/showCommitSearch.ts index 49c9338..a8b5435 100644 --- a/src/commands/showCommitSearch.ts +++ b/src/commands/showCommitSearch.ts @@ -80,7 +80,7 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand { args.searchBy = searchByMap.get(match[1]); args.search = args.search.substring(args.search[1] === ' ' ? 2 : 1); } - else if (GitService.isSha(args.search)) { + else if (GitService.isShaLike(args.search)) { args.searchBy = GitRepoSearchBy.Sha; } else { diff --git a/src/git/git.ts b/src/git/git.ts index e0f743d..d65c710 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -1,6 +1,6 @@ 'use strict'; import * as iconv from 'iconv-lite'; -import * as path from 'path'; +import * as paths from 'path'; import { GlyphChars } from '../constants'; import { Logger } from '../logger'; import { Objects, Strings } from '../system'; @@ -165,12 +165,12 @@ function throwExceptionHandler(ex: Error) { let gitInfo: GitLocation; export class Git { - static deletedOrMissingSha = 'ffffffffffffffffffffffffffffffffffffffff'; - static shaRegex = /^[0-9a-f]{40}(\^[0-9]*?)??( -)?$/; - static shaStrictRegex = /^[0-9a-f]{40}$/; - static stagedUncommittedRegex = /^[0]{40}(\^[0-9]*?)??:$/; + static deletedOrMissingSha = '0000000000000000000000000000000000000000-'; + static shaLikeRegex = /(^[0-9a-f]{40}([\^@~:]\S*)?$)|(^[0]{40}(:|-)$)/; + static shaRegex = /(^[0-9a-f]{40}$)|(^[0]{40}(:|-)$)/; + static stagedUncommittedRegex = /^[0]{40}([\^@~]\S*)?:$/; static stagedUncommittedSha = '0000000000000000000000000000000000000000:'; - static uncommittedRegex = /^[0]{40}(\^[0-9]*?)??:??$/; + static uncommittedRegex = /^[0]{40}(?:[\^@~:]\S*)?:?$/; static uncommittedSha = '0000000000000000000000000000000000000000'; static getEncoding(encoding: string | undefined) { @@ -197,39 +197,42 @@ export class Git { ); } - static isResolveRequired(sha: string) { - return Git.isSha(sha) && !Git.shaStrictRegex.test(sha); + static isSha(ref: string) { + return Git.shaRegex.test(ref); } - static isSha(sha: string) { - return Git.shaRegex.test(sha); + static isShaLike(ref: string) { + return Git.shaLikeRegex.test(ref); } - static isStagedUncommitted(sha: string | undefined): boolean { - return sha === undefined ? false : Git.stagedUncommittedRegex.test(sha); + static isStagedUncommitted(ref: string | undefined): boolean { + return ref ? Git.stagedUncommittedRegex.test(ref) : false; } - static isUncommitted(sha: string | undefined) { - return sha === undefined ? false : Git.uncommittedRegex.test(sha); + static isUncommitted(ref: string | undefined) { + return ref ? Git.uncommittedRegex.test(ref) : false; } static shortenSha( - sha: string, + ref: string, strings: { stagedUncommitted?: string; uncommitted?: string; working?: string } = {} ) { strings = { stagedUncommitted: 'index', uncommitted: 'working', working: '', ...strings }; - if (sha === '') return strings.working; - if (Git.isStagedUncommitted(sha)) return strings.stagedUncommitted; - if (Git.isUncommitted(sha)) return strings.uncommitted; + if (ref === '') return strings.working; + if (Git.isUncommitted(ref)) { + if (Git.isStagedUncommitted(ref)) return strings.stagedUncommitted; + + return strings.uncommitted; + } - const index = sha.indexOf('^'); + const index = ref.indexOf('^'); if (index > 6) { // Only grab a max of 5 chars for the suffix - const suffix = sha.substring(index).substring(0, 5); - return `${sha.substring(0, 8 - suffix.length)}${suffix}`; + const suffix = ref.substring(index).substring(0, 5); + return `${ref.substring(0, 8 - suffix.length)}${suffix}`; } - return sha.substring(0, 8); + return ref.substring(0, 8); } static splitPath(fileName: string, repoPath: string | undefined, extract: boolean = true): [string, string] { @@ -243,8 +246,8 @@ export class Git { } } else { - repoPath = Strings.normalizePath(extract ? path.dirname(fileName) : repoPath!); - fileName = Strings.normalizePath(extract ? path.basename(fileName) : fileName); + repoPath = Strings.normalizePath(extract ? paths.dirname(fileName) : repoPath!); + fileName = Strings.normalizePath(extract ? paths.basename(fileName) : fileName); } return [fileName, repoPath]; @@ -260,7 +263,7 @@ export class Git { static async blame( repoPath: string | undefined, fileName: string, - sha?: string, + ref?: string, options: { args?: string[] | null; ignoreWhitespace?: boolean; startLine?: number; endLine?: number } = {} ) { const [file, root] = Git.splitPath(fileName, repoPath); @@ -278,8 +281,8 @@ export class Git { } let stdin; - if (sha) { - if (Git.isStagedUncommitted(sha)) { + if (ref) { + if (Git.isStagedUncommitted(ref)) { // Pipe the blame contents to stdin params.push('--contents', '-'); @@ -287,7 +290,7 @@ export class Git { stdin = await Git.show(repoPath, fileName, ':'); } else { - params.push(sha); + params.push(ref); } } @@ -353,10 +356,10 @@ export class Git { return git({ cwd: repoPath }, 'check-mailmap', author); } - static checkout(repoPath: string, fileName: string, sha: string) { + static checkout(repoPath: string, fileName: string, ref: string) { const [file, root] = Git.splitPath(fileName, repoPath); - return git({ cwd: root }, 'checkout', sha, '--', file); + return git({ cwd: root }, 'checkout', ref, '--', file); } static async config_get(key: string, repoPath?: string) { @@ -379,38 +382,38 @@ export class Git { return data === '' ? undefined : data.trim(); } - static diff(repoPath: string, fileName: string, sha1?: string, sha2?: string, options: { encoding?: string } = {}) { + static diff(repoPath: string, fileName: string, ref1?: string, ref2?: string, options: { encoding?: string } = {}) { const params = ['-c', 'color.diff=false', 'diff', '--diff-filter=M', '-M', '--no-ext-diff', '--minimal']; - if (sha1) { - params.push(Git.isStagedUncommitted(sha1) ? '--staged' : sha1); + if (ref1) { + params.push(Git.isStagedUncommitted(ref1) ? '--staged' : ref1); } - if (sha2) { - params.push(Git.isStagedUncommitted(sha2) ? '--staged' : sha2); + if (ref2) { + params.push(Git.isStagedUncommitted(ref2) ? '--staged' : ref2); } const encoding: BufferEncoding = options.encoding === 'utf8' ? 'utf8' : 'binary'; return git({ cwd: repoPath, encoding: encoding }, ...params, '--', fileName); } - static diff_nameStatus(repoPath: string, sha1?: string, sha2?: string, options: { filter?: string } = {}) { + static diff_nameStatus(repoPath: string, ref1?: string, ref2?: string, options: { filter?: string } = {}) { const params = ['-c', 'color.diff=false', 'diff', '--name-status', '-M', '--no-ext-diff']; if (options && options.filter) { params.push(`--diff-filter=${options.filter}`); } - if (sha1) { - params.push(sha1); + if (ref1) { + params.push(ref1); } - if (sha2) { - params.push(sha2); + if (ref2) { + params.push(ref2); } return git({ cwd: repoPath }, ...params); } - static diff_shortstat(repoPath: string, sha?: string) { + static diff_shortstat(repoPath: string, ref?: string) { const params = ['-c', 'color.diff=false', 'diff', '--shortstat', '--no-ext-diff']; - if (sha) { - params.push(sha); + if (ref) { + params.push(ref); } return git({ cwd: repoPath }, ...params); } @@ -676,7 +679,7 @@ export class Git { if (Git.isStagedUncommitted(ref)) { ref = ':'; } - if (Git.isUncommitted(ref)) throw new Error(`sha=${ref} is uncommitted`); + if (Git.isUncommitted(ref)) throw new Error(`ref=${ref} is uncommitted`); const opts = { cwd: root, diff --git a/src/git/gitService.ts b/src/git/gitService.ts index e0ec452..fbd5b99 100644 --- a/src/git/gitService.ts +++ b/src/git/gitService.ts @@ -1,6 +1,6 @@ 'use strict'; import * as fs from 'fs'; -import * as path from 'path'; +import * as paths from 'path'; import { ConfigurationChangeEvent, Disposable, @@ -284,9 +284,9 @@ export class GitService implements Disposable { Object.create(null) as any ); - let paths; + let repoPaths; try { - paths = await this.repositorySearchCore(folderUri.fsPath, depth, excludes); + repoPaths = await this.repositorySearchCore(folderUri.fsPath, depth, excludes); } catch (ex) { if (RepoSearchWarnings.doesNotExist.test(ex.message || '')) { @@ -303,8 +303,8 @@ export class GitService implements Disposable { return repositories; } - for (let p of paths) { - p = path.dirname(p); + for (let p of repoPaths) { + p = paths.dirname(p); // If we are the same as the root, skip it if (Strings.normalizePath(p) === rootPath) continue; @@ -345,15 +345,15 @@ export class GitService implements Disposable { const folders: string[] = []; const promises = files.map(file => { - const fullPath = path.resolve(root, file); + const path = paths.resolve(root, file); return new Promise((res, rej) => { - fs.stat(fullPath, (err, stat) => { + fs.stat(path, (err, stat) => { if (file === '.git') { - repositories.push(fullPath); + repositories.push(path); } else if (err == null && excludes[file] !== true && stat != null && stat.isDirectory()) { - folders.push(fullPath); + folders.push(path); } res(); @@ -432,17 +432,17 @@ export class GitService implements Disposable { fileName: string, options: { ensureCase: boolean } = { ensureCase: false } ): Promise { - const filePath = path.resolve(repoPath, fileName); - const exists = await new Promise((resolve, reject) => fs.exists(filePath, resolve)); + const path = paths.resolve(repoPath, fileName); + const exists = await new Promise((resolve, reject) => fs.exists(path, resolve)); if (!options.ensureCase || !exists) return exists; // Deal with renames in case only on case-insensative file systems - const normalizedRepoPath = path.normalize(repoPath); - return this.fileExistsWithCase(filePath, normalizedRepoPath, normalizedRepoPath.length); + const normalizedRepoPath = paths.normalize(repoPath); + return this.fileExistsWithCase(path, normalizedRepoPath, normalizedRepoPath.length); } - private async fileExistsWithCase(filePath: string, repoPath: string, repoPathLength: number): Promise { - const dir = path.dirname(filePath); + private async fileExistsWithCase(path: string, repoPath: string, repoPathLength: number): Promise { + const dir = paths.dirname(path); if (dir.length < repoPathLength) return false; if (dir === repoPath) return true; @@ -456,7 +456,7 @@ export class GitService implements Disposable { } }) ); - if (filenames.indexOf(path.basename(filePath)) === -1) { + if (filenames.indexOf(paths.basename(path)) === -1) { return false; } return this.fileExistsWithCase(dir, repoPath, repoPathLength); @@ -526,7 +526,7 @@ export class GitService implements Disposable { [fileName, repoPath] = Git.splitPath(fileName, repoPath); } else { - fileName = Strings.normalizePath(path.relative(repoPath, fileName)); + fileName = Strings.normalizePath(paths.relative(repoPath, fileName)); } } else { @@ -921,10 +921,10 @@ export class GitService implements Disposable { return GitBranchParser.parse(data, repoPath) || []; } - async getChangedFilesCount(repoPath: string, sha?: string): Promise { - Logger.log(`getChangedFilesCount('${repoPath}', '${sha}')`); + async getChangedFilesCount(repoPath: string, ref?: string): Promise { + Logger.log(`getChangedFilesCount('${repoPath}', '${ref}')`); - const data = await Git.diff_shortstat(repoPath, sha); + const data = await Git.diff_shortstat(repoPath, ref); return GitDiffParser.parseShortStat(data); } @@ -974,17 +974,17 @@ export class GitService implements Disposable { return user; } - async getDiffForFile(uri: GitUri, sha1?: string, sha2?: string): Promise { - if (sha1 !== undefined && sha2 === undefined && uri.sha !== undefined) { - sha2 = uri.sha; + async getDiffForFile(uri: GitUri, ref1?: string, ref2?: string): Promise { + if (ref1 !== undefined && ref2 === undefined && uri.sha !== undefined) { + ref2 = uri.sha; } let key = 'diff'; - if (sha1 !== undefined) { - key += `:${sha1}`; + if (ref1 !== undefined) { + key += `:${ref1}`; } - if (sha2 !== undefined) { - key += `:${sha2}`; + if (ref2 !== undefined) { + key += `:${ref2}`; } const doc = await Container.tracker.getOrAdd(uri); @@ -993,27 +993,27 @@ export class GitService implements Disposable { const cachedDiff = doc.state.get(key); if (cachedDiff !== undefined) { Logger.log( - `getDiffForFile[Cached(${key})]('${uri.repoPath}', '${uri.fsPath}', '${sha1}', '${sha2}')` + `getDiffForFile[Cached(${key})]('${uri.repoPath}', '${uri.fsPath}', '${ref1}', '${ref2}')` ); return cachedDiff.item; } } - Logger.log(`getDiffForFile[Not Cached(${key})]('${uri.repoPath}', '${uri.fsPath}', '${sha1}', '${sha2}')`); + Logger.log(`getDiffForFile[Not Cached(${key})]('${uri.repoPath}', '${uri.fsPath}', '${ref1}', '${ref2}')`); if (doc.state === undefined) { doc.state = new GitDocumentState(doc.key); } } else { - Logger.log(`getDiffForFile('${uri.repoPath}', '${uri.fsPath}', '${sha1}', '${sha2}')`); + Logger.log(`getDiffForFile('${uri.repoPath}', '${uri.fsPath}', '${ref1}', '${ref2}')`); } const promise = this.getDiffForFileCore( uri.repoPath, uri.fsPath, - sha1, - sha2, + ref1, + ref2, { encoding: GitService.getEncoding(uri) }, doc, key @@ -1033,8 +1033,8 @@ export class GitService implements Disposable { private async getDiffForFileCore( repoPath: string | undefined, fileName: string, - sha1: string | undefined, - sha2: string | undefined, + ref1: string | undefined, + ref2: string | undefined, options: { encoding?: string }, document: TrackedDocument, key: string @@ -1042,7 +1042,7 @@ export class GitService implements Disposable { const [file, root] = Git.splitPath(fileName, repoPath, false); try { - const data = await Git.diff(root, file, sha1, sha2, options); + const data = await Git.diff(root, file, ref1, ref2, options); const diff = GitDiffParser.parse(data); return diff; } @@ -1067,13 +1067,13 @@ export class GitService implements Disposable { async getDiffForLine( uri: GitUri, line: number, - sha1?: string, - sha2?: string + ref1?: string, + ref2?: string ): Promise { - Logger.log(`getDiffForLine('${uri.repoPath}', '${uri.fsPath}', ${line}, '${sha1}', '${sha2}')`); + Logger.log(`getDiffForLine('${uri.repoPath}', '${uri.fsPath}', ${line}, '${ref1}', '${ref2}')`); try { - const diff = await this.getDiffForFile(uri, sha1, sha2); + const diff = await this.getDiffForFile(uri, ref1, ref2); if (diff === undefined) return undefined; const chunk = diff.chunks.find(c => c.currentPosition.start <= line && c.currentPosition.end >= line); @@ -1088,14 +1088,14 @@ export class GitService implements Disposable { async getDiffStatus( repoPath: string, - sha1?: string, - sha2?: string, + ref1?: string, + ref2?: string, options: { filter?: string } = {} ): Promise { - Logger.log(`getDiffStatus('${repoPath}', '${sha1}', '${sha2}', ${options.filter})`); + Logger.log(`getDiffStatus('${repoPath}', '${ref1}', '${ref2}', ${options.filter})`); try { - const data = await Git.diff_nameStatus(repoPath, sha1, sha2, options); + const data = await Git.diff_nameStatus(repoPath, ref1, ref2, options); const diff = GitDiffParser.parseNameStatus(data, repoPath); return diff; } @@ -1141,8 +1141,8 @@ export class GitService implements Disposable { const commit = options.ref && log.commits.get(options.ref); if (commit === undefined && !options.firstIfNotFound && options.ref) { - // If the sha isn't resolved we will never find it, so let it fall through so we return the first - if (!Git.isResolveRequired(options.ref)) return undefined; + // If the ref isn't a valid sha we will never find it, so let it fall through so we return the first + if (!Git.isSha(options.ref) || Git.isUncommitted(options.ref)) return undefined; } return commit || Iterables.first(log.commits.values()); @@ -1539,7 +1539,7 @@ export class GitService implements Disposable { private async getRepoPathCore(filePath: string, isDirectory: boolean): Promise { try { - return await Git.revparse_toplevel(isDirectory ? filePath : path.dirname(filePath)); + return await Git.revparse_toplevel(isDirectory ? filePath : paths.dirname(filePath)); } catch (ex) { Logger.error(ex, 'GitService.getRepoPathCore'); @@ -1687,31 +1687,31 @@ export class GitService implements Disposable { async getVersionedFile( repoPath: string | undefined, fileName: string, - sha: string | undefined + ref: string | undefined ): Promise { - if (sha === GitService.deletedOrMissingSha) return undefined; + if (ref === GitService.deletedOrMissingSha) return undefined; - Logger.log(`getVersionedFile('${repoPath}', '${fileName}', '${sha}')`); + Logger.log(`getVersionedFile('${repoPath}', '${fileName}', '${ref}')`); - if (!sha || (Git.isUncommitted(sha) && !Git.isStagedUncommitted(sha))) { + if (!ref || (Git.isUncommitted(ref) && !Git.isStagedUncommitted(ref))) { if (await this.fileExists(repoPath!, fileName)) return Uri.file(fileName); return undefined; } - return GitUri.toRevisionUri(sha, fileName, repoPath!); + return GitUri.toRevisionUri(ref, fileName, repoPath!); } - getVersionedFileBuffer(repoPath: string, fileName: string, sha: string) { - Logger.log(`getVersionedFileBuffer('${repoPath}', '${fileName}', ${sha})`); + getVersionedFileBuffer(repoPath: string, fileName: string, ref: string) { + Logger.log(`getVersionedFileBuffer('${repoPath}', '${fileName}', ${ref})`); - return Git.show(repoPath, fileName, sha, { encoding: 'buffer' }); + return Git.show(repoPath, fileName, ref, { encoding: 'buffer' }); } - // getVersionedFileText(repoPath: string, fileName: string, sha: string) { - // Logger.log(`getVersionedFileText('${repoPath}', '${fileName}', ${sha})`); + // getVersionedFileText(repoPath: string, fileName: string, ref: string) { + // Logger.log(`getVersionedFileText('${repoPath}', '${fileName}', ${ref})`); - // return Git.show(repoPath, fileName, sha, { encoding: GitService.getEncoding(repoPath, fileName) }); + // return Git.show(repoPath, fileName, ref, { encoding: GitService.getEncoding(repoPath, fileName) }); // } getVersionedUri(uri: Uri) { @@ -1795,11 +1795,11 @@ export class GitService implements Disposable { if (ref === GitService.deletedOrMissingSha) return false; try { - // Even if we have a sha, check first to see if the file exists (that way the cache will be better reused) + // Even if we have a ref, check first to see if the file exists (that way the cache will be better reused) let tracked = !!(await Git.ls_files(repoPath === undefined ? '' : repoPath, fileName)); if (!tracked && ref !== undefined) { tracked = !!(await Git.ls_files(repoPath === undefined ? '' : repoPath, fileName, { ref: ref })); - // If we still haven't found this file, make sure it wasn't deleted in that sha (i.e. check the previous) + // If we still haven't found this file, make sure it wasn't deleted in that ref (i.e. check the previous) if (!tracked) { tracked = !!(await Git.ls_files(repoPath === undefined ? '' : repoPath, fileName, { ref: `${ref}^` @@ -1841,13 +1841,13 @@ export class GitService implements Disposable { } async resolveReference(repoPath: string, ref: string, uri?: Uri) { - if (ref.endsWith('^3')) return ref; + if (Git.isSha(ref) || !Git.isShaLike(ref) || ref.endsWith('^3')) return ref; Logger.log(`resolveReference('${repoPath}', '${ref}', '${uri && uri.toString(true)}')`); if (uri == null) return (await Git.revparse(repoPath, ref)) || ref; - const fileName = Strings.normalizePath(path.relative(repoPath, uri.fsPath)); + const fileName = Strings.normalizePath(paths.relative(repoPath, uri.fsPath)); const resolvedRef = await Git.log_resolve(repoPath, fileName, ref); const ensuredRef = await Git.cat_file_validate(repoPath, fileName, resolvedRef || ref); @@ -1886,7 +1886,7 @@ export class GitService implements Disposable { static getEncoding(repoPath: string, fileName: string): string; static getEncoding(uri: Uri): string; static getEncoding(repoPathOrUri: string | Uri, fileName?: string): string { - const uri = typeof repoPathOrUri === 'string' ? Uri.file(path.join(repoPathOrUri, fileName!)) : repoPathOrUri; + const uri = typeof repoPathOrUri === 'string' ? Uri.file(paths.join(repoPathOrUri, fileName!)) : repoPathOrUri; return Git.getEncoding(workspace.getConfiguration('files', uri).get('encoding')); } @@ -1915,34 +1915,30 @@ export class GitService implements Disposable { return Git.getGitVersion(); } - static isResolveRequired(sha: string): boolean { - return Git.isResolveRequired(sha); + static isShaLike(ref: string): boolean { + return Git.isShaLike(ref); } - static isSha(sha: string): boolean { - return Git.isSha(sha); + static isStagedUncommitted(ref: string | undefined): boolean { + return Git.isStagedUncommitted(ref); } - static isStagedUncommitted(sha: string | undefined): boolean { - return Git.isStagedUncommitted(sha); - } - - static isUncommitted(sha: string | undefined): boolean { - return Git.isUncommitted(sha); + static isUncommitted(ref: string | undefined): boolean { + return Git.isUncommitted(ref); } static shortenSha( - sha: string | undefined, + ref: string | undefined, strings: { deletedOrMissing?: string; stagedUncommitted?: string; uncommitted?: string; working?: string } = {} ) { - if (sha === undefined) return undefined; + if (ref === undefined) return undefined; strings = { deletedOrMissing: '(deleted)', working: '', ...strings }; - if (sha === '') return strings.working; - if (sha === GitService.deletedOrMissingSha) return strings.deletedOrMissing; + if (ref === '') return strings.working; + if (ref === GitService.deletedOrMissingSha) return strings.deletedOrMissing; - return Git.isSha(sha) || Git.isStagedUncommitted(sha) ? Git.shortenSha(sha, strings) : sha; + return Git.isShaLike(ref) || Git.isStagedUncommitted(ref) ? Git.shortenSha(ref, strings) : ref; } static compareGitVersion(version: string, throwIfLessThan?: Error) {