Skip to content

Commit 3b2c1b3

Browse files
committed
Add LogFormat as an arguments enum and utilities to check for log format arguments in the child process commands
Use a `LogFormat` in diff parser to handle various summary formats Pass the `LogFormat` into diff parser from log and stash tasks
1 parent de949d5 commit 3b2c1b3

File tree

8 files changed

+177
-43
lines changed

8 files changed

+177
-43
lines changed

simple-git/src/lib/args/log-format.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
export enum LogFormat {
3+
NONE = '',
4+
STAT = '--stat',
5+
NUM_STAT = '--numstat',
6+
NAME_ONLY = '--name-only',
7+
NAME_STATUS = '--name-status',
8+
}
9+
10+
const logFormatRegex = /^--(stat|numstat|name-only|name-status)(=|$)/;
11+
12+
export function logFormatFromCommand(customArgs: string[]) {
13+
for (let i = 0; i < customArgs.length; i++) {
14+
const format = logFormatRegex.exec(customArgs[i]);
15+
if (format) {
16+
return `--${format[1]}` as LogFormat;
17+
}
18+
}
19+
20+
return LogFormat.NONE;
21+
}
22+
23+
export function isLogFormat(customArg: string | unknown) {
24+
return logFormatRegex.test(customArg as string);
25+
}
Lines changed: 80 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,101 @@
11
import { DiffResult } from '../../../typings';
2+
import { LogFormat } from '../args/log-format';
23
import { DiffSummary } from '../responses/DiffSummary';
34
import { asNumber, LineParser, parseStringResponse } from '../utils';
45

