Skip to content

Commit 79b960c

Browse files
committed
Adds changes stats for file commits
1 parent b6a1b92 commit 79b960c

File tree

5 files changed

+115
-30
lines changed

5 files changed

+115
-30
lines changed

src/git/formatters/commitFormatter.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { DateStyle, FileAnnotationType } from '../../configuration';
1111
import { GlyphChars } from '../../constants';
1212
import { Container } from '../../container';
13-
import { GitCommit, GitCommitType, GitLogCommit, GitRemote, GitService, GitUri } from '../gitService';
13+
import { GitCommit, GitLogCommit, GitRemote, GitService, GitUri } from '../gitService';
1414
import { Strings } from '../../system';
1515
import { FormatOptions, Formatter } from './formatter';
1616
import * as emojis from '../../emojis.json';
@@ -143,20 +143,17 @@ export class CommitFormatter extends Formatter<GitCommit, CommitFormatOptions> {
143143
}
144144

145145
get changes() {
146-
if (!(this._item instanceof GitLogCommit) || this._item.type === GitCommitType.LogFile) {
147-
return this._padOrTruncate(emptyStr, this._options.tokenOptions.changes);
148-
}
149-
150-
return this._padOrTruncate(this._item.getFormattedDiffStatus(), this._options.tokenOptions.changes);
146+
return this._padOrTruncate(
147+
this._item instanceof GitLogCommit ? this._item.getFormattedDiffStatus() : emptyStr,
148+
this._options.tokenOptions.changes
149+
);
151150
}
152151

153152
get changesShort() {
154-
if (!(this._item instanceof GitLogCommit) || this._item.type === GitCommitType.LogFile) {
155-
return this._padOrTruncate(emptyStr, this._options.tokenOptions.changesShort);
156-
}
157-
158153
return this._padOrTruncate(
159-
this._item.getFormattedDiffStatus({ compact: true, separator: emptyStr }),
154+
this._item instanceof GitLogCommit
155+
? this._item.getFormattedDiffStatus({ compact: true, separator: emptyStr })
156+
: emptyStr,
160157
this._options.tokenOptions.changesShort
161158
);
162159
}

