Skip to content

Commit 111d02a

Browse files
committed
feat: allow to filter git log
1 parent 9a25dcb commit 111d02a

8 files changed

Lines changed: 214 additions & 33 deletions

File tree

apps/backend/src/infrastructure/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const initConfig: Config = {
99
scopes: [],
1010
groups: [],
1111
entries: [],
12+
filter: [],
1213
teams: {
1314
'example-team-a': ['John Doe', 'Jane Doe'],
1415
'example-team-b': ['Max Muster', 'Susi Sorglos'],

apps/backend/src/infrastructure/git.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ export function getCommitCount(): string {
5151
export function getGitLog(limits = noLimits): Promise<string> {
5252
return new Promise<string>((resolve, reject) => {
5353
try {
54-
const args = ['log', '--numstat', '--pretty=format:"%an <%ae>,%ad"'];
54+
const args = [
55+
'log',
56+
'--numstat',
57+
'--pretty=format:"%an <%ae>,%ad%x09%H,%s"',
58+
];
5559

5660
if (limits.limitCommits) {
5761
args.push('-n ' + limits.limitCommits);

apps/backend/src/model/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export type Config = {
22
scopes: string[];
33
groups: string[];
4+
filter: string[];
45
teams: Record<string, string[]>;
56
entries: [];
67
};
@@ -10,4 +11,5 @@ export const emptyConfig: Config = {
1011
groups: [],
1112
teams: {},
1213
entries: [],
14+
filter: [],
1315
};

apps/backend/src/services/change-coupling.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { loadConfig } from '../infrastructure/config';
22
import { Limits } from '../model/limits';
33
import { Options } from '../options/options';
4-
import { parseGitLog } from '../utils/git-parser';
4+
import { parseGitLog, ParseOptions } from '../utils/git-parser';
55
import { getEmptyMatrix } from '../utils/matrix';
66
import { normalizeFolder } from '../utils/normalize-folder';
77

@@ -27,6 +27,11 @@ export async function calcChangeCoupling(
2727

2828
const commitsPerModule: number[] = new Array(matrix.length).fill(0);
2929

30+
const parseOptions: ParseOptions = {
31+
limits,
32+
filter: config.filter,
33+
};
34+
3035
await parseGitLog((entry) => {
3136
const touchedModules = new Set<number>();
3237
for (const change of entry.body) {
@@ -39,7 +44,7 @@ export async function calcChangeCoupling(
3944
}
4045
}
4146
addToMatrix(touchedModules, matrix);
42-
}, limits);
47+
}, parseOptions);
4348

4449
return {
4550
matrix,

apps/backend/src/services/hotspot.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import path from 'path';
22
import * as fs from 'fs';
33

44
import { Options } from '../options/options';
5-
import { LogBodyEntry, parseGitLog } from '../utils/git-parser';
5+
import { LogBodyEntry, parseGitLog, ParseOptions } from '../utils/git-parser';
66
import { loadConfig } from '../infrastructure/config';
77
import { normalizeFolder, toDisplayFolder } from '../utils/normalize-folder';
88
import { Limits } from '../model/limits';
99

1010
import { calcCyclomaticComplexity } from '../utils/complexity';
1111
import { countLinesInFile } from '../utils/count-lines';
12+
import { Config } from '../model/config';
1213

1314
export type ComplexityMetric = 'McCabe' | 'Length';
1415

@@ -51,10 +52,13 @@ export async function findHotspotFiles(
5152
limits: Limits,
5253
options: Options
5354
): Promise<HotspotResult> {
55+
const config = loadConfig(options);
56+
5457
const hotspots: Record<string, Hotspot> = await analyzeLogs(
5558
criteria,
5659
limits,
57-
options
60+
options,
61+
config.filter
5862
);
5963

6064
const filtered: FlatHotspot[] = [];
@@ -79,8 +83,8 @@ export async function aggregateHotspots(
7983
): Promise<AggregatedHotspotsResult> {
8084
const hotspotResult = await findHotspotFiles(criteria, limits, options);
8185
const hotspots = hotspotResult.hotspots;
82-
const config = loadConfig(options);
8386

87+
const config = loadConfig(options);
8488
const modules = config.scopes.map((m) => normalizeFolder(m));
8589

8690
const result: AggregatedHotspot[] = [];
@@ -104,10 +108,16 @@ export async function aggregateHotspots(
104108
async function analyzeLogs(
105109
criteria: HotspotCriteria,
106110
limits: Limits,
107-
options: Options
111+
options: Options,
112+
filter: string[]
108113
) {
109114
const hotspots: Record<string, Hotspot> = {};
110115

116+
const parseOptions: ParseOptions = {
117+
limits,
118+
filter,
119+
};
120+
111121
await parseGitLog((entry) => {
112122
for (const change of entry.body) {
113123
let hotspot: Hotspot;
@@ -134,7 +144,7 @@ async function analyzeLogs(
134144
hotspot.commits++;
135145
hotspot.changedLines += change.linesAdded + change.linesRemoved;
136146
}
137-
}, limits);
147+
}, parseOptions);
138148
return hotspots;
139149
}
140150

apps/backend/src/services/team-alignment.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { loadConfig } from '../infrastructure/config';
22
import { Limits } from '../model/limits';
33
import { Options } from '../options/options';
4-
import { parseGitLog } from '../utils/git-parser';
4+
import { parseGitLog, ParseOptions } from '../utils/git-parser';
55
import { normalizeFolder } from '../utils/normalize-folder';
66

77
const UNKNOWN_TEAM = 'unknown';
@@ -30,6 +30,11 @@ export async function calcTeamAlignment(
3030

3131
const users = new Set<string>();
3232

33+
const parseOptions: ParseOptions = {
34+
limits,
35+
filter: config.filter,
36+
};
37+
3338
let count = 0;
3439
await parseGitLog((entry) => {
3540
let userName = entry.header.userName;
@@ -63,7 +68,7 @@ export async function calcTeamAlignment(
6368
}
6469
}
6570
}
66-
}, limits);
71+
}, parseOptions);
6772

6873
if (byUser) {
6974
result.teams = Array.from(users);

apps/backend/src/utils/git-parser.spec.ts

Lines changed: 144 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { emptyConfig } from '../model/config';
2-
import { Limits } from '../model/limits';
3-
import { LogEntry, parseGitLog } from './git-parser';
2+
import { LogEntry, parseGitLog, ParseOptions } from './git-parser';
43
import { getToday, subtractMonths, subtractSeconds } from '../utils/date-utils';
54
import { loadCachedLog } from '../infrastructure/log';
65

@@ -16,7 +15,7 @@ jest.mock('../infrastructure/config', () => ({
1615
})),
1716
}));
1817

19-
const logWithoutRenames = `"John Doe <john.doe@acme.com>,${today.toISOString()}"
18+
const logWithoutRenames = `"John Doe <john.doe@acme.com>,${today.toISOString()}\t123456,ci: format with prettier"
2019
1\t0\t/booking/feature-manage/my.component.ts
2120
1\t0\t/booking/feature-manage/my-other.component.ts
2221
0\t1\t/checkin/feature-checkin/my.component.ts
@@ -65,16 +64,19 @@ describe('git parser', () => {
6564
});
6665

6766
it('returns all log entries', async () => {
68-
const limits: Limits = {
69-
limitCommits: 0,
70-
limitMonths: 0,
67+
const parseOptions: ParseOptions = {
68+
limits: {
69+
limitCommits: 0,
70+
limitMonths: 0,
71+
},
72+
filter: [],
7173
};
7274

7375
const entries: LogEntry[] = [];
7476

7577
parseGitLog((entry) => {
7678
entries.push(entry);
77-
}, limits);
79+
}, parseOptions);
7880

7981
expect(entries).toEqual([
8082
{
@@ -167,16 +169,19 @@ describe('git parser', () => {
167169
});
168170

169171
it('returns last 2 log entries', async () => {
170-
const limits: Limits = {
171-
limitCommits: 2,
172-
limitMonths: 0,
172+
const parseOptions: ParseOptions = {
173+
limits: {
174+
limitCommits: 2,
175+
limitMonths: 0,
176+
},
177+
filter: null,
173178
};
174179

175180
const entries: LogEntry[] = [];
176181

177182
parseGitLog((entry) => {
178183
entries.push(entry);
179-
}, limits);
184+
}, parseOptions);
180185

181186
expect(entries).toEqual([
182187
{
@@ -230,17 +235,134 @@ describe('git parser', () => {
230235
]);
231236
});
232237

238+
it('returns last 2 log entries after filtering', async () => {
239+
const parseOptions: ParseOptions = {
240+
limits: {
241+
limitCommits: 2,
242+
limitMonths: 0,
243+
},
244+
filter: ['format with prettier'],
245+
};
246+
247+
const entries: LogEntry[] = [];
248+
249+
parseGitLog((entry) => {
250+
entries.push(entry);
251+
}, parseOptions);
252+
253+
expect(entries).toEqual([
254+
{
255+
header: {
256+
userName: 'Jane Doe',
257+
email: 'jane.doe@acme.com',
258+
date: today,
259+
},
260+
body: [
261+
{
262+
linesAdded: 10,
263+
linesRemoved: 0,
264+
path: '/booking/feature-manage/my.component.ts',
265+
},
266+
{
267+
linesAdded: 0,
268+
linesRemoved: 1,
269+
path: '/checkin/feature-checkin/my.component.ts',
270+
},
271+
],
272+
},
273+
{
274+
header: {
275+
userName: 'John Doe',
276+
email: 'john.doe@acme.com',
277+
date: past,
278+
},
279+
body: [
280+
{
281+
linesAdded: 10,
282+
linesRemoved: 0,
283+
path: '/booking/feature-manage/my.component.ts',
284+
},
285+
{
286+
linesAdded: 0,
287+
linesRemoved: 1,
288+
path: '/shared/feature-checkin/my.component.ts',
289+
},
290+
],
291+
},
292+
]);
293+
});
294+
295+
it('returns last 2 log entries after filtering with multiple filters', async () => {
296+
const parseOptions: ParseOptions = {
297+
limits: {
298+
limitCommits: 2,
299+
limitMonths: 0,
300+
},
301+
filter: ['does not match', 'format with prettier'],
302+
};
303+
304+
const entries: LogEntry[] = [];
305+
306+
parseGitLog((entry) => {
307+
entries.push(entry);
308+
}, parseOptions);
309+
310+
expect(entries).toEqual([
311+
{
312+
header: {
313+
userName: 'Jane Doe',
314+
email: 'jane.doe@acme.com',
315+
date: today,
316+
},
317+
body: [
318+
{
319+
linesAdded: 10,
320+
linesRemoved: 0,
321+
path: '/booking/feature-manage/my.component.ts',
322+
},
323+
{
324+
linesAdded: 0,
325+
linesRemoved: 1,
326+
path: '/checkin/feature-checkin/my.component.ts',
327+
},
328+
],
329+
},
330+
{
331+
header: {
332+
userName: 'John Doe',
333+
email: 'john.doe@acme.com',
334+
date: past,
335+
},
336+
body: [
337+
{
338+
linesAdded: 10,
339+
linesRemoved: 0,
340+
path: '/booking/feature-manage/my.component.ts',
341+
},
342+
{
343+
linesAdded: 0,
344+
linesRemoved: 1,
345+
path: '/shared/feature-checkin/my.component.ts',
346+
},
347+
],
348+
},
349+
]);
350+
});
351+
233352
it("returns last month's log entries", async () => {
234-
const limits: Limits = {
235-
limitCommits: 0,
236-
limitMonths: 1,
353+
const parseOptions: ParseOptions = {
354+
limits: {
355+
limitCommits: 0,
356+
limitMonths: 1,
357+
},
358+
filter: [],
237359
};
238360

239361
const entries: LogEntry[] = [];
240362

241363
parseGitLog((entry) => {
242364
entries.push(entry);
243-
}, limits);
365+
}, parseOptions);
244366

245367
expect(entries).toEqual([
246368
{
@@ -318,16 +440,19 @@ describe('git parser', () => {
318440
mocks.loadCachedLog.mockImplementation(() => logWithRenames);
319441
});
320442
it('returns all log entries', async () => {
321-
const limits: Limits = {
322-
limitCommits: 0,
323-
limitMonths: 0,
443+
const parseOptions: ParseOptions = {
444+
limits: {
445+
limitCommits: 0,
446+
limitMonths: 0,
447+
},
448+
filter: [],
324449
};
325450

326451
const entries: LogEntry[] = [];
327452

328453
parseGitLog((entry) => {
329454
entries.push(entry);
330-
}, limits);
455+
}, parseOptions);
331456

332457
expect(entries).toEqual([
333458
{

0 commit comments

Comments
 (0)