Skip to content

Commit ebb1085

Browse files
author
Eric Amodio
committed
Switches to porcelain blame format
Provides more data (commit message, previous commit, etc)
1 parent 47ce5c5 commit ebb1085

File tree

3 files changed

+159
-38
lines changed

3 files changed

+159
-38
lines changed

src/git.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default class Git {
2323

2424
if (sha) {
2525
console.log('git', 'blame', '-fn', '--root', `${sha}^`, '--', fileName);
26-
return gitCommand(repoPath, 'blame', '-fnw', '--root', `${sha}^`, '--', fileName);
26+
return gitCommand(repoPath, 'blame', '-fn', '--root', `${sha}^`, '--', fileName);
2727
}
2828

2929
console.log('git', 'blame', '-fn', '--root', '--', fileName);
@@ -32,6 +32,20 @@ export default class Git {
3232
// .catch(ex => console.error(ex));
3333
}
3434

35+
static blamePorcelain(fileName: string, repoPath: string, sha?: string) {
36+
fileName = Git.normalizePath(fileName, repoPath);
37+
38+
if (sha) {
39+
console.log('git', 'blame', '--porcelain', '--root', `${sha}^`, '--', fileName);
40+
return gitCommand(repoPath, 'blame', '--porcelain', '--root', `${sha}^`, '--', fileName);
41+
}
42+
43+
console.log('git', 'blame', '--porcelain', '--root', '--', fileName);
44+
return gitCommand(repoPath, 'blame', '--porcelain', '--root', '--', fileName);
45+
// .then(s => { console.log(s); return s; })
46+
// .catch(ex => console.error(ex));
47+
}
48+
3549
static getVersionedFile(fileName: string, repoPath: string, sha: string) {
3650
return new Promise<string>((resolve, reject) => {
3751
Git.getVersionedFileText(fileName, repoPath, sha).then(data => {
@@ -79,6 +93,7 @@ export default class Git {
7993
static getCommitMessages(fileName: string, repoPath: string) {
8094
fileName = Git.normalizePath(fileName, repoPath);
8195

96+
// git log --format="%h (%aN %x09 %ai) %s" --
8297
console.log('git', 'log', '--oneline', '--', fileName);
8398
return gitCommand(repoPath, 'log', '--oneline', '--', fileName);
8499
// .then(s => { console.log(s); return s; })

src/gitBlameController.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ export default class GitBlameController extends Disposable {
7676
class GitBlameEditorController extends Disposable {
7777
private _subscription: Disposable;
7878
private _blame: Promise<IGitBlame>;
79-
private _commits: Promise<Map<string, string>>;
79+
//private _commits: Promise<Map<string, string>>;
8080

8181
constructor(private git: GitProvider, private blameDecoration: TextEditorDecorationType, private highlightDecoration: TextEditorDecorationType, public editor: TextEditor, public sha: string) {
8282
super(() => this.dispose());
8383

8484
const fileName = this.editor.document.uri.path;
8585
this._blame = this.git.getBlameForFile(fileName);
86-
this._commits = this.git.getCommitMessages(fileName);
86+
//this._commits = this.git.getCommitMessages(fileName);
8787

8888
this._subscription = Disposable.from(window.onDidChangeTextEditorSelection(e => {
8989
const activeLine = e.selections[0].active.line;
@@ -106,22 +106,22 @@ class GitBlameEditorController extends Disposable {
106106
return this._blame.then(blame => {
107107
if (!blame.lines.length) return;
108108

109-
return this._commits.then(msgs => {
110-
const commits = Array.from(blame.commits.values());
111-
commits.forEach(c => c.message = msgs.get(c.sha.substring(0, c.sha.length - 1)));
109+
// return this._commits.then(msgs => {
110+
// const commits = Array.from(blame.commits.values());
111+
// commits.forEach(c => c.message = msgs.get(c.sha.substring(0, c.sha.length - 1)));
112112

113113
const blameDecorationOptions: DecorationOptions[] = blame.lines.map(l => {
114114
const c = blame.commits.get(l.sha);
115115
return {
116116
range: this.editor.document.validateRange(new Range(l.line, 0, l.line, 0)),
117117
hoverMessage: `${c.message}\n${c.author}, ${moment(c.date).format('MMMM Do, YYYY hh:MM a')}`,
118-
renderOptions: { before: { contentText: `${l.sha}`, } }
118+
renderOptions: { before: { contentText: `${l.sha.substring(0, 8)}`, } }
119119
};
120120
});
121121

122122
this.editor.setDecorations(this.blameDecoration, blameDecorationOptions);
123-
return this.applyHighlight(sha || commits[0].sha);
124-
});
123+
return this.applyHighlight(sha || blame.commits.values().next().value.sha);
124+
// });
125125
});
126126
}
127127

src/gitProvider.ts

Lines changed: 135 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import * as _ from 'lodash';
88

99
const blameMatcher = /^([\^0-9a-fA-F]{8})\s([\S]*)\s+([0-9\S]+)\s\((.*)\s([0-9]{4}-[0-9]{2}-[0-9]{2}\s[0-9]{2}:[0-9]{2}:[0-9]{2}\s[-|+][0-9]{4})\s+([0-9]+)\)(.*)$/gm;
1010
const commitMessageMatcher = /^([\^0-9a-fA-F]{7})\s(.*)$/gm;
11+
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;
12+
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;
1113

1214
export default class GitProvider extends Disposable {
1315
public repoPath: string;
@@ -43,53 +45,138 @@ export default class GitProvider extends Disposable {
4345
return Git.repoPath(cwd);
4446
}
4547

48+
// getBlameForFile(fileName: string) {
49+
// fileName = Git.normalizePath(fileName, this.repoPath);
50+
51+
// let blame = this._blames.get(fileName);
52+
// if (blame !== undefined) return blame;
53+
54+
// blame = Git.blame(fileName, this.repoPath)
55+
// .then(data => {
56+
// const authors: Map<string, IGitAuthor> = new Map();
57+
// const commits: Map<string, IGitCommit> = new Map();
58+
// const lines: Array<IGitCommitLine> = [];
59+
60+
// let m: Array<string>;
61+
// while ((m = blameMatcher.exec(data)) != null) {
62+
// const authorName = m[4].trim();
63+
// let author = authors.get(authorName);
64+
// if (!author) {
65+
// author = {
66+
// name: authorName,
67+
// lineCount: 0
68+
// };
69+
// authors.set(authorName, author);
70+
// }
71+
72+
// const sha = m[1];
73+
// let commit = commits.get(sha);
74+
// if (!commit) {
75+
// commit = {
76+
// sha,
77+
// fileName: fileName,
78+
// author: authorName,
79+
// date: new Date(m[5]),
80+
// lines: []
81+
// };
82+
83+
// const file = m[2].trim();
84+
// if (!fileName.toLowerCase().endsWith(file.toLowerCase())) {
85+
// commit.originalFileName = file;
86+
// }
87+
88+
// commits.set(sha, commit);
89+
// }
90+
91+
// const line: IGitCommitLine = {
92+
// sha,
93+
// line: parseInt(m[6], 10) - 1,
94+
// originalLine: parseInt(m[3], 10) - 1
95+
// //code: m[7]
96+
// }
97+
98+
// commit.lines.push(line);
99+
// lines.push(line);
100+
// }
101+
102+
// commits.forEach(c => authors.get(c.author).lineCount += c.lines.length);
103+
104+
// const sortedAuthors: Map<string, IGitAuthor> = new Map();
105+
// const values = Array.from(authors.values())
106+
// .sort((a, b) => b.lineCount - a.lineCount)
107+
// .forEach(a => sortedAuthors.set(a.name, a));
108+
109+
// const sortedCommits = new Map();
110+
// Array.from(commits.values())
111+
// .sort((a, b) => b.date.getTime() - a.date.getTime())
112+
// .forEach(c => sortedCommits.set(c.sha, c));
113+
114+
// return {
115+
// authors: sortedAuthors,
116+
// commits: sortedCommits,
117+
// lines: lines
118+
// };
119+
// });
120+
121+
// this._blames.set(fileName, blame);
122+
// return blame;
123+
// }
124+
46125
getBlameForFile(fileName: string) {
47126
fileName = Git.normalizePath(fileName, this.repoPath);
48127

49128
let blame = this._blames.get(fileName);
50129
if (blame !== undefined) return blame;
51130

52-
blame = Git.blame(fileName, this.repoPath)
131+
blame = Git.blamePorcelain(fileName, this.repoPath)
53132
.then(data => {
54133
const authors: Map<string, IGitAuthor> = new Map();
55134
const commits: Map<string, IGitCommit> = new Map();
56135
const lines: Array<IGitCommitLine> = [];
57136

58137
let m: Array<string>;
59-
while ((m = blameMatcher.exec(data)) != null) {
60-
const authorName = m[4].trim();
61-
let author = authors.get(authorName);
62-
if (!author) {
63-
author = {
64-
name: authorName,
65-
lineCount: 0
66-
};
67-
authors.set(authorName, author);
68-
}
69-
70-
const sha = m[1];
138+
while ((m = blamePorcelainMatcher.exec(data)) != null) {
139+
const sha = m[1].substring(0, 8);
71140
let commit = commits.get(sha);
72141
if (!commit) {
142+
const authorName = m[5].trim();
143+
let author = authors.get(authorName);
144+
if (!author) {
145+
author = {
146+
name: authorName,
147+
lineCount: 0
148+
};
149+
authors.set(authorName, author);
150+
}
151+
73152
commit = {
74153
sha,
75154
fileName: fileName,
76-
author: m[4].trim(),
77-
date: new Date(m[5]),
155+
author: authorName,
156+
date: moment(`${m[7]} ${m[8]}`, 'X Z').toDate(),
157+
message: m[13],
78158
lines: []
79159
};
160+
161+
const originalFileName = m[16];
162+
if (!fileName.toLowerCase().endsWith(originalFileName.toLowerCase())) {
163+
commit.originalFileName = originalFileName;
164+
}
165+
166+
const previousSha = m[14];
167+
if (previousSha) {
168+
commit.previousSha = previousSha.substring(0, 8);
169+
commit.previousFileName = m[15];
170+
}
171+
80172
commits.set(sha, commit);
81173
}
82174

83175
const line: IGitCommitLine = {
84176
sha,
85-
line: parseInt(m[6], 10) - 1,
86-
originalLine: parseInt(m[3], 10) - 1
87-
//code: m[7]
88-
}
89-
90-
const file = m[2].trim();
91-
if (!fileName.toLowerCase().endsWith(file.toLowerCase())) {
92-
line.originalFileName = file;
177+
line: parseInt(m[3], 10) - 1,
178+
originalLine: parseInt(m[2], 10) - 1
179+
//code: m[17]
93180
}
94181

95182
commit.lines.push(line);
@@ -116,8 +203,7 @@ export default class GitProvider extends Disposable {
116203
});
117204

118205
this._blames.set(fileName, blame);
119-
return blame;
120-
}
206+
return blame; }
121207

122208
getBlameForLine(fileName: string, line: number): Promise<IGitBlameLine> {
123209
return this.getBlameForFile(fileName).then(blame => {
@@ -192,8 +278,8 @@ export default class GitProvider extends Disposable {
192278
Array.from(blame.commits.values())
193279
.forEach((c, i) => {
194280
const uri = this.toBlameUri(c, i + 1, commitCount, range);
195-
c.lines.forEach(l => locations.push(new Location(l.originalFileName
196-
? this.toBlameUri(c, i + 1, commitCount, range, l.originalFileName)
281+
c.lines.forEach(l => locations.push(new Location(c.originalFileName
282+
? this.toBlameUri(c, i + 1, commitCount, range, c.originalFileName)
197283
: uri,
198284
new Position(l.originalLine, 0))));
199285
});
@@ -202,6 +288,24 @@ export default class GitProvider extends Disposable {
202288
});
203289
}
204290

291+
// getHistoryLocations(fileName: string, range: Range) {
292+
// return this.getBlameForRange(fileName, range).then(blame => {
293+
// const commitCount = blame.commits.size;
294+
295+
// const locations: Array<Location> = [];
296+
// Array.from(blame.commits.values())
297+
// .forEach((c, i) => {
298+
// const uri = this.toBlameUri(c, i + 1, commitCount, range);
299+
// c.lines.forEach(l => locations.push(new Location(c.originalFileName
300+
// ? this.toBlameUri(c, i + 1, commitCount, range, c.originalFileName)
301+
// : uri,
302+
// new Position(l.originalLine, 0))));
303+
// });
304+
305+
// return locations;
306+
// });
307+
// }
308+
205309
getCommitMessage(sha: string) {
206310
return Git.getCommitMessage(sha, this.repoPath);
207311
}
@@ -303,15 +407,17 @@ export interface IGitCommit {
303407
fileName: string;
304408
author: string;
305409
date: Date;
410+
message: string;
306411
lines: IGitCommitLine[];
307-
message?: string;
412+
originalFileName?: string;
413+
previousSha?: string;
414+
previousFileName?: string;
308415
}
309416

310417
export interface IGitCommitLine {
311418
sha: string;
312419
line: number;
313420
originalLine: number;
314-
originalFileName?: string;
315421
code?: string;
316422
}
317423

0 commit comments

Comments
 (0)