src/git/git.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ export class Git {
685685
) {
686686
const [file, root] = Git.splitPath(fileName, repoPath);
687687

688-
const params = ['log', '--name-status', `--format=${format}`];
688+
const params = ['log', `--format=${format}`];
689689

690690
if (maxCount && !reverse) {
691691
params.push(`-n${maxCount}`);
@@ -697,7 +697,7 @@ export class Git {
697697
}
698698

699699
if (startLine == null) {
700-
params.push('--name-status');
700+
params.push('--numstat', '--summary');
701701
}
702702
else {
703703
// Don't include --name-status or -s because Git won't honor it

src/git/models/logCommit.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ import { GitUri } from '../gitUri';
66
import { GitCommit, GitCommitType } from './commit';
77
import { GitFile, GitFileStatus } from './file';
88

9+
const emptyStats = Object.freeze({
10+
added: 0,
11+
deleted: 0,
12+
changed: 0
13+
});
14+
915
export interface GitLogCommitLine {
1016
from: {
1117
line: number;
@@ -36,6 +42,12 @@ export class GitLogCommit extends GitCommit {
3642
originalFileName?: string | undefined,
3743
previousSha?: string | undefined,
3844
previousFileName?: string | undefined,
45+
private readonly _fileStats?:
46+
| {
47+
insertions: number;
48+
deletions: number;
49+
}
50+
| undefined,
3951
public readonly parentShas?: string[],
4052
public readonly line?: GitLogCommitLine
4153
) {
@@ -69,13 +81,21 @@ export class GitLogCommit extends GitCommit {
6981

7082
@memoize()
7183
getDiffStatus() {
84+
if (this._fileStats !== undefined) {
85+
return {
86+
added: this._fileStats.insertions,
87+
deleted: this._fileStats.deletions,
88+
changed: 0
89+
};
90+
}
91+
92+
if (this.isFile || this.files.length === 0) return emptyStats;
93+
7294
const diff = {
7395
added: 0,
7496
deleted: 0,
7597
changed: 0
7698
};
77-
if (this.files.length === 0) return diff;
78-
7999
for (const f of this.files) {
80100
switch (f.status) {
81101
case 'A':
@@ -113,21 +133,24 @@ export class GitLogCommit extends GitCommit {
113133
if (added === 0 && changed === 0 && deleted === 0) return empty || '';
114134

115135
if (expand) {
136+
const type = this.isFile ? 'line' : 'file';
137+
116138
let status = '';
117139
if (added) {
118-
status += `${Strings.pluralize('file', added)} added`;
140+
status += `${Strings.pluralize(type, added)} added`;
119141
}
120142
if (changed) {
121-
status += `${status.length === 0 ? '' : separator}${Strings.pluralize('file', changed)} changed`;
143+
status += `${status.length === 0 ? '' : separator}${Strings.pluralize(type, changed)} changed`;
122144
}
123145
if (deleted) {
124-
status += `${status.length === 0 ? '' : separator}${Strings.pluralize('file', deleted)} deleted`;
146+
status += `${status.length === 0 ? '' : separator}${Strings.pluralize(type, deleted)} deleted`;
125147
}
126148
return `${prefix}${status}${suffix}`;
127149
}
128150

151+
// When `isFile` we are getting line changes -- and we can't get changed lines (only inserts and deletes)
129152
return `${prefix}${compact && added === 0 ? '' : `+${added}${separator}`}${
130-
compact && changed === 0 ? '' : `~${changed}${separator}`
153+
(compact || this.isFile) && changed === 0 ? '' : `~${changed}${separator}`
131154
}${compact && deleted === 0 ? '' : `-${deleted}`}${suffix}`;
132155
}
133156

@@ -195,7 +218,9 @@ export class GitLogCommit extends GitCommit {
195218
this.getChangedValue(changes.originalFileName, this.originalFileName),
196219
this.getChangedValue(changes.previousSha, this.previousSha),
197220
this.getChangedValue(changes.previousFileName, this.previousFileName),
198-
undefined
221+
this._fileStats,
222+
this.parentShas,
223+
this.line
199224
);
200225
}
201226
}

src/git/parsers/logParser.ts

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ const diffRegex = /diff --git a\/(.*) b\/(.*)/;
1212
const diffRangeRegex = /^@@ -(\d+?),(\d+?) \+(\d+?),(\d+?) @@/;
1313

1414
export const fileStatusRegex = /(\S)\S*\t([^\t\n]+)(?:\t(.+))?/;
15+
const fileStatusAndSummaryRegex = /^(\d+?|-)\s+?(\d+?|-)\s+?(.*)(?:\n\s(delete|rename|create))?/;
16+
const fileStatusAndSummaryRenamedFileRegex = /(.+)\s=>\s(.+)/;
17+
const fileStatusAndSummaryRenamedFilePathRegex = /(.*?){(.+?)\s=>\s(.+?)}(.*)/;
18+
1519
const logFileSimpleRegex = /^<r> (.*)\s*(?:(?:diff --git a\/(.*) b\/(.*))|(?:(\S)\S*\t([^\t\n]+)(?:\t(.+))?))/gm;
1620
const logFileSimpleRenamedRegex = /^<r> (\S+)\s*(.*)$/s;
1721
const logFileSimpleRenamedFilesRegex = /^(\S)\S*\t([^\t\n]+)(?:\t(.+)?)$/gm;
@@ -37,6 +41,10 @@ interface LogEntry {
3741
files?: GitFile[];
3842

3943
status?: GitFileStatus;
44+
fileStats?: {
45+
insertions: number;
46+
deletions: number;
47+
};
4048

4149
summary?: string;
4250

@@ -99,6 +107,7 @@ export class GitLogParser {
99107

100108
let match;
101109
let renamedFileName;
110+
let renamedMatch;
102111

103112
while (true) {
104113
next = lines.next();
@@ -242,18 +251,64 @@ export class GitLogParser {
242251
break;
243252
}
244253
else {
245-
match = fileStatusRegex.exec(line);
254+
next = lines.next();
255+
match = fileStatusAndSummaryRegex.exec(`${line}\n${next.value}`);
246256
if (match != null) {
247-
entry.status = match[1] as GitFileStatus;
248-
renamedFileName = match[3];
249-
if (renamedFileName !== undefined) {
250-
entry.fileName = renamedFileName;
251-
entry.originalFileName = match[2];
252-
}
253-
else {
254-
entry.fileName = match[2];
257+
entry.fileStats = {
258+
insertions: Number(match[1]) || 0,
259+
deletions: Number(match[2]) || 0
260+
};
261+
262+
switch (match[4]) {
263+
case undefined:
264+
entry.status = 'M' as GitFileStatus;
265+
entry.fileName = match[3];
266+
break;
267+
case 'rename':
268+
entry.status = 'R' as GitFileStatus;
269+
270+
renamedFileName = match[3];
271+
renamedMatch = fileStatusAndSummaryRenamedFilePathRegex.exec(
272+
renamedFileName
273+
);
274+
if (renamedMatch != null) {
275+
entry.fileName = `${renamedMatch[1]}${renamedMatch[3]}${
276+
renamedMatch[4]
277+
}`;
278+
entry.originalFileName = `${renamedMatch[1]}${renamedMatch[2]}${
279+
renamedMatch[4]
280+
}`;
281+
}
282+
else {
283+
renamedMatch = fileStatusAndSummaryRenamedFileRegex.exec(
284+
renamedFileName
285+
);
286+
if (renamedMatch != null) {
287+
entry.fileName = renamedMatch[2];
288+
entry.originalFileName = renamedMatch[1];
289+
}
290+
else {
291+
entry.fileName = renamedFileName;
292+
}
293+
}
294+
295+
break;
296+
case 'create':
297+
entry.status = 'A' as GitFileStatus;
298+
entry.fileName = match[3];
299+
break;
300+
case 'delete':
301+
entry.status = 'D' as GitFileStatus;
302+
entry.fileName = match[3];
303+
break;
304+
default:
305+
entry.status = 'M' as GitFileStatus;
306+
entry.fileName = match[3];
307+
break;
255308
}
256309
}
310+
311+
if (next.done || next.value === '</f>') break;
257312
}
258313
}
259314
}
@@ -355,6 +410,7 @@ export class GitLogParser {
355410

356411
const originalFileName =
357412
entry.originalFileName || (relativeFileName !== entry.fileName ? entry.fileName : undefined);
413+
358414
if (type === GitCommitType.LogFile) {
359415
entry.files = [
360416
{
@@ -380,6 +436,7 @@ export class GitLogParser {
380436
originalFileName,
381437
type === GitCommitType.Log ? entry.parentShas![0] : undefined,
382438
undefined,
439+
entry.fileStats,
383440
entry.parentShas!,
384441
entry.line
385442
);

src/views/nodes/commitFileNode.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,13 @@ export class CommitFileNode extends ViewRefFileNode {
162162
this._tooltip = CommitFormatter.fromTemplate(
163163
this.commit.isUncommitted
164164
? `\${author} ${GlyphChars.Dash} \${id}\n${status}\n\${ago} (\${date})`
165-
: `\${author} ${GlyphChars.Dash} \${id}\n${status}\n\${ago} (\${date})\n\n\${message}`,
165+
: `\${author} ${
166+
GlyphChars.Dash
167+
} \${id}\n${status}\n\${ago} (\${date})\n\n\${message}${this.commit.getFormattedDiffStatus({
168+
expand: true,
169+
prefix: '\n\n',
170+
separator: '\n'
171+
})}`,
166172
this.commit,
167173
{
168174
dateFormat: Container.config.defaultDateFormat

0 commit comments

Comments
 (0)