5-
const parsers = {
6-
summary: new LineParser<DiffResult>(/(\d+) files? changed\s*((?:, \d+ [^,]+){0,2})/, (result, [changed, summary]) => {
6+
const statParser = [
7+
new LineParser<DiffResult>(/(.+)\s+\|\s+(\d+)(\s+[+\-]+)?$/, (result, [file, changes, alterations = '']) => {
8+
result.files.push({
9+
file: file.trim(),
10+
changes: asNumber(changes),
11+
insertions: alterations.replace(/[^+]/g, '').length,
12+
deletions: alterations.replace(/[^-]/g, '').length,
13+
binary: false
14+
});
15+
}),
16+
new LineParser<DiffResult>(/(.+) \|\s+Bin ([0-9.]+) -> ([0-9.]+) ([a-z]+)/, (result, [file, before, after]) => {
17+
result.files.push({
18+
file: file.trim(),
19+
before: asNumber(before),
20+
after: asNumber(after),
21+
binary: true
22+
});
23+
}),
24+
new LineParser<DiffResult>(/(\d+) files? changed\s*((?:, \d+ [^,]+){0,2})/, (result, [changed, summary]) => {
725
const inserted = /(\d+) i/.exec(summary);
826
const deleted = /(\d+) d/.exec(summary);
927

1028
result.changed = asNumber(changed);
1129
result.insertions = asNumber(inserted?.[1]);
1230
result.deletions = asNumber(deleted?.[1]);
13-
}),
31+
})
32+
];
33+
34+
const numStatParser = [
35+
new LineParser<DiffResult>(/(\d+)\t(\d+)\t(.+)$/, (result, [changesInsert, changesDelete, file]) => {
36+
const insertions = asNumber(changesInsert);
37+
const deletions = asNumber(changesDelete);
38+
39+
result.changed++;
40+
result.insertions += insertions;
41+
result.deletions += deletions;
1442

15-
binary: new LineParser<DiffResult>(/(.+) \|\s+Bin ([0-9.]+) -> ([0-9.]+) ([a-z]+)/, (result, [file, before, after]) => {
1643
result.files.push({
17-
file: file.trim(),
18-
before: asNumber(before),
19-
after: asNumber(after),
20-
binary: true
44+
file,
45+
changes: insertions + deletions,
46+
insertions,
47+
deletions,
48+
binary: false,
2149
});
2250
}),
51+
new LineParser<DiffResult>(/-\t-\t(.+)$/, (result, [file]) => {
52+
result.changed++;
2353

24-
text: new LineParser<DiffResult>(/(.+)\s+\|\s+(\d+)(\s+[+\-]+)?$/, (result, [file, changes, alterations = '']) => {
2554
result.files.push({
26-
file: file.trim(),
27-
changes: asNumber(changes),
28-
insertions: alterations.replace(/[^+]/g, '').length,
29-
deletions: alterations.replace(/[^-]/g, '').length,
30-
binary: false
55+
file,
56+
after: 0,
57+
before: 0,
58+
binary: true,
3159
});
32-
}),
33-
}
60+
})
61+
];
62+
63+
const nameOnlyParser = [
64+
new LineParser<DiffResult>(/(.+)$/, (result, [file]) => {
65+
result.changed++;
66+
result.files.push({
67+
file,
68+
changes: 0,
69+
insertions: 0,
70+
deletions: 0,
71+
binary: false,
72+
});
73+
})
74+
];
75+
76+
const nameStatusParser = [
77+
new LineParser<DiffResult>(/([ACDMRTUXB])\s*(.+)$/, (result, [_status, file]) => {
78+
result.changed++;
79+
result.files.push({
80+
file,
81+
changes: 0,
82+
insertions: 0,
83+
deletions: 0,
84+
binary: false,
85+
});
86+
})
87+
];
3488

35-
export function parseDiffResult(stdOut: string): DiffResult {
36-
const status = new DiffSummary();
89+
const diffSummaryParsers: Record<LogFormat, LineParser<DiffResult>[]> = {
90+
[LogFormat.NONE]: statParser,
91+
[LogFormat.STAT]: statParser,
92+
[LogFormat.NUM_STAT]: numStatParser,
93+
[LogFormat.NAME_STATUS]: nameStatusParser,
94+
[LogFormat.NAME_ONLY]: nameOnlyParser,
95+
};
3796

38-
parseStringResponse(status, [
39-
parsers.text,
40-
parsers.binary,
41-
parsers.summary,
42-
], stdOut);
97+
export function getDiffParser(format = LogFormat.NONE) {
98+
const parser = diffSummaryParsers[format];
4399

44-
return status;
100+
return (stdOut: string) => parseStringResponse(new DiffSummary(), parser, stdOut);
45101
}

simple-git/src/lib/parsers/parse-list-log-summary.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ListLogLine, LogResult } from '../../../typings';
22
import { toLinesWithContent } from '../utils';
3-
import { parseDiffResult } from './parse-diff-summary';
3+
import { getDiffParser } from './parse-diff-summary';
4+
import { LogFormat } from '../args/log-format';
45

56
export const START_BOUNDARY = 'òòòòòò ';
67

@@ -17,7 +18,9 @@ function lineBuilder(tokens: string[], fields: string[]): any {
1718
}, Object.create({diff: null}) as any);
1819
}
1920

