diff --git a/src/git/git.ts b/src/git/git.ts index bf9beb3..ae5a325 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -91,20 +91,20 @@ export class Git { static splitPath(fileName: string, repoPath?: string): [string, string] { if (repoPath) { + fileName = this.normalizePath(fileName); + repoPath = this.normalizePath(repoPath); + const normalizedRepoPath = (repoPath.endsWith('/') ? repoPath : `${repoPath}/`).toLowerCase(); if (fileName.toLowerCase().startsWith(normalizedRepoPath)) { fileName = fileName.substring(normalizedRepoPath.length); } } else { - repoPath = path.dirname(fileName); - fileName = path.basename(fileName); + repoPath = this.normalizePath(path.dirname(fileName)); + fileName = this.normalizePath(path.basename(fileName)); } - return [ - this.normalizePath(fileName), - this.normalizePath(repoPath) - ]; + return [ fileName, repoPath ]; } // Git commands @@ -182,7 +182,7 @@ export class Git { } static log_file(repoPath: string, fileName: string, sha?: string, maxCount?: number, reverse: boolean = false, startLine?: number, endLine?: number) { - const [file, root]: [string, string] = Git.splitPath(fileName, repoPath); + const [file, root] = Git.splitPath(fileName, repoPath); const params = [...defaultLogParams, `--no-merges`, `--follow`]; if (maxCount && !reverse) { @@ -215,7 +215,7 @@ export class Git { } static status_file(repoPath: string, fileName: string): Promise { - const [file, root]: [string, string] = Git.splitPath(fileName, repoPath); + const [file, root] = Git.splitPath(fileName, repoPath); const params = ['status', '--porcelain=v2', file]; return gitCommand(root, ...params); diff --git a/src/git/gitUri.ts b/src/git/gitUri.ts index 325e9ed..886f2b6 100644 --- a/src/git/gitUri.ts +++ b/src/git/gitUri.ts @@ -64,6 +64,8 @@ export class GitUri extends Uri { if (this.repoPath) { directory = path.relative(this.repoPath, directory); } + directory = Git.normalizePath(directory); + return (!directory || directory === '.') ? path.basename(this.fsPath) : `${path.basename(this.fsPath)}${separator}${directory}`; diff --git a/src/git/models/commit.ts b/src/git/models/commit.ts index 0711abe..5adbd81 100644 --- a/src/git/models/commit.ts +++ b/src/git/models/commit.ts @@ -86,7 +86,7 @@ export class GitCommit implements IGitCommit { } getFormattedPath(separator: string = ' \u00a0\u2022\u00a0 '): string { - const directory = path.dirname(this.fileName); + const directory = Git.normalizePath(path.dirname(this.fileName)); return (!directory || directory === '.') ? path.basename(this.fileName) : `${path.basename(this.fileName)}${separator}${directory}`; diff --git a/src/git/parsers/blameParser.ts b/src/git/parsers/blameParser.ts index 6d83c8d..522d076 100644 --- a/src/git/parsers/blameParser.ts +++ b/src/git/parsers/blameParser.ts @@ -132,7 +132,7 @@ export class GitBlameParser { if (i === 0) { // Try to get the repoPath from the most recent commit - repoPath = fileName.replace(`/${entry.fileName}`, ''); + repoPath = Git.normalizePath(fileName.replace(`/${entry.fileName}`, '')); relativeFileName = path.relative(repoPath, fileName).replace(/\\/g, '/'); } diff --git a/src/git/parsers/logParser.ts b/src/git/parsers/logParser.ts index 563f50a..b994be9 100644 --- a/src/git/parsers/logParser.ts +++ b/src/git/parsers/logParser.ts @@ -188,7 +188,7 @@ export class GitLogParser { let recentCommit: GitLogCommit; if (isRepoPath) { - repoPath = fileNameOrRepoPath; + repoPath = Git.normalizePath(fileNameOrRepoPath); } for (let i = 0, len = entries.length; i < len; i++) { @@ -203,7 +203,7 @@ export class GitLogParser { } else { // Try to get the repoPath from the most recent commit - repoPath = fileNameOrRepoPath.replace(fileNameOrRepoPath.startsWith('/') ? `/${entry.fileName}` : entry.fileName, ''); + repoPath = Git.normalizePath(fileNameOrRepoPath.replace(fileNameOrRepoPath.startsWith('/') ? `/${entry.fileName}` : entry.fileName, '')); relativeFileName = path.relative(repoPath, fileNameOrRepoPath).replace(/\\/g, '/'); } } diff --git a/src/git/parsers/statusParser.ts b/src/git/parsers/statusParser.ts index d48715b..7b46bca 100644 --- a/src/git/parsers/statusParser.ts +++ b/src/git/parsers/statusParser.ts @@ -1,5 +1,5 @@ 'use strict'; -import { GitStatusFileStatus, GitStatusFile, IGitStatus } from './../git'; +import { Git, GitStatusFileStatus, GitStatusFile, IGitStatus } from './../git'; interface IFileStatusEntry { staged: boolean; @@ -17,7 +17,7 @@ export class GitStatusParser { if (!lines.length) return undefined; const status = { - repoPath: repoPath, + repoPath: Git.normalizePath(repoPath), state: { ahead: 0, behind: 0 diff --git a/src/gitService.ts b/src/gitService.ts index 4322799..7818d7c 100644 --- a/src/gitService.ts +++ b/src/gitService.ts @@ -203,9 +203,7 @@ export class GitService extends Disposable { if (!this.UseGitCaching) return; if (document.uri.scheme !== DocumentSchemes.File) return; - const fileName = Git.normalizePath(document.fileName); - - const cacheKey = this.getCacheEntryKey(fileName); + const cacheKey = this.getCacheEntryKey(document.fileName); if (reason === RemoveCacheReason.DocumentSaved) { // Don't remove broken blame on save (since otherwise we'll have to run the broken blame again) @@ -268,7 +266,7 @@ export class GitService extends Disposable { public getBlameability(fileName: string): boolean { if (!this.UseGitCaching) return true; - const cacheKey = this.getCacheEntryKey(Git.normalizePath(fileName)); + const cacheKey = this.getCacheEntryKey(fileName); const entry = this._gitCache.get(cacheKey); return !(entry && entry.hasErrors); } @@ -276,7 +274,7 @@ export class GitService extends Disposable { async getBlameForFile(uri: GitUri): Promise { Logger.log(`getBlameForFile('${uri.repoPath}', '${uri.fsPath}', ${uri.sha})`); - const fileName = Git.normalizePath(uri.fsPath); + const fileName = uri.fsPath; let entry: GitCacheEntry | undefined; if (this.UseGitCaching && !uri.sha) { @@ -306,8 +304,10 @@ export class GitService extends Disposable { } private async _getBlameForFile(uri: GitUri, fileName: string, entry: GitCacheEntry | undefined): Promise { + const [file, root] = Git.splitPath(fileName, uri.repoPath); + const ignore = await this._gitignore; - if (ignore && !ignore.filter([fileName]).length) { + if (ignore && !ignore.filter([file]).length) { Logger.log(`Skipping blame; '${fileName}' is gitignored`); if (entry && entry.key) { this._onDidBlameFailEmitter.fire(entry.key); @@ -316,8 +316,8 @@ export class GitService extends Disposable { } try { - const data = await Git.blame(uri.repoPath, fileName, uri.sha); - return GitBlameParser.parse(data, fileName); + const data = await Git.blame(root, file, uri.sha); + return GitBlameParser.parse(data, file); } catch (ex) { // Trap and cache expected blame errors @@ -356,7 +356,7 @@ export class GitService extends Disposable { } as IGitBlameLine; } - const fileName = Git.normalizePath(uri.fsPath); + const fileName = uri.fsPath; try { const data = await Git.blame(uri.repoPath, fileName, uri.sha, line + 1, line + 1); @@ -474,7 +474,7 @@ export class GitService extends Disposable { } getCacheEntryKey(fileName: string) { - return fileName.toLowerCase(); + return Git.normalizePath(fileName).toLowerCase(); } getGitUriForFile(fileName: string) { @@ -502,8 +502,6 @@ export class GitService extends Disposable { getLogForFile(repoPath: string, fileName: string, sha?: string, range?: Range, maxCount?: number, reverse: boolean = false): Promise { Logger.log(`getLogForFile('${repoPath}', '${fileName}', ${sha}, ${range && `[${range.start.line}, ${range.end.line}]`}, ${maxCount}, ${reverse})`); - fileName = Git.normalizePath(fileName); - let entry: GitCacheEntry | undefined; if (this.UseGitCaching && !sha && !range && !maxCount) { const cacheKey = this.getCacheEntryKey(fileName); @@ -532,15 +530,17 @@ export class GitService extends Disposable { } private async _getLogForFile(repoPath: string, fileName: string, sha: string, range: Range, maxCount: number, reverse: boolean, entry: GitCacheEntry | undefined): Promise { + const [file, root] = Git.splitPath(fileName, repoPath); + const ignore = await this._gitignore; - if (ignore && !ignore.filter([fileName]).length) { + if (ignore && !ignore.filter([file]).length) { Logger.log(`Skipping log; '${fileName}' is gitignored`); return await GitService.EmptyPromise as IGitLog; } try { - const data = await Git.log_file(repoPath, fileName, sha, maxCount, reverse, range && range.start.line + 1, range && range.end.line + 1); - return GitLogParser.parse(data, 'file', repoPath || fileName, maxCount, !!repoPath, reverse, range); + const data = await Git.log_file(root, file, sha, maxCount, reverse, range && range.start.line + 1, range && range.end.line + 1); + return GitLogParser.parse(data, 'file', root || file, maxCount, !!root, reverse, range); } catch (ex) { // Trap and cache expected log errors diff --git a/src/quickPicks/gitQuickPicks.ts b/src/quickPicks/gitQuickPicks.ts index 44dd597..55edf86 100644 --- a/src/quickPicks/gitQuickPicks.ts +++ b/src/quickPicks/gitQuickPicks.ts @@ -1,6 +1,6 @@ 'use strict'; import { QuickPickItem, Uri } from 'vscode'; -import { getGitStatusIcon, GitCommit, GitStatusFileStatus, GitService, GitUri } from '../gitService'; +import { getGitStatusIcon, Git, GitCommit, GitStatusFileStatus, GitService, GitUri } from '../gitService'; import { OpenFileCommandQuickPickItem } from './quickPicks'; import * as moment from 'moment'; import * as path from 'path'; @@ -29,7 +29,7 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI constructor(commit: GitCommit, fileName: string, status: GitStatusFileStatus) { const icon = getGitStatusIcon(status); - let directory = path.dirname(fileName); + let directory = Git.normalizePath(path.dirname(fileName)); if (!directory || directory === '.') { directory = undefined; } diff --git a/src/quickPicks/repoStatus.ts b/src/quickPicks/repoStatus.ts index 366b448..e7e6e75 100644 --- a/src/quickPicks/repoStatus.ts +++ b/src/quickPicks/repoStatus.ts @@ -2,7 +2,7 @@ import { Iterables } from '../system'; import { QuickPickItem, QuickPickOptions, Uri, window } from 'vscode'; import { Commands, Keyboard } from '../commands'; -import { GitStatusFile, GitUri, IGitStatus } from '../gitService'; +import { Git, GitStatusFile, GitUri, IGitStatus } from '../gitService'; import { CommandQuickPickItem, getQuickPickIgnoreFocusOut, OpenFileCommandQuickPickItem } from './quickPicks'; import * as path from 'path'; @@ -12,7 +12,7 @@ export class OpenStatusFileCommandQuickPickItem extends OpenFileCommandQuickPick const uri = Uri.file(path.resolve(status.repoPath, status.fileName)); const icon = status.getIcon(); - let directory = path.dirname(status.fileName); + let directory = Git.normalizePath(path.dirname(status.fileName)); if (!directory || directory === '.') { directory = undefined; }