mirror of
https://github.com/ckaczor/vscode-gitlens.git
synced 2026-02-17 02:51:47 -05:00
Fixes #1 Support blame on files outside repo
Replaces blame regex parsing with more robust parser (also use s--incremental instead of --porcelain) Stops throwing on git blame errors (too many are common) Fixes issues with Diff with Previous command Fixes issues with blame explorer code lens -- with previous commits Fixes issues with compact blame annotations -- skips blank lines
This commit is contained in:
@@ -62,9 +62,7 @@ export class DiffWithPreviousCommand extends EditorCommand {
|
|||||||
return window.showInformationMessage(`Commit ${sha} has no previous commit`);
|
return window.showInformationMessage(`Commit ${sha} has no previous commit`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Moving doesn't always seem to work -- or more accurately it seems like it moves down that number of lines from the current line
|
return Promise.all([this.git.getVersionedFile(shaUri.fsPath, sha), this.git.getVersionedFile(compareWithUri.fsPath, compareWithSha)])
|
||||||
// which for a diff could be the first difference
|
|
||||||
return Promise.all([this.git.getVersionedFile(uri.fsPath, sha), this.git.getVersionedFile(uri.fsPath, compareWithSha)])
|
|
||||||
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', 'getVersionedFile', ex))
|
.catch(ex => console.error('[GitLens.DiffWithPreviousCommand]', 'getVersionedFile', ex))
|
||||||
.then(values => commands.executeCommand(BuiltInCommands.Diff, Uri.file(values[1]), Uri.file(values[0]), `${path.basename(compareWithUri.fsPath)} (${compareWithSha}) ↔ ${path.basename(shaUri.fsPath)} (${sha})`)
|
.then(values => commands.executeCommand(BuiltInCommands.Diff, Uri.file(values[1]), Uri.file(values[0]), `${path.basename(compareWithUri.fsPath)} (${compareWithSha}) ↔ ${path.basename(shaUri.fsPath)} (${sha})`)
|
||||||
.then(() => commands.executeCommand(BuiltInCommands.RevealLine, {lineNumber: line, at: 'center'})));
|
.then(() => commands.executeCommand(BuiltInCommands.RevealLine, {lineNumber: line, at: 'center'})));
|
||||||
@@ -91,8 +89,6 @@ export class DiffWithWorkingCommand extends EditorCommand {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Moving doesn't always seem to work -- or more accurately it seems like it moves down that number of lines from the current line
|
|
||||||
// which for a diff could be the first difference
|
|
||||||
return this.git.getVersionedFile(shaUri.fsPath, sha)
|
return this.git.getVersionedFile(shaUri.fsPath, sha)
|
||||||
.catch(ex => console.error('[GitLens.DiffWithWorkingCommand]', 'getVersionedFile', ex))
|
.catch(ex => console.error('[GitLens.DiffWithWorkingCommand]', 'getVersionedFile', ex))
|
||||||
.then(compare => commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), uri, `${path.basename(shaUri.fsPath)} (${sha}) ↔ ${path.basename(uri.fsPath)} (index)`)
|
.then(compare => commands.executeCommand(BuiltInCommands.Diff, Uri.file(compare), uri, `${path.basename(shaUri.fsPath)} (${sha}) ↔ ${path.basename(uri.fsPath)} (index)`)
|
||||||
|
|||||||
202
src/git/enrichers/blameParserEnricher.ts
Normal file
202
src/git/enrichers/blameParserEnricher.ts
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
'use strict'
|
||||||
|
import {GitBlameFormat, GitCommit, IGitAuthor, IGitBlame, IGitCommit, IGitCommitLine, IGitEnricher} from './../git';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
interface IBlameEntry {
|
||||||
|
sha: string;
|
||||||
|
line: number;
|
||||||
|
originalLine: number;
|
||||||
|
lineCount: number;
|
||||||
|
|
||||||
|
author?: string;
|
||||||
|
authorEmail?: string;
|
||||||
|
authorDate?: string;
|
||||||
|
authorTimeZone?: string;
|
||||||
|
|
||||||
|
committer?: string;
|
||||||
|
committerEmail?: string;
|
||||||
|
committerDate?: string;
|
||||||
|
committerTimeZone?: string;
|
||||||
|
|
||||||
|
previousSha?: string;
|
||||||
|
previousFileName?: string;
|
||||||
|
|
||||||
|
fileName?: string;
|
||||||
|
|
||||||
|
summary?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GitBlameParserEnricher implements IGitEnricher<IGitBlame> {
|
||||||
|
constructor(public format: GitBlameFormat) {
|
||||||
|
if (format !== GitBlameFormat.incremental) {
|
||||||
|
throw new Error(`Invalid blame format=${format}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _parseEntries(data: string): IBlameEntry[] {
|
||||||
|
if (!data) return null;
|
||||||
|
|
||||||
|
const lines = data.split('\n');
|
||||||
|
if (!lines.length) return null;
|
||||||
|
|
||||||
|
const entries: IBlameEntry[] = [];
|
||||||
|
|
||||||
|
let entry: IBlameEntry;
|
||||||
|
let position = -1;
|
||||||
|
while (++position < lines.length) {
|
||||||
|
let lineParts = lines[position].split(" ");
|
||||||
|
if (lineParts.length < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
entry = {
|
||||||
|
sha: lineParts[0].substring(0, 8),
|
||||||
|
originalLine: parseInt(lineParts[1], 10) - 1,
|
||||||
|
line: parseInt(lineParts[2], 10) - 1,
|
||||||
|
lineCount: parseInt(lineParts[3], 10)
|
||||||
|
};
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (lineParts[0]) {
|
||||||
|
case "author":
|
||||||
|
entry.author = lineParts.slice(1).join(" ").trim();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case "author-mail":
|
||||||
|
// entry.authorEmail = lineParts[1].trim();
|
||||||
|
// break;
|
||||||
|
|
||||||
|
case "author-time":
|
||||||
|
entry.authorDate = lineParts[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "author-tz":
|
||||||
|
entry.authorTimeZone = lineParts[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case "committer":
|
||||||
|
// entry.committer = lineParts.slice(1).join(" ").trim();
|
||||||
|
// break;
|
||||||
|
|
||||||
|
// case "committer-mail":
|
||||||
|
// entry.committerEmail = lineParts[1].trim();
|
||||||
|
// break;
|
||||||
|
|
||||||
|
// case "committer-time":
|
||||||
|
// entry.committerDate = lineParts[1];
|
||||||
|
// break;
|
||||||
|
|
||||||
|
// case "committer-tz":
|
||||||
|
// entry.committerTimeZone = lineParts[1];
|
||||||
|
// break;
|
||||||
|
|
||||||
|
case "summary":
|
||||||
|
entry.summary = lineParts.slice(1).join(" ").trim();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "previous":
|
||||||
|
entry.previousSha = lineParts[1].substring(0, 8);
|
||||||
|
entry.previousFileName = lineParts.slice(2).join(" ");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "filename":
|
||||||
|
entry.fileName = lineParts.slice(1).join(" ");
|
||||||
|
|
||||||
|
entries.push(entry);
|
||||||
|
entry = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
enrich(data: string, fileName: string): IGitBlame {
|
||||||
|
const entries = this._parseEntries(data);
|
||||||
|
if (!entries) return null;
|
||||||
|
|
||||||
|
const authors: Map<string, IGitAuthor> = new Map();
|
||||||
|
const commits: Map<string, IGitCommit> = new Map();
|
||||||
|
const lines: Array<IGitCommitLine> = [];
|
||||||
|
|
||||||
|
let repoPath: string;
|
||||||
|
let relativeFileName: string;
|
||||||
|
|
||||||
|
for (let i = 0, len = entries.length; i < len; i++) {
|
||||||
|
const entry = entries[i];
|
||||||
|
|
||||||
|
if (i === 0) {
|
||||||
|
// Try to get the repoPath from the most recent commit
|
||||||
|
repoPath = fileName.replace(`/${entry.fileName}`, '');
|
||||||
|
relativeFileName = path.relative(repoPath, fileName).replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
let commit = commits.get(entry.sha);
|
||||||
|
if (!commit) {
|
||||||
|
let author = authors.get(entry.author);
|
||||||
|
if (!author) {
|
||||||
|
author = {
|
||||||
|
name: entry.author,
|
||||||
|
lineCount: 0
|
||||||
|
};
|
||||||
|
authors.set(entry.author, author);
|
||||||
|
}
|
||||||
|
|
||||||
|
commit = new GitCommit(repoPath, entry.sha, relativeFileName, entry.author, moment(`${entry.authorDate} ${entry.authorTimeZone}`, 'X Z').toDate(), entry.summary);
|
||||||
|
|
||||||
|
if (relativeFileName !== entry.fileName) {
|
||||||
|
commit.originalFileName = entry.fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.previousSha) {
|
||||||
|
commit.previousSha = entry.previousSha;
|
||||||
|
commit.previousFileName = entry.previousFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
commits.set(entry.sha, commit);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0, len = entry.lineCount; j < len; j++) {
|
||||||
|
const line: IGitCommitLine = {
|
||||||
|
sha: entry.sha,
|
||||||
|
line: entry.line + j,
|
||||||
|
originalLine: entry.originalLine + j
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commit.previousSha) {
|
||||||
|
line.previousSha = commit.previousSha;
|
||||||
|
}
|
||||||
|
|
||||||
|
commit.lines.push(line);
|
||||||
|
lines[line.line] = line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commits.forEach(c => authors.get(c.author).lineCount += c.lines.length);
|
||||||
|
|
||||||
|
const sortedAuthors: Map<string, IGitAuthor> = new Map();
|
||||||
|
const values = Array.from(authors.values())
|
||||||
|
.sort((a, b) => b.lineCount - a.lineCount)
|
||||||
|
.forEach(a => sortedAuthors.set(a.name, a));
|
||||||
|
|
||||||
|
// const sortedCommits: Map<string, IGitCommit> = new Map();
|
||||||
|
// Array.from(commits.values())
|
||||||
|
// .sort((a, b) => b.date.getTime() - a.date.getTime())
|
||||||
|
// .forEach(c => sortedCommits.set(c.sha, c));
|
||||||
|
|
||||||
|
return <IGitBlame>{
|
||||||
|
repoPath: repoPath,
|
||||||
|
authors: sortedAuthors,
|
||||||
|
// commits: sortedCommits,
|
||||||
|
commits: commits,
|
||||||
|
lines: lines
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
import {Uri} from 'vscode';
|
import {GitBlameFormat, GitCommit, IGitAuthor, IGitBlame, IGitCommit, IGitCommitLine, IGitEnricher} from './../git';
|
||||||
import {GitBlameFormat} from './git'
|
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
const blamePorcelainMatcher = /^([\^0-9a-fA-F]{40})\s([0-9]+)\s([0-9]+)(?:\s([0-9]+))?$\n(?:^author\s(.*)$\n^author-mail\s(.*)$\n^author-time\s(.*)$\n^author-tz\s(.*)$\n^committer\s(.*)$\n^committer-mail\s(.*)$\n^committer-time\s(.*)$\n^committer-tz\s(.*)$\n^summary\s(.*)$\n(?:^previous\s(.*)?\s(.*)$\n)?^filename\s(.*)$\n)?^(.*)$/gm;
|
const blamePorcelainMatcher = /^([\^0-9a-fA-F]{40})\s([0-9]+)\s([0-9]+)(?:\s([0-9]+))?$\n(?:^author\s(.*)$\n^author-mail\s(.*)$\n^author-time\s(.*)$\n^author-tz\s(.*)$\n^committer\s(.*)$\n^committer-mail\s(.*)$\n^committer-time\s(.*)$\n^committer-tz\s(.*)$\n^summary\s(.*)$\n(?:^previous\s(.*)?\s(.*)$\n)?^filename\s(.*)$\n)?^(.*)$/gm;
|
||||||
const blameLinePorcelainMatcher = /^([\^0-9a-fA-F]{40})\s([0-9]+)\s([0-9]+)(?:\s([0-9]+))?$\n^author\s(.*)$\n^author-mail\s(.*)$\n^author-time\s(.*)$\n^author-tz\s(.*)$\n^committer\s(.*)$\n^committer-mail\s(.*)$\n^committer-time\s(.*)$\n^committer-tz\s(.*)$\n^summary\s(.*)$\n(?:^previous\s(.*)?\s(.*)$\n)?^filename\s(.*)$\n^(.*)$/gm;
|
const blameLinePorcelainMatcher = /^([\^0-9a-fA-F]{40})\s([0-9]+)\s([0-9]+)(?:\s([0-9]+))?$\n^author\s(.*)$\n^author-mail\s(.*)$\n^author-time\s(.*)$\n^author-tz\s(.*)$\n^committer\s(.*)$\n^committer-mail\s(.*)$\n^committer-time\s(.*)$\n^committer-tz\s(.*)$\n^summary\s(.*)$\n(?:^previous\s(.*)?\s(.*)$\n)?^filename\s(.*)$\n^(.*)$/gm;
|
||||||
|
|
||||||
interface IGitEnricher<T> {
|
export class GitBlameRegExpEnricher implements IGitEnricher<IGitBlame> {
|
||||||
enrich(data: string, ...args): T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GitBlameEnricher implements IGitEnricher<IGitBlame> {
|
|
||||||
private _matcher: RegExp;
|
private _matcher: RegExp;
|
||||||
|
|
||||||
constructor(public format: GitBlameFormat, private repoPath: string) {
|
constructor(public format: GitBlameFormat, private repoPath: string) {
|
||||||
@@ -96,76 +90,3 @@ export class GitBlameEnricher implements IGitEnricher<IGitBlame> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IGitBlame {
|
|
||||||
authors: Map<string, IGitAuthor>;
|
|
||||||
commits: Map<string, IGitCommit>;
|
|
||||||
lines: IGitCommitLine[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGitBlameLine {
|
|
||||||
author: IGitAuthor;
|
|
||||||
commit: IGitCommit;
|
|
||||||
line: IGitCommitLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGitBlameLines extends IGitBlame {
|
|
||||||
allLines: IGitCommitLine[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGitBlameCommitLines {
|
|
||||||
author: IGitAuthor;
|
|
||||||
commit: IGitCommit;
|
|
||||||
lines: IGitCommitLine[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGitAuthor {
|
|
||||||
name: string;
|
|
||||||
lineCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGitCommit {
|
|
||||||
sha: string;
|
|
||||||
fileName: string;
|
|
||||||
author: string;
|
|
||||||
date: Date;
|
|
||||||
message: string;
|
|
||||||
lines: IGitCommitLine[];
|
|
||||||
originalFileName?: string;
|
|
||||||
previousSha?: string;
|
|
||||||
previousFileName?: string;
|
|
||||||
|
|
||||||
previousUri: Uri;
|
|
||||||
uri: Uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class GitCommit implements IGitCommit {
|
|
||||||
lines: IGitCommitLine[];
|
|
||||||
originalFileName?: string;
|
|
||||||
previousSha?: string;
|
|
||||||
previousFileName?: string;
|
|
||||||
|
|
||||||
constructor(private repoPath: string, public sha: string, public fileName: string, public author: string, public date: Date, public message: string,
|
|
||||||
lines?: IGitCommitLine[], originalFileName?: string, previousSha?: string, previousFileName?: string) {
|
|
||||||
this.lines = lines || [];
|
|
||||||
this.originalFileName = originalFileName;
|
|
||||||
this.previousSha = previousSha;
|
|
||||||
this.previousFileName = previousFileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get previousUri(): Uri {
|
|
||||||
return this.previousFileName ? Uri.file(path.join(this.repoPath, this.previousFileName)) : this.uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
get uri(): Uri {
|
|
||||||
return Uri.file(path.join(this.repoPath, this.originalFileName || this.fileName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGitCommitLine {
|
|
||||||
sha: string;
|
|
||||||
previousSha?: string;
|
|
||||||
line: number;
|
|
||||||
originalLine: number;
|
|
||||||
code?: string;
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,8 @@ import * as tmp from 'tmp';
|
|||||||
import {spawnPromise} from 'spawn-rx';
|
import {spawnPromise} from 'spawn-rx';
|
||||||
|
|
||||||
export * from './gitEnrichment';
|
export * from './gitEnrichment';
|
||||||
|
//export * from './enrichers/blameRegExpEnricher';
|
||||||
|
export * from './enrichers/blameParserEnricher';
|
||||||
|
|
||||||
function gitCommand(cwd: string, ...args) {
|
function gitCommand(cwd: string, ...args) {
|
||||||
return spawnPromise('git', args, { cwd: cwd })
|
return spawnPromise('git', args, { cwd: cwd })
|
||||||
@@ -32,30 +34,33 @@ export const GitBlameFormat = {
|
|||||||
|
|
||||||
export default class Git {
|
export default class Git {
|
||||||
static normalizePath(fileName: string, repoPath?: string) {
|
static normalizePath(fileName: string, repoPath?: string) {
|
||||||
fileName = fileName.replace(/\\/g, '/');
|
return fileName.replace(/\\/g, '/');
|
||||||
repoPath = repoPath.replace(/\\/g, '/');
|
}
|
||||||
if (path.isAbsolute(fileName) && fileName.startsWith(repoPath)) {
|
|
||||||
fileName = path.relative(repoPath, fileName).replace(/\\/g, '/');
|
static splitPath(fileName: string) {
|
||||||
}
|
// if (!path.isAbsolute(fileName)) {
|
||||||
return fileName;
|
// console.error('[GitLens]', `Git.splitPath(${fileName}) is not an absolute path!`);
|
||||||
|
// debugger;
|
||||||
|
// }
|
||||||
|
return [path.basename(fileName).replace(/\\/g, '/'), path.dirname(fileName).replace(/\\/g, '/')];
|
||||||
}
|
}
|
||||||
|
|
||||||
static repoPath(cwd: string) {
|
static repoPath(cwd: string) {
|
||||||
return gitCommand(cwd, 'rev-parse', '--show-toplevel').then(data => data.replace(/\r?\n|\r/g, '').replace(/\\/g, '/'));
|
return gitCommand(cwd, 'rev-parse', '--show-toplevel').then(data => data.replace(/\r?\n|\r/g, '').replace(/\\/g, '/'));
|
||||||
}
|
}
|
||||||
|
|
||||||
static blame(format: GitBlameFormat, fileName: string, repoPath: string, sha?: string) {
|
static blame(format: GitBlameFormat, fileName: string, sha?: string) {
|
||||||
fileName = Git.normalizePath(fileName, repoPath);
|
const [file, root] = Git.splitPath(Git.normalizePath(fileName));
|
||||||
|
|
||||||
if (sha) {
|
if (sha) {
|
||||||
return gitCommand(repoPath, 'blame', format, '--root', `${sha}^`, '--', fileName);
|
return gitCommand(root, 'blame', format, '--root', `${sha}^`, '--', file);
|
||||||
}
|
}
|
||||||
return gitCommand(repoPath, 'blame', format, '--root', '--', fileName);
|
return gitCommand(root, 'blame', format, '--root', '--', file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getVersionedFile(fileName: string, repoPath: string, sha: string) {
|
static getVersionedFile(fileName: string, sha: string) {
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
Git.getVersionedFileText(fileName, repoPath, sha).then(data => {
|
Git.getVersionedFileText(fileName, sha).then(data => {
|
||||||
const ext = path.extname(fileName);
|
const ext = path.extname(fileName);
|
||||||
tmp.file({ prefix: `${path.basename(fileName, ext)}-${sha}_`, postfix: ext }, (err, destination, fd, cleanupCallback) => {
|
tmp.file({ prefix: `${path.basename(fileName, ext)}-${sha}_`, postfix: ext }, (err, destination, fd, cleanupCallback) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -76,27 +81,10 @@ export default class Git {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static getVersionedFileText(fileName: string, repoPath: string, sha: string) {
|
static getVersionedFileText(fileName: string, sha: string) {
|
||||||
fileName = Git.normalizePath(fileName, repoPath);
|
const [file, root] = Git.splitPath(Git.normalizePath(fileName));
|
||||||
sha = sha.replace('^', '');
|
sha = sha.replace('^', '');
|
||||||
|
|
||||||
return gitCommand(repoPath, 'show', `${sha}:./${fileName}`);
|
return gitCommand(root, 'show', `${sha}:./${file}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static getCommitMessage(sha: string, repoPath: string) {
|
|
||||||
// sha = sha.replace('^', '');
|
|
||||||
|
|
||||||
// return gitCommand(repoPath, 'show', '-s', '--format=%B', sha);
|
|
||||||
// // .then(s => { console.log(s); return s; })
|
|
||||||
// // .catch(ex => console.error(ex));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static getCommitMessages(fileName: string, repoPath: string) {
|
|
||||||
// fileName = Git.normalizePath(fileName, repoPath);
|
|
||||||
|
|
||||||
// // git log --format="%h (%aN %x09 %ai) %s" --
|
|
||||||
// return gitCommand(repoPath, 'log', '--oneline', '--', fileName);
|
|
||||||
// // .then(s => { console.log(s); return s; })
|
|
||||||
// // .catch(ex => console.error(ex));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
82
src/git/gitEnrichment.ts
Normal file
82
src/git/gitEnrichment.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
'use strict'
|
||||||
|
import {Uri} from 'vscode';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export interface IGitEnricher<T> {
|
||||||
|
enrich(data: string, ...args): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitBlame {
|
||||||
|
repoPath: string;
|
||||||
|
authors: Map<string, IGitAuthor>;
|
||||||
|
commits: Map<string, IGitCommit>;
|
||||||
|
lines: IGitCommitLine[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitBlameLine {
|
||||||
|
author: IGitAuthor;
|
||||||
|
commit: IGitCommit;
|
||||||
|
line: IGitCommitLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitBlameLines extends IGitBlame {
|
||||||
|
allLines: IGitCommitLine[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitBlameCommitLines {
|
||||||
|
author: IGitAuthor;
|
||||||
|
commit: IGitCommit;
|
||||||
|
lines: IGitCommitLine[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitAuthor {
|
||||||
|
name: string;
|
||||||
|
lineCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitCommit {
|
||||||
|
repoPath: string;
|
||||||
|
sha: string;
|
||||||
|
fileName: string;
|
||||||
|
author: string;
|
||||||
|
date: Date;
|
||||||
|
message: string;
|
||||||
|
lines: IGitCommitLine[];
|
||||||
|
originalFileName?: string;
|
||||||
|
previousSha?: string;
|
||||||
|
previousFileName?: string;
|
||||||
|
|
||||||
|
previousUri: Uri;
|
||||||
|
uri: Uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GitCommit implements IGitCommit {
|
||||||
|
lines: IGitCommitLine[];
|
||||||
|
originalFileName?: string;
|
||||||
|
previousSha?: string;
|
||||||
|
previousFileName?: string;
|
||||||
|
|
||||||
|
constructor(public repoPath: string, public sha: string, public fileName: string, public author: string, public date: Date, public message: string,
|
||||||
|
lines?: IGitCommitLine[], originalFileName?: string, previousSha?: string, previousFileName?: string) {
|
||||||
|
this.lines = lines || [];
|
||||||
|
this.originalFileName = originalFileName;
|
||||||
|
this.previousSha = previousSha;
|
||||||
|
this.previousFileName = previousFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get previousUri(): Uri {
|
||||||
|
return this.previousFileName ? Uri.file(path.join(this.repoPath, this.previousFileName)) : this.uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
get uri(): Uri {
|
||||||
|
return Uri.file(path.join(this.repoPath, this.originalFileName || this.fileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGitCommitLine {
|
||||||
|
sha: string;
|
||||||
|
previousSha?: string;
|
||||||
|
line: number;
|
||||||
|
originalLine: number;
|
||||||
|
code?: string;
|
||||||
|
}
|
||||||
@@ -6,13 +6,13 @@ import * as moment from 'moment';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
export class GitDiffWithWorkingTreeCodeLens extends CodeLens {
|
export class GitDiffWithWorkingTreeCodeLens extends CodeLens {
|
||||||
constructor(private git: GitProvider, public fileName: string, public sha: string, range: Range) {
|
constructor(private git: GitProvider, public fileName: string, public commit: IGitCommit, range: Range) {
|
||||||
super(range);
|
super(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GitDiffWithPreviousCodeLens extends CodeLens {
|
export class GitDiffWithPreviousCodeLens extends CodeLens {
|
||||||
constructor(private git: GitProvider, public fileName: string, public sha: string, public compareWithSha: string, range: Range) {
|
constructor(private git: GitProvider, public fileName: string, public commit: IGitCommit, range: Range) {
|
||||||
super(range);
|
super(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,22 +31,17 @@ export default class GitBlameCodeLensProvider implements CodeLensProvider {
|
|||||||
const lenses: CodeLens[] = [];
|
const lenses: CodeLens[] = [];
|
||||||
if (!blame) return lenses;
|
if (!blame) return lenses;
|
||||||
|
|
||||||
const commits = Array.from(blame.commits.values());
|
const commit = blame.commits.get(sha);
|
||||||
let index = commits.findIndex(c => c.sha === sha) + 1;
|
const absoluteFileName = path.join(commit.repoPath, fileName);
|
||||||
|
|
||||||
let previousCommit: IGitCommit;
|
|
||||||
if (index < commits.length) {
|
|
||||||
previousCommit = commits[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add codelens to each "group" of blame lines
|
// Add codelens to each "group" of blame lines
|
||||||
const lines = blame.lines.filter(l => l.sha === sha && l.originalLine >= data.range.start.line && l.originalLine <= data.range.end.line);
|
const lines = blame.lines.filter(l => l.sha === sha && l.originalLine >= data.range.start.line && l.originalLine <= data.range.end.line);
|
||||||
let lastLine = lines[0].originalLine;
|
let lastLine = lines[0].originalLine;
|
||||||
lines.forEach(l => {
|
lines.forEach(l => {
|
||||||
if (l.originalLine !== lastLine + 1) {
|
if (l.originalLine !== lastLine + 1) {
|
||||||
lenses.push(new GitDiffWithWorkingTreeCodeLens(this.git, fileName, sha, new Range(l.originalLine, 0, l.originalLine, 1)));
|
lenses.push(new GitDiffWithWorkingTreeCodeLens(this.git, absoluteFileName, commit, new Range(l.originalLine, 0, l.originalLine, 1)));
|
||||||
if (previousCommit) {
|
if (commit.previousSha) {
|
||||||
lenses.push(new GitDiffWithPreviousCodeLens(this.git, fileName, sha, previousCommit.sha, new Range(l.originalLine, 1, l.originalLine, 2)));
|
lenses.push(new GitDiffWithPreviousCodeLens(this.git, absoluteFileName, commit, new Range(l.originalLine, 1, l.originalLine, 2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastLine = l.originalLine;
|
lastLine = l.originalLine;
|
||||||
@@ -54,9 +49,9 @@ export default class GitBlameCodeLensProvider implements CodeLensProvider {
|
|||||||
|
|
||||||
// Check if we have a lens for the whole document -- if not add one
|
// Check if we have a lens for the whole document -- if not add one
|
||||||
if (!lenses.find(l => l.range.start.line === 0 && l.range.end.line === 0)) {
|
if (!lenses.find(l => l.range.start.line === 0 && l.range.end.line === 0)) {
|
||||||
lenses.push(new GitDiffWithWorkingTreeCodeLens(this.git, fileName, sha, new Range(0, 0, 0, 1)));
|
lenses.push(new GitDiffWithWorkingTreeCodeLens(this.git, absoluteFileName, commit, new Range(0, 0, 0, 1)));
|
||||||
if (previousCommit) {
|
if (commit.previousSha) {
|
||||||
lenses.push(new GitDiffWithPreviousCodeLens(this.git, fileName, sha, previousCommit.sha, new Range(0, 1, 0, 2)));
|
lenses.push(new GitDiffWithPreviousCodeLens(this.git, absoluteFileName, commit, new Range(0, 1, 0, 2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,16 +68,16 @@ export default class GitBlameCodeLensProvider implements CodeLensProvider {
|
|||||||
lens.command = {
|
lens.command = {
|
||||||
title: `Compare with Working Tree`,
|
title: `Compare with Working Tree`,
|
||||||
command: Commands.DiffWithWorking,
|
command: Commands.DiffWithWorking,
|
||||||
arguments: [Uri.file(path.join(this.git.repoPath, lens.fileName)), lens.sha]
|
arguments: [Uri.file(lens.fileName), lens.commit.sha, lens.commit.uri, lens.range.start.line]
|
||||||
};
|
};
|
||||||
return Promise.resolve(lens);
|
return Promise.resolve(lens);
|
||||||
}
|
}
|
||||||
|
|
||||||
_resolveGitDiffWithPreviousCodeLens(lens: GitDiffWithPreviousCodeLens, token: CancellationToken): Thenable<CodeLens> {
|
_resolveGitDiffWithPreviousCodeLens(lens: GitDiffWithPreviousCodeLens, token: CancellationToken): Thenable<CodeLens> {
|
||||||
lens.command = {
|
lens.command = {
|
||||||
title: `Compare with Previous (${lens.compareWithSha})`,
|
title: `Compare with Previous (${lens.commit.previousSha})`,
|
||||||
command: Commands.DiffWithPrevious,
|
command: Commands.DiffWithPrevious,
|
||||||
arguments: [Uri.file(path.join(this.git.repoPath, lens.fileName)), lens.sha, lens.compareWithSha]
|
arguments: [Uri.file(lens.fileName), lens.commit.sha, lens.commit.uri, lens.commit.previousSha, lens.commit.previousUri, lens.range.start.line]
|
||||||
};
|
};
|
||||||
return Promise.resolve(lens);
|
return Promise.resolve(lens);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
import {commands, DecorationInstanceRenderOptions, DecorationOptions, Diagnostic, DiagnosticCollection, DiagnosticSeverity, Disposable, ExtensionContext, languages, OverviewRulerLane, Position, Range, TextEditor, TextEditorDecorationType, Uri, window, workspace} from 'vscode';
|
import {commands, DecorationInstanceRenderOptions, DecorationOptions, Diagnostic, DiagnosticCollection, DiagnosticSeverity, Disposable, ExtensionContext, languages, OverviewRulerLane, Position, Range, TextDocument, TextEditor, TextEditorDecorationType, Uri, window, workspace} from 'vscode';
|
||||||
import {BuiltInCommands, Commands, DocumentSchemes} from './constants';
|
import {BuiltInCommands, Commands, DocumentSchemes} from './constants';
|
||||||
import {BlameAnnotationStyle, IBlameConfig} from './configuration';
|
import {BlameAnnotationStyle, IBlameConfig} from './configuration';
|
||||||
import GitProvider, {IGitBlame, IGitCommit} from './gitProvider';
|
import GitProvider, {IGitBlame, IGitCommit} from './gitProvider';
|
||||||
@@ -96,13 +96,16 @@ class GitBlameEditorController extends Disposable {
|
|||||||
private _config: IBlameConfig;
|
private _config: IBlameConfig;
|
||||||
private _diagnostics: DiagnosticCollection;
|
private _diagnostics: DiagnosticCollection;
|
||||||
private _disposable: Disposable;
|
private _disposable: Disposable;
|
||||||
|
private _document: TextDocument;
|
||||||
private _toggleWhitespace: boolean;
|
private _toggleWhitespace: boolean;
|
||||||
|
|
||||||
constructor(private context: ExtensionContext, private git: GitProvider, public editor: TextEditor) {
|
constructor(private context: ExtensionContext, private git: GitProvider, public editor: TextEditor) {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
|
|
||||||
this.uri = this.editor.document.uri;
|
this._document = this.editor.document;
|
||||||
|
this.uri = this._document.uri;
|
||||||
const fileName = this.uri.fsPath;
|
const fileName = this.uri.fsPath;
|
||||||
|
|
||||||
this._blame = this.git.getBlameForFile(fileName);
|
this._blame = this.git.getBlameForFile(fileName);
|
||||||
|
|
||||||
this._config = workspace.getConfiguration('gitlens').get<IBlameConfig>('blame');
|
this._config = workspace.getConfiguration('gitlens').get<IBlameConfig>('blame');
|
||||||
@@ -205,24 +208,33 @@ class GitBlameEditorController extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let gutter = '';
|
let gutter = '';
|
||||||
if (lastSha === l.sha) {
|
if (lastSha !== l.sha) {
|
||||||
count++;
|
count = -1;
|
||||||
if (count === 1) {
|
|
||||||
gutter = `\\00a6\\00a0 ${this._getAuthor(commit, 17, true)}`;
|
|
||||||
} else if (count === 2) {
|
|
||||||
gutter = `\\00a6\\00a0 ${this._getDate(commit, true)}`;
|
|
||||||
} else {
|
|
||||||
gutter = '\\00a6\\00a0';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
count = 0;
|
|
||||||
gutter = commit.sha.substring(0, 8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isEmptyOrWhitespace = this._document.lineAt(l.line).isEmptyOrWhitespace;
|
||||||
|
if (!isEmptyOrWhitespace) {
|
||||||
|
switch (++count) {
|
||||||
|
case 0:
|
||||||
|
gutter = commit.sha.substring(0, 8);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
gutter = `\\00a6\\00a0 ${this._getAuthor(commit, 17, true)}`;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
gutter = `\\00a6\\00a0 ${this._getDate(commit, true)}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
gutter = '\\00a6\\00a0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lastSha = l.sha;
|
lastSha = l.sha;
|
||||||
|
|
||||||
return <DecorationOptions>{
|
return <DecorationOptions>{
|
||||||
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)),
|
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)),
|
||||||
hoverMessage: [commit.message, `${commit.author}, ${moment(commit.date).format('MMMM Do, YYYY hh:MM a')}`],
|
hoverMessage: [`_${l.sha}_: ${commit.message}`, `${commit.author}, ${moment(commit.date).format('MMMM Do, YYYY hh:MM a')}`],
|
||||||
renderOptions: { before: { color: color, contentText: gutter, width: '11em' } }
|
renderOptions: { before: { color: color, contentText: gutter, width: '11em' } }
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -258,18 +270,6 @@ class GitBlameEditorController extends Disposable {
|
|||||||
if (l.sha.startsWith('00000000')) {
|
if (l.sha.startsWith('00000000')) {
|
||||||
color = 'rgba(0, 188, 242, 0.6)';
|
color = 'rgba(0, 188, 242, 0.6)';
|
||||||
hoverMessage = '';
|
hoverMessage = '';
|
||||||
// if (l.previousSha) {
|
|
||||||
// let previousCommit = blame.commits.get(l.previousSha);
|
|
||||||
// if (previousCommit) {//} && previousCommit.lines.find(_ => _.line === l.originalLine)) {
|
|
||||||
// commit = previousCommit;
|
|
||||||
// color = 'rgba(0, 188, 242, 0.6)';
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// color = 'rgba(127, 186, 0, 0.6)';
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// color = 'rgba(127, 186, 0, 0.6)';
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const gutter = this._getGutter(commit);
|
const gutter = this._getGutter(commit);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {Disposable, ExtensionContext, languages, Location, Position, Range, Uri,
|
|||||||
import {DocumentSchemes, WorkspaceState} from './constants';
|
import {DocumentSchemes, WorkspaceState} from './constants';
|
||||||
import {IConfig} from './configuration';
|
import {IConfig} from './configuration';
|
||||||
import GitCodeLensProvider from './gitCodeLensProvider';
|
import GitCodeLensProvider from './gitCodeLensProvider';
|
||||||
import Git, {GitBlameEnricher, GitBlameFormat, GitCommit, IGitAuthor, IGitBlame, IGitBlameCommitLines, IGitBlameLine, IGitBlameLines, IGitCommit} from './git';
|
import Git, {GitBlameParserEnricher, GitBlameFormat, GitCommit, IGitAuthor, IGitBlame, IGitBlameCommitLines, IGitBlameLine, IGitBlameLines, IGitCommit} from './git/git';
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as ignore from 'ignore';
|
import * as ignore from 'ignore';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@@ -11,7 +11,7 @@ import * as moment from 'moment';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
export { Git };
|
export { Git };
|
||||||
export * from './git';
|
export * from './git/git';
|
||||||
|
|
||||||
interface IBlameCacheEntry {
|
interface IBlameCacheEntry {
|
||||||
//date: Date;
|
//date: Date;
|
||||||
@@ -26,8 +26,6 @@ enum RemoveCacheReason {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class GitProvider extends Disposable {
|
export default class GitProvider extends Disposable {
|
||||||
public repoPath: string;
|
|
||||||
|
|
||||||
private _blameCache: Map<string, IBlameCacheEntry>;
|
private _blameCache: Map<string, IBlameCacheEntry>;
|
||||||
private _blameCacheDisposable: Disposable;
|
private _blameCacheDisposable: Disposable;
|
||||||
|
|
||||||
@@ -37,17 +35,17 @@ export default class GitProvider extends Disposable {
|
|||||||
private _gitignore: Promise<ignore.Ignore>;
|
private _gitignore: Promise<ignore.Ignore>;
|
||||||
|
|
||||||
static BlameEmptyPromise = Promise.resolve(<IGitBlame>null);
|
static BlameEmptyPromise = Promise.resolve(<IGitBlame>null);
|
||||||
static BlameFormat = GitBlameFormat.porcelain;
|
static BlameFormat = GitBlameFormat.incremental;
|
||||||
|
|
||||||
constructor(private context: ExtensionContext) {
|
constructor(private context: ExtensionContext) {
|
||||||
super(() => this.dispose());
|
super(() => this.dispose());
|
||||||
|
|
||||||
this.repoPath = context.workspaceState.get(WorkspaceState.RepoPath) as string;
|
const repoPath = context.workspaceState.get(WorkspaceState.RepoPath) as string;
|
||||||
|
|
||||||
this._onConfigure();
|
this._onConfigure();
|
||||||
|
|
||||||
this._gitignore = new Promise<ignore.Ignore>((resolve, reject) => {
|
this._gitignore = new Promise<ignore.Ignore>((resolve, reject) => {
|
||||||
const gitignorePath = path.join(this.repoPath, '.gitignore');
|
const gitignorePath = path.join(repoPath, '.gitignore');
|
||||||
fs.exists(gitignorePath, e => {
|
fs.exists(gitignorePath, e => {
|
||||||
if (e) {
|
if (e) {
|
||||||
fs.readFile(gitignorePath, 'utf8', (err, data) => {
|
fs.readFile(gitignorePath, 'utf8', (err, data) => {
|
||||||
@@ -126,7 +124,7 @@ export default class GitProvider extends Disposable {
|
|||||||
private _removeCachedBlame(fileName: string, reason: RemoveCacheReason) {
|
private _removeCachedBlame(fileName: string, reason: RemoveCacheReason) {
|
||||||
if (!this.UseCaching) return;
|
if (!this.UseCaching) return;
|
||||||
|
|
||||||
fileName = Git.normalizePath(fileName, this.repoPath);
|
fileName = Git.normalizePath(fileName);
|
||||||
|
|
||||||
const cacheKey = this._getBlameCacheKey(fileName);
|
const cacheKey = this._getBlameCacheKey(fileName);
|
||||||
if (reason === RemoveCacheReason.DocumentClosed) {
|
if (reason === RemoveCacheReason.DocumentClosed) {
|
||||||
@@ -150,7 +148,7 @@ export default class GitProvider extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getBlameForFile(fileName: string) {
|
getBlameForFile(fileName: string) {
|
||||||
fileName = Git.normalizePath(fileName, this.repoPath);
|
fileName = Git.normalizePath(fileName);
|
||||||
|
|
||||||
const cacheKey = this._getBlameCacheKey(fileName);
|
const cacheKey = this._getBlameCacheKey(fileName);
|
||||||
if (this.UseCaching) {
|
if (this.UseCaching) {
|
||||||
@@ -164,15 +162,14 @@ export default class GitProvider extends Disposable {
|
|||||||
console.log('[GitLens]', `Skipping blame; ${fileName} is gitignored`);
|
console.log('[GitLens]', `Skipping blame; ${fileName} is gitignored`);
|
||||||
blame = GitProvider.BlameEmptyPromise;
|
blame = GitProvider.BlameEmptyPromise;
|
||||||
} else {
|
} else {
|
||||||
const enricher = new GitBlameEnricher(GitProvider.BlameFormat, this.repoPath);
|
const enricher = new GitBlameParserEnricher(GitProvider.BlameFormat);
|
||||||
blame = Git.blame(GitProvider.BlameFormat, fileName, this.repoPath)
|
|
||||||
.then(data => enricher.enrich(data, fileName));
|
|
||||||
|
|
||||||
if (this.UseCaching) {
|
blame = Git.blame(GitProvider.BlameFormat, fileName)
|
||||||
// Trap and cache expected blame errors
|
.then(data => enricher.enrich(data, fileName))
|
||||||
blame.catch(ex => {
|
.catch(ex => {
|
||||||
const msg = ex && ex.toString();
|
// Trap and cache expected blame errors
|
||||||
if (msg && (msg.includes('is outside repository') || msg.includes('no such path'))) {
|
if (this.UseCaching) {
|
||||||
|
const msg = ex && ex.toString();
|
||||||
console.log('[GitLens]', `Replace blame cache: cacheKey=${cacheKey}`);
|
console.log('[GitLens]', `Replace blame cache: cacheKey=${cacheKey}`);
|
||||||
this._blameCache.set(cacheKey, <IBlameCacheEntry>{
|
this._blameCache.set(cacheKey, <IBlameCacheEntry>{
|
||||||
//date: new Date(),
|
//date: new Date(),
|
||||||
@@ -181,16 +178,7 @@ export default class GitProvider extends Disposable {
|
|||||||
});
|
});
|
||||||
return GitProvider.BlameEmptyPromise;
|
return GitProvider.BlameEmptyPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
const brokenBlame = this._blameCache.get(cacheKey);
|
|
||||||
if (brokenBlame) {
|
|
||||||
brokenBlame.errorMessage = msg;
|
|
||||||
this._blameCache.set(cacheKey, brokenBlame);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ex;
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.UseCaching) {
|
if (this.UseCaching) {
|
||||||
@@ -238,7 +226,7 @@ export default class GitProvider extends Disposable {
|
|||||||
blame.commits.forEach(c => {
|
blame.commits.forEach(c => {
|
||||||
if (!shas.has(c.sha)) return;
|
if (!shas.has(c.sha)) return;
|
||||||
|
|
||||||
const commit: IGitCommit = new GitCommit(this.repoPath, c.sha, c.fileName, c.author, c.date, c.message,
|
const commit: IGitCommit = new GitCommit(c.repoPath, c.sha, c.fileName, c.author, c.date, c.message,
|
||||||
c.lines.filter(l => l.line >= range.start.line && l.line <= range.end.line), c.originalFileName, c.previousSha, c.previousFileName);
|
c.lines.filter(l => l.line >= range.start.line && l.line <= range.end.line), c.originalFileName, c.previousSha, c.previousFileName);
|
||||||
commits.set(c.sha, commit);
|
commits.set(c.sha, commit);
|
||||||
|
|
||||||
@@ -274,7 +262,7 @@ export default class GitProvider extends Disposable {
|
|||||||
|
|
||||||
const lines = blame.lines.slice(range.start.line, range.end.line + 1).filter(l => l.sha === sha);
|
const lines = blame.lines.slice(range.start.line, range.end.line + 1).filter(l => l.sha === sha);
|
||||||
let commit = blame.commits.get(sha);
|
let commit = blame.commits.get(sha);
|
||||||
commit = new GitCommit(this.repoPath, commit.sha, commit.fileName, commit.author, commit.date, commit.message,
|
commit = new GitCommit(commit.repoPath, commit.sha, commit.fileName, commit.author, commit.date, commit.message,
|
||||||
lines, commit.originalFileName, commit.previousSha, commit.previousFileName);
|
lines, commit.originalFileName, commit.previousSha, commit.previousFileName);
|
||||||
return {
|
return {
|
||||||
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
|
author: Object.assign({}, blame.authors.get(commit.author), { lineCount: commit.lines.length }),
|
||||||
@@ -304,50 +292,12 @@ export default class GitProvider extends Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHistoryLocations(fileName: string, range: Range) {
|
|
||||||
// return this.getBlameForRange(fileName, range).then(blame => {
|
|
||||||
// if (!blame) return null;
|
|
||||||
|
|
||||||
// const commitCount = blame.commits.size;
|
|
||||||
|
|
||||||
// const locations: Array<Location> = [];
|
|
||||||
// Array.from(blame.commits.values())
|
|
||||||
// .forEach((c, i) => {
|
|
||||||
// const uri = this.toBlameUri(c, i + 1, commitCount, range);
|
|
||||||
// c.lines.forEach(l => locations.push(new Location(c.originalFileName
|
|
||||||
// ? this.toBlameUri(c, i + 1, commitCount, range, c.originalFileName)
|
|
||||||
// : uri,
|
|
||||||
// new Position(l.originalLine, 0))));
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return locations;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const commitMessageMatcher = /^([\^0-9a-fA-F]{7})\s(.*)$/gm;
|
|
||||||
|
|
||||||
// getCommitMessage(sha: string) {
|
|
||||||
// return Git.getCommitMessage(sha, this.repoPath);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// getCommitMessages(fileName: string) {
|
|
||||||
// return Git.getCommitMessages(fileName, this.repoPath).then(data => {
|
|
||||||
// const commits: Map<string, string> = new Map();
|
|
||||||
// let m: Array<string>;
|
|
||||||
// while ((m = commitMessageMatcher.exec(data)) != null) {
|
|
||||||
// commits.set(m[1], m[2]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return commits;
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
getVersionedFile(fileName: string, sha: string) {
|
getVersionedFile(fileName: string, sha: string) {
|
||||||
return Git.getVersionedFile(fileName, this.repoPath, sha);
|
return Git.getVersionedFile(fileName, sha);
|
||||||
}
|
}
|
||||||
|
|
||||||
getVersionedFileText(fileName: string, sha: string) {
|
getVersionedFileText(fileName: string, sha: string) {
|
||||||
return Git.getVersionedFileText(fileName, this.repoPath, sha);
|
return Git.getVersionedFileText(fileName, sha);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromBlameUri(uri: Uri): IGitBlameUriData {
|
static fromBlameUri(uri: Uri): IGitBlameUriData {
|
||||||
@@ -385,10 +335,10 @@ export default class GitProvider extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static _toGitUriData<T extends IGitUriData>(commit: IGitCommit, index: number, originalFileName?: string): T {
|
private static _toGitUriData<T extends IGitUriData>(commit: IGitCommit, index: number, originalFileName?: string): T {
|
||||||
const fileName = originalFileName || commit.fileName;
|
const fileName = Git.normalizePath(path.join(commit.repoPath, commit.fileName));
|
||||||
const data = { fileName: commit.fileName, sha: commit.sha, index: index } as T;
|
const data = { fileName: fileName, sha: commit.sha, index: index } as T;
|
||||||
if (originalFileName) {
|
if (originalFileName) {
|
||||||
data.originalFileName = originalFileName;
|
data.originalFileName = Git.normalizePath(path.join(commit.repoPath, originalFileName));
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user