20-
export function createListLogSummaryParser<T = any> (splitter = SPLITTER, fields = defaultFieldNames) {
21+
export function createListLogSummaryParser<T = any> (splitter = SPLITTER, fields = defaultFieldNames, logFormat = LogFormat.NONE) {
22+
const parseDiffResult = getDiffParser(logFormat);
23+
2124
return function (stdOut: string): LogResult<T> {
2225
const all: ReadonlyArray<T & ListLogLine> = toLinesWithContent(stdOut, true, START_BOUNDARY)
2326
.map(function (item) {

simple-git/src/lib/tasks/diff.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
import { StringTask } from '../types';
22
import { DiffResult } from '../../../typings';
3-
import { parseDiffResult } from '../parsers/parse-diff-summary';
3+
import { isLogFormat, LogFormat, logFormatFromCommand } from '../args/log-format';
4+
import { getDiffParser } from '../parsers/parse-diff-summary';
5+
import { configurationErrorTask, EmptyTask } from './task';
46

57
export function diffSummaryTask(customArgs: string[]): StringTask<DiffResult> {
8+
let logFormat = logFormatFromCommand(customArgs);
9+
10+
const commands = ['diff'];
11+
12+
if (logFormat === LogFormat.NONE) {
13+
logFormat = LogFormat.STAT;
14+
commands.push('--stat=4096');
15+
}
16+
17+
commands.push(...customArgs);
18+
619
return {
7-
commands: ['diff', '--stat=4096', ...customArgs],
20+
commands,
821
format: 'utf-8',
9-
parser (stdOut) {
10-
return parseDiffResult(stdOut);
11-
}
22+
parser: getDiffParser(logFormat),
23+
}
24+
}
25+
26+
export function validateSummaryOptions(customArgs: unknown[]): EmptyTask | void {
27+
const flags = customArgs.filter(isLogFormat);
28+
29+
if (flags.length > 1) {
30+
return configurationErrorTask(`Summary flags are mutually exclusive - pick one of ${flags.join(',')}`);
1231
}
1332
}

simple-git/src/lib/tasks/log.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Options, StringTask } from '../types';
22
import { LogResult, SimpleGit } from '../../../typings';
3+
import { logFormatFromCommand } from '../args/log-format';
34
import {
45
COMMIT_BOUNDARY,
56
createListLogSummaryParser,
@@ -17,6 +18,7 @@ import {
1718
} from '../utils';
1819
import { SimpleGitApi } from '../simple-git-api';
1920
import { configurationErrorTask } from './task';
21+
import { validateSummaryOptions } from './diff';
2022

2123
enum excludeOptions {
2224
'--pretty',
@@ -133,19 +135,23 @@ export function parseLogOptions<T extends Options>(opt: Options | LogOptions<T>
133135
}
134136

135137
export function logTask<T>(splitter: string, fields: string[], customArgs: string[]): StringTask<LogResult<T>> {
138+
const parser = createListLogSummaryParser(splitter, fields, logFormatFromCommand(customArgs));
139+
136140
return {
137141
commands: ['log', ...customArgs],
138142
format: 'utf-8',
139-
parser: createListLogSummaryParser(splitter, fields),
143+
parser,
140144
};
141145
}
142146

143147
export default function (): Pick<SimpleGit, 'log'> {
144148
return {
145149
log<T extends Options>(this: SimpleGitApi, ...rest: unknown[]) {
146150
const next = trailingFunctionArgument(arguments);
151+
const options = parseLogOptions<T>(trailingOptionsArgument(arguments), filterType(arguments[0], filterArray));
147152
const task = rejectDeprecatedSignatures(...rest) ||
148-
createLogTask(parseLogOptions<T>(trailingOptionsArgument(arguments), filterType(arguments[0], filterArray)))
153+
validateSummaryOptions(options.commands) ||
154+
createLogTask(options)
149155

150156
return this._runTask(task, next);
151157
}

simple-git/src/lib/tasks/stash-list.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { LogOptions, LogResult } from '../../../typings';
2+
import { logFormatFromCommand } from '../args/log-format';
23
import { createListLogSummaryParser } from '../parsers/parse-list-log-summary';
34
import { StringTask } from '../types';
45
import { parseLogOptions } from './log';
56

67
export function stashListTask(opt: LogOptions = {}, customArgs: string[]): StringTask<LogResult> {
78
const options = parseLogOptions<any>(opt);
8-
const parser = createListLogSummaryParser(options.splitter, options.fields);
9+
const commands = ['stash', 'list', ...options.commands, ...customArgs];
10+
const parser = createListLogSummaryParser(options.splitter, options.fields, logFormatFromCommand(commands));
911

1012
return {
11-
commands: ['stash', 'list', ...options.commands, ...customArgs],
13+
commands,
1214
format: 'utf-8',
1315
parser,
1416
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { LogFormat, logFormatFromCommand } from '../../src/lib/args/log-format';
2+
3+
describe('log-format', function () {
4+
5+
it.each<[LogFormat, string[]]>([
6+
[LogFormat.NONE, []],
7+
[LogFormat.NONE, ['foo', 'bar', '--nothing']],
8+
[LogFormat.STAT, ['foo', '--stat', 'bar']],
9+
[LogFormat.STAT, ['foo', '--stat=4096', '--bar']],
10+
[LogFormat.NUM_STAT, ['foo', '--numstat', '--bar']],
11+
[LogFormat.NAME_ONLY, ['--name-only', 'foo', '--bar']],
12+
[LogFormat.NAME_STATUS, ['--name-status']],
13+
])('Picks %s from %s', (format, args) => {
14+
expect(logFormatFromCommand(args)).toBe(format);
15+
});
16+
17+
it('picks the first format', () => {
18+
expect(logFormatFromCommand(['--stat', '--numstat'])).toBe(LogFormat.STAT);
19+
expect(logFormatFromCommand(['--numstat', '--stat'])).toBe(LogFormat.NUM_STAT);
20+
});
21+
22+
});

simple-git/test/unit/diff.spec.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
wait
1111
} from './__fixtures__';
1212
import { SimpleGit, TaskConfigurationError } from '../..';
13-
import { parseDiffResult } from '../../src/lib/parsers/parse-diff-summary';
13+
import { LogFormat } from '../../src/lib/args/log-format';
14+
import { getDiffParser } from '../../src/lib/parsers/parse-diff-summary';
1415
import { promiseError } from '@kwsites/promise-result';
1516

1617
describe('diff', () => {
@@ -20,7 +21,7 @@ describe('diff', () => {
2021
describe('parsing', () => {
2122

2223
it('bin summary', () => {
23-
const summary = parseDiffResult(`
24+
const summary = getDiffParser(LogFormat.STAT)(`
2425
my-package.tar.gz | Bin 3163 -> 3244 bytes
2526
1 file changed, 0 insertions(+), 0 deletions(-)
2627
`);
@@ -37,7 +38,7 @@ describe('diff', () => {
3738
});
3839

3940
it('single text file with changes', () => {
40-
const actual = parseDiffResult(
41+
const actual = getDiffParser(LogFormat.STAT)(
4142
diffSummarySingleFile(1, 2, 'package.json').stdOut
4243
);
4344
expect(actual).toEqual(like({
@@ -58,7 +59,7 @@ describe('diff', () => {
5859
});
5960

6061
it('multiple text files', () => {
61-
const actual = parseDiffResult(diffSummaryMultiFile(
62+
const actual = getDiffParser(LogFormat.STAT)(diffSummaryMultiFile(
6263
{fileName: 'src/git.js', insertions: 2},
6364
{fileName: 'test/testCommands.js', deletions: 2, insertions: 1},
6465
).stdOut);
@@ -87,7 +88,7 @@ describe('diff', () => {
8788
});
8889

8990
it('recognises binary files', () => {
90-
const actual = parseDiffResult(`
91+
const actual = getDiffParser(LogFormat.STAT)(`
9192
some/image.png | Bin 0 -> 9806 bytes
9293
1 file changed, 1 insertion(+)
9394
`);
@@ -105,7 +106,7 @@ describe('diff', () => {
105106
});
106107

107108
it('recognises files changed in modified time only', () => {
108-
const actual = parseDiffResult(`
109+
const actual = getDiffParser(LogFormat.STAT)(`
109110
abc | 0
110111
def | 1 +
111112
2 files changed, 1 insertion(+)
@@ -120,8 +121,8 @@ describe('diff', () => {
120121
});
121122

122123
it('picks number of files changed from summary line', () => {
123-
expect(parseDiffResult('1 file changed, 1 insertion(+)')).toHaveProperty('changed', 1);
124-
expect(parseDiffResult('2 files changed, 1 insertion(+), 1 deletion(+)')).toHaveProperty('changed', 2);
124+
expect(getDiffParser(LogFormat.STAT)('1 file changed, 1 insertion(+)')).toHaveProperty('changed', 1);
125+
expect(getDiffParser(LogFormat.STAT)('2 files changed, 1 insertion(+), 1 deletion(+)')).toHaveProperty('changed', 2);
125126
});
126127

127128
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy