@mcarvin/smart-diff 1.0.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.cjs +307 -219
- package/dist/index.cjs.map +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.cjs.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.min.umd.js +1 -1
- package/dist/index.min.umd.js.map +1 -1
- package/dist/index.mjs +307 -219
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +307 -219
- package/dist/index.umd.js.map +1 -1
- package/dist/typings/ai/aiConstants.d.ts +3 -0
- package/dist/typings/ai/aiSummary.d.ts +2 -17
- package/dist/typings/ai/aiTypes.d.ts +21 -0
- package/dist/typings/git/commitMessageFilter.d.ts +2 -0
- package/dist/typings/git/diffGitStatus.d.ts +3 -0
- package/dist/typings/git/diffNameStatusParse.d.ts +8 -0
- package/dist/typings/git/diffNumstatParse.d.ts +4 -0
- package/dist/typings/git/diffPathspecs.d.ts +2 -0
- package/dist/typings/git/diffSummaryBuild.d.ts +2 -0
- package/dist/typings/git/diffSummaryParse.d.ts +2 -0
- package/dist/typings/git/diffTypes.d.ts +31 -0
- package/dist/typings/git/gitDiff.d.ts +5 -33
- package/dist/typings/git/gitDiffOps.d.ts +8 -0
- package/dist/typings/index.d.ts +9 -8
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -35,7 +35,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
35
35
|
|
|
36
36
|
function resolveLlmBaseUrl() {
|
|
37
37
|
var _a, _b, _c;
|
|
38
|
-
return (_b = (_a = process.env.LLM_BASE_URL) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : (_c = process.env.OPENAI_BASE_URL) === null || _c === void 0 ? void 0 : _c.trim();
|
|
38
|
+
return ((_b = (_a = process.env.LLM_BASE_URL) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : (_c = process.env.OPENAI_BASE_URL) === null || _c === void 0 ? void 0 : _c.trim());
|
|
39
39
|
}
|
|
40
40
|
function parseHeaderJsonObject(raw) {
|
|
41
41
|
const trimmed = raw === null || raw === void 0 ? void 0 : raw.trim();
|
|
@@ -43,12 +43,14 @@ function parseHeaderJsonObject(raw) {
|
|
|
43
43
|
return {};
|
|
44
44
|
try {
|
|
45
45
|
const parsed = JSON.parse(trimmed);
|
|
46
|
-
if (typeof parsed !==
|
|
46
|
+
if (typeof parsed !== "object" ||
|
|
47
|
+
parsed === null ||
|
|
48
|
+
Array.isArray(parsed)) {
|
|
47
49
|
return {};
|
|
48
50
|
}
|
|
49
51
|
const out = {};
|
|
50
52
|
for (const [key, value] of Object.entries(parsed)) {
|
|
51
|
-
if (typeof value ===
|
|
53
|
+
if (typeof value === "string" && value.length > 0) {
|
|
52
54
|
out[key] = value;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -65,7 +67,7 @@ function parseLlmDefaultHeadersFromEnv() {
|
|
|
65
67
|
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
66
68
|
}
|
|
67
69
|
function findAuthorizationHeaderName(headers) {
|
|
68
|
-
return Object.keys(headers).find((k) => k.toLowerCase() ===
|
|
70
|
+
return Object.keys(headers).find((k) => k.toLowerCase() === "authorization");
|
|
69
71
|
}
|
|
70
72
|
function stripBearerPrefix(value) {
|
|
71
73
|
var _a;
|
|
@@ -108,7 +110,7 @@ function resolveOpenAiLikeClientInit() {
|
|
|
108
110
|
var _a, _b, _c, _d, _e;
|
|
109
111
|
const baseURL = resolveLlmBaseUrl();
|
|
110
112
|
const mergedHeaders = (_a = parseLlmDefaultHeadersFromEnv()) !== null && _a !== void 0 ? _a : {};
|
|
111
|
-
const envApiKey = (_e = (_c = (_b = process.env.LLM_API_KEY) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : (_d = process.env.OPENAI_API_KEY) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e :
|
|
113
|
+
const envApiKey = (_e = (_c = (_b = process.env.LLM_API_KEY) === null || _b === void 0 ? void 0 : _b.trim()) !== null && _c !== void 0 ? _c : (_d = process.env.OPENAI_API_KEY) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e : "";
|
|
112
114
|
let defaultHeaders;
|
|
113
115
|
let apiKey = envApiKey;
|
|
114
116
|
if (apiKey.length === 0) {
|
|
@@ -116,12 +118,16 @@ function resolveOpenAiLikeClientInit() {
|
|
|
116
118
|
if (split.apiKeyFromAuthHeader) {
|
|
117
119
|
apiKey = split.apiKeyFromAuthHeader;
|
|
118
120
|
}
|
|
119
|
-
defaultHeaders =
|
|
121
|
+
defaultHeaders =
|
|
122
|
+
Object.keys(split.defaultHeaders).length > 0
|
|
123
|
+
? split.defaultHeaders
|
|
124
|
+
: undefined;
|
|
120
125
|
}
|
|
121
126
|
else {
|
|
122
|
-
defaultHeaders =
|
|
127
|
+
defaultHeaders =
|
|
128
|
+
Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined;
|
|
123
129
|
}
|
|
124
|
-
return Object.assign(Object.assign({ apiKey: apiKey.length > 0 ? apiKey :
|
|
130
|
+
return Object.assign(Object.assign({ apiKey: apiKey.length > 0 ? apiKey : "unused" }, (baseURL ? { baseURL } : {})), (defaultHeaders ? { defaultHeaders } : {}));
|
|
125
131
|
}
|
|
126
132
|
function createOpenAiLikeClient() {
|
|
127
133
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -131,9 +137,22 @@ function createOpenAiLikeClient() {
|
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
const DEFAULT_LLM_MAX_DIFF_CHARS = 120000;
|
|
140
|
+
const DEFAULT_GIT_DIFF_SYSTEM_PROMPT = `You are a senior software engineer helping developers understand code and configuration changes from the git context they supplied.
|
|
141
|
+
You receive: commit subject lines (when available), changed file paths, and unified git patch(es)—either one range diff or concatenated per-commit patches, depending on how the diff was produced. Patches may be truncated mid-section with an explicit marker—do not infer changes beyond visible lines.
|
|
142
|
+
Explain what changed in terms of behavior, APIs, data, configuration, security, and operational risk. Tie claims to the patch when possible.
|
|
143
|
+
Produce a concise, developer-focused summary in Markdown.
|
|
144
|
+
Use sections that fit the change (for example: Highlights, Breaking or risky changes, API / contract changes, Data & schema, Configuration & infra, Security & auth, Tests & quality). Omit empty sections.
|
|
145
|
+
Group related changes; do not list every individual file. When multiple commits appear in the context, briefly separate notable themes by commit when helpful.
|
|
146
|
+
If the user message includes a Team line, use that exact team name in the summary title (for example: "## <Team> – Change summary" or similar).`;
|
|
147
|
+
const LLM_GATEWAY_REQUIRED_MESSAGE = "No LLM gateway configured. Set OPENAI_API_KEY or LLM_API_KEY, and/or LLM_BASE_URL or OPENAI_BASE_URL, " +
|
|
148
|
+
"and/or JSON in OPENAI_DEFAULT_HEADERS or LLM_DEFAULT_HEADERS. " +
|
|
149
|
+
"Alternatively pass openAiClientProvider to generateSummary or summarizeGitDiff.";
|
|
150
|
+
|
|
134
151
|
function resolveLlmMaxDiffChars(cliOverride) {
|
|
135
152
|
var _a;
|
|
136
|
-
if (cliOverride !== undefined &&
|
|
153
|
+
if (cliOverride !== undefined &&
|
|
154
|
+
Number.isFinite(cliOverride) &&
|
|
155
|
+
cliOverride > 0) {
|
|
137
156
|
return Math.trunc(cliOverride);
|
|
138
157
|
}
|
|
139
158
|
const raw = (_a = process.env.LLM_MAX_DIFF_CHARS) === null || _a === void 0 ? void 0 : _a.trim();
|
|
@@ -152,70 +171,75 @@ function truncateUnifiedDiffForLlm(diffText, maxChars) {
|
|
|
152
171
|
const marker = `\n\n--- TRUNCATED: unified diff was ${diffText.length} characters; only the first ${maxChars} were sent. Narrow the ref range, adjust commit/path filters, or raise maxDiffChars / LLM_MAX_DIFF_CHARS only if your model context allows. ---\n`;
|
|
153
172
|
return diffText.slice(0, maxChars) + marker;
|
|
154
173
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
Use sections that fit the change (for example: Highlights, Breaking or risky changes, API / contract changes, Data & schema, Configuration & infra, Security & auth, Tests & quality). Omit empty sections.
|
|
160
|
-
Group related changes; do not list every individual file. When multiple commits appear in the context, briefly separate notable themes by commit when helpful.
|
|
161
|
-
If the user message includes a Team line, use that exact team name in the summary title (for example: "## <Team> – Change summary" or similar).`;
|
|
162
|
-
const LLM_GATEWAY_REQUIRED_MESSAGE = 'No LLM gateway configured. Set OPENAI_API_KEY or LLM_API_KEY, and/or LLM_BASE_URL or OPENAI_BASE_URL, ' +
|
|
163
|
-
'and/or JSON in OPENAI_DEFAULT_HEADERS or LLM_DEFAULT_HEADERS. ' +
|
|
164
|
-
'Alternatively pass openAiClientProvider to generateSummary or summarizeGitDiff.';
|
|
165
|
-
function generateSummary(diffText, fileNames, commits, flags, openAiClientProvider, diffSummary) {
|
|
174
|
+
function markdownDiffTruncationNotice(originalChars, maxChars) {
|
|
175
|
+
return `> **Truncated diff:** The unified diff was ${originalChars} characters; only the first ${maxChars} were sent to the model. The summary may not reflect the full change set. Narrow the ref range, adjust path filters, or raise \`maxDiffChars\` / \`LLM_MAX_DIFF_CHARS\`—often together with switching to a model whose context window can fit a larger prompt.\n\n`;
|
|
176
|
+
}
|
|
177
|
+
function generateSummary(input) {
|
|
166
178
|
return __awaiter(this, void 0, void 0, function* () {
|
|
167
179
|
var _a, _b;
|
|
180
|
+
const { diffText, fileNames, commits, flags, openAiClientProvider, diffSummary, } = input;
|
|
168
181
|
if (!shouldUseLlmGateway() && openAiClientProvider === undefined) {
|
|
169
182
|
throw new Error(LLM_GATEWAY_REQUIRED_MESSAGE);
|
|
170
183
|
}
|
|
171
184
|
const maxDiffChars = resolveLlmMaxDiffChars(flags.maxDiffChars);
|
|
185
|
+
const diffTruncated = diffText.length > maxDiffChars;
|
|
172
186
|
const diffForLlm = truncateUnifiedDiffForLlm(diffText, maxDiffChars);
|
|
173
187
|
const userContent = buildOpenAiUserContent(flags, commits, fileNames, diffForLlm, diffSummary);
|
|
174
|
-
|
|
188
|
+
const summary = yield callOpenAi(userContent, (_a = flags.model) !== null && _a !== void 0 ? _a : "gpt-4o-mini", (_b = flags.systemPrompt) !== null && _b !== void 0 ? _b : DEFAULT_GIT_DIFF_SYSTEM_PROMPT, openAiClientProvider !== null && openAiClientProvider !== void 0 ? openAiClientProvider : (() => __awaiter(this, void 0, void 0, function* () { return createOpenAiLikeClient(); })));
|
|
189
|
+
if (!diffTruncated) {
|
|
190
|
+
return summary;
|
|
191
|
+
}
|
|
192
|
+
return markdownDiffTruncationNotice(diffText.length, maxDiffChars) + summary;
|
|
175
193
|
});
|
|
176
194
|
}
|
|
177
195
|
function formatRegexFilterLines(flags) {
|
|
178
196
|
var _a, _b;
|
|
179
|
-
const includes = ((_a = flags.commitMessageIncludeRegexes) !== null && _a !== void 0 ? _a : [])
|
|
180
|
-
|
|
197
|
+
const includes = ((_a = flags.commitMessageIncludeRegexes) !== null && _a !== void 0 ? _a : [])
|
|
198
|
+
.map((s) => s.trim())
|
|
199
|
+
.filter(Boolean);
|
|
200
|
+
const excludes = ((_b = flags.commitMessageExcludeRegexes) !== null && _b !== void 0 ? _b : [])
|
|
201
|
+
.map((s) => s.trim())
|
|
202
|
+
.filter(Boolean);
|
|
181
203
|
const incLine = includes.length > 0
|
|
182
|
-
? `Commit message include regexes (OR): ${includes.map((r) => JSON.stringify(r)).join(
|
|
183
|
-
:
|
|
204
|
+
? `Commit message include regexes (OR): ${includes.map((r) => JSON.stringify(r)).join(", ")}\n`
|
|
205
|
+
: "";
|
|
184
206
|
const excLine = excludes.length > 0
|
|
185
|
-
? `Commit message exclude regexes: ${excludes.map((r) => JSON.stringify(r)).join(
|
|
186
|
-
:
|
|
207
|
+
? `Commit message exclude regexes: ${excludes.map((r) => JSON.stringify(r)).join(", ")}\n`
|
|
208
|
+
: "";
|
|
187
209
|
if (!incLine && !excLine) {
|
|
188
|
-
return
|
|
210
|
+
return "Commit message filters: none.\nGit context shape: single unified diff for the full ref range.\n";
|
|
189
211
|
}
|
|
190
212
|
return (`${incLine}${excLine}` +
|
|
191
|
-
|
|
213
|
+
"Git context shape: concatenated per-commit unified patches for commits that pass the message filters.\n");
|
|
192
214
|
}
|
|
193
215
|
function buildOpenAiUserContent(flags, commits, fileNames, diffText, diffSummary) {
|
|
194
216
|
var _a, _b;
|
|
195
217
|
const from = flags.from;
|
|
196
|
-
const to = (_a = flags.to) !== null && _a !== void 0 ? _a :
|
|
218
|
+
const to = (_a = flags.to) !== null && _a !== void 0 ? _a : "HEAD";
|
|
197
219
|
const team = (_b = flags.team) === null || _b === void 0 ? void 0 : _b.trim();
|
|
198
220
|
const ts = new Date().toISOString();
|
|
199
|
-
const teamLine = team ? `Team: ${team}\n` :
|
|
221
|
+
const teamLine = team ? `Team: ${team}\n` : "";
|
|
200
222
|
const filterBlock = formatRegexFilterLines(flags);
|
|
201
223
|
const commitBlock = commits.length > 0
|
|
202
|
-
? commits
|
|
203
|
-
|
|
204
|
-
|
|
224
|
+
? commits
|
|
225
|
+
.map((c) => `- ${c.hash.slice(0, 7)} ${c.message.replace(/\r?\n/g, " ")}`)
|
|
226
|
+
.join("\n")
|
|
227
|
+
: "- (no commits in range after filtering)";
|
|
228
|
+
const pathsBlock = fileNames.length > 0 ? fileNames.join("\n") : "(no paths in diff scope)";
|
|
205
229
|
const structuredDiffSection = diffSummary
|
|
206
230
|
? `=== Structured git context (JSON summary) ===\n${JSON.stringify(diffSummary, null, 2)}\n\n`
|
|
207
|
-
:
|
|
231
|
+
: "";
|
|
208
232
|
return (`${teamLine}` +
|
|
209
233
|
`Date: ${ts}\n\n` +
|
|
210
234
|
`Git refs: ${from}..${to}\n` +
|
|
211
235
|
filterBlock +
|
|
212
|
-
|
|
213
|
-
|
|
236
|
+
"\n" +
|
|
237
|
+
"=== Included commits (subject lines) ===\n" +
|
|
214
238
|
`${commitBlock}\n\n` +
|
|
215
|
-
|
|
239
|
+
"=== Changed paths ===\n" +
|
|
216
240
|
`${pathsBlock}\n\n` +
|
|
217
241
|
structuredDiffSection +
|
|
218
|
-
|
|
242
|
+
"=== Git context (unified diff(s); patches may be truncated with an explicit marker) ===\n" +
|
|
219
243
|
diffText);
|
|
220
244
|
}
|
|
221
245
|
function callOpenAi(userContent, model, systemPrompt, openAiClientProvider) {
|
|
@@ -229,11 +253,11 @@ function callOpenAi(userContent, model, systemPrompt, openAiClientProvider) {
|
|
|
229
253
|
model,
|
|
230
254
|
messages: [
|
|
231
255
|
{
|
|
232
|
-
role:
|
|
256
|
+
role: "system",
|
|
233
257
|
content: systemPrompt,
|
|
234
258
|
},
|
|
235
259
|
{
|
|
236
|
-
role:
|
|
260
|
+
role: "user",
|
|
237
261
|
content: userContent,
|
|
238
262
|
},
|
|
239
263
|
],
|
|
@@ -241,64 +265,25 @@ function callOpenAi(userContent, model, systemPrompt, openAiClientProvider) {
|
|
|
241
265
|
max_tokens: maxTokens,
|
|
242
266
|
});
|
|
243
267
|
const typedResponse = response;
|
|
244
|
-
const text = (_f = (_e = (_d = (_c = (_b = typedResponse.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f :
|
|
245
|
-
return text.length > 0 ? text :
|
|
268
|
+
const text = (_f = (_e = (_d = (_c = (_b = typedResponse.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content) === null || _e === void 0 ? void 0 : _e.trim()) !== null && _f !== void 0 ? _f : "";
|
|
269
|
+
return text.length > 0 ? text : "No summary generated by OpenAI.";
|
|
246
270
|
});
|
|
247
271
|
}
|
|
248
272
|
|
|
249
|
-
function createGitClient(cwd = process.cwd()) {
|
|
250
|
-
return simpleGit(cwd);
|
|
251
|
-
}
|
|
252
|
-
function getCommits(git, from, to) {
|
|
253
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
254
|
-
const logResult = yield git.log({ from, to });
|
|
255
|
-
return logResult.all;
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
function compileRegex(pattern, label) {
|
|
259
|
-
try {
|
|
260
|
-
return new RegExp(pattern, 'i');
|
|
261
|
-
}
|
|
262
|
-
catch (_a) {
|
|
263
|
-
throw new Error(`Invalid ${label} regular expression: ${JSON.stringify(pattern)}`);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
function commitMessagePassesFilters(message, includeRes, excludeRes) {
|
|
267
|
-
for (const ex of excludeRes) {
|
|
268
|
-
if (ex.test(message))
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
if (includeRes.length > 0 && !includeRes.some((r) => r.test(message)))
|
|
272
|
-
return false;
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
function filterCommitsByMessageRegexes(commits, includePatterns, excludePatterns) {
|
|
276
|
-
const includes = (includePatterns !== null && includePatterns !== void 0 ? includePatterns : []).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
277
|
-
const excludes = (excludePatterns !== null && excludePatterns !== void 0 ? excludePatterns : []).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
278
|
-
const includeRes = includes.map((p, i) => compileRegex(p, `commit message include pattern[${i}]`));
|
|
279
|
-
const excludeRes = excludes.map((p, i) => compileRegex(p, `commit message exclude pattern[${i}]`));
|
|
280
|
-
return commits.filter((c) => commitMessagePassesFilters(c.message, includeRes, excludeRes));
|
|
281
|
-
}
|
|
282
|
-
function getRepoRoot(git) {
|
|
283
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
284
|
-
const root = yield git.revparse(['--show-toplevel']);
|
|
285
|
-
return root.trim();
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
273
|
function normalizeRepoRelativePath(p) {
|
|
289
|
-
const trimmed = p.trim().replace(/\\/g,
|
|
290
|
-
const noLeading = trimmed.replace(/^\/+/,
|
|
291
|
-
const noTrailingSlash = noLeading.replace(/\/+$/,
|
|
292
|
-
return noTrailingSlash.length > 0 ? noTrailingSlash :
|
|
274
|
+
const trimmed = p.trim().replace(/\\/g, "/");
|
|
275
|
+
const noLeading = trimmed.replace(/^\/+/, "");
|
|
276
|
+
const noTrailingSlash = noLeading.replace(/\/+$/, "");
|
|
277
|
+
return noTrailingSlash.length > 0 ? noTrailingSlash : ".";
|
|
293
278
|
}
|
|
294
279
|
function assertPathUnderRepo(repoRoot, userPath) {
|
|
295
280
|
const abs = resolve(repoRoot, userPath);
|
|
296
281
|
const rel = relative(repoRoot, abs);
|
|
297
|
-
if (rel ===
|
|
282
|
+
if (rel === "..") {
|
|
298
283
|
throw new Error(`Path escapes repository root: ${JSON.stringify(userPath)}`);
|
|
299
284
|
}
|
|
300
285
|
const segments = rel.split(/[/\\]/);
|
|
301
|
-
if (segments.includes(
|
|
286
|
+
if (segments.includes("..")) {
|
|
302
287
|
throw new Error(`Path escapes repository root: ${JSON.stringify(userPath)}`);
|
|
303
288
|
}
|
|
304
289
|
}
|
|
@@ -306,9 +291,13 @@ function buildDiffPathspecs(repoRoot, pathFilter) {
|
|
|
306
291
|
var _a, _b, _c, _d;
|
|
307
292
|
const includeRaw = (_b = (_a = pathFilter === null || pathFilter === void 0 ? void 0 : pathFilter.includeFolders) === null || _a === void 0 ? void 0 : _a.filter((p) => p.trim().length > 0)) !== null && _b !== void 0 ? _b : [];
|
|
308
293
|
const excludeRaw = (_d = (_c = pathFilter === null || pathFilter === void 0 ? void 0 : pathFilter.excludeFolders) === null || _c === void 0 ? void 0 : _c.filter((p) => p.trim().length > 0)) !== null && _d !== void 0 ? _d : [];
|
|
309
|
-
const includes = includeRaw
|
|
310
|
-
|
|
311
|
-
|
|
294
|
+
const includes = includeRaw
|
|
295
|
+
.map(normalizeRepoRelativePath)
|
|
296
|
+
.filter((p) => p !== "." && p !== "");
|
|
297
|
+
const excludes = excludeRaw
|
|
298
|
+
.map(normalizeRepoRelativePath)
|
|
299
|
+
.filter((p) => p !== "." && p !== "");
|
|
300
|
+
const toValidate = includes.length > 0 ? includes : ["."];
|
|
312
301
|
for (const inc of toValidate) {
|
|
313
302
|
assertPathUnderRepo(repoRoot, inc);
|
|
314
303
|
}
|
|
@@ -317,7 +306,7 @@ function buildDiffPathspecs(repoRoot, pathFilter) {
|
|
|
317
306
|
}
|
|
318
307
|
const specs = [];
|
|
319
308
|
if (includes.length === 0) {
|
|
320
|
-
specs.push(
|
|
309
|
+
specs.push(".");
|
|
321
310
|
}
|
|
322
311
|
else {
|
|
323
312
|
for (const inc of includes) {
|
|
@@ -329,100 +318,73 @@ function buildDiffPathspecs(repoRoot, pathFilter) {
|
|
|
329
318
|
}
|
|
330
319
|
return specs;
|
|
331
320
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
341
|
-
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
342
|
-
if (!filterByCommits) {
|
|
343
|
-
return git.diff([`${from}..${to}`, '--', ...specs]);
|
|
344
|
-
}
|
|
345
|
-
const patches = yield Promise.all(commits.map((c) => git.diff([`${c.hash}^!`, '--', ...specs])));
|
|
346
|
-
return patches.filter(Boolean).join('\n');
|
|
347
|
-
});
|
|
321
|
+
|
|
322
|
+
function compileRegex(pattern, label) {
|
|
323
|
+
try {
|
|
324
|
+
return new RegExp(pattern, "i");
|
|
325
|
+
}
|
|
326
|
+
catch (_a) {
|
|
327
|
+
throw new Error(`Invalid ${label} regular expression: ${JSON.stringify(pattern)}`);
|
|
328
|
+
}
|
|
348
329
|
}
|
|
349
|
-
function
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return buildDiffSummaryFromGitOutputs(nameOutput, numOutput);
|
|
358
|
-
}
|
|
359
|
-
const pairs = yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
|
|
360
|
-
const range = `${c.hash}^!`;
|
|
361
|
-
const [numOutput, nameOutput] = yield Promise.all([
|
|
362
|
-
git.diff(['--numstat', range, '--', ...specs]),
|
|
363
|
-
git.diff(['--name-status', range, '--', ...specs]),
|
|
364
|
-
]);
|
|
365
|
-
return { numOutput, nameOutput };
|
|
366
|
-
})));
|
|
367
|
-
const nameJoined = pairs
|
|
368
|
-
.map((p) => p.nameOutput)
|
|
369
|
-
.filter(Boolean)
|
|
370
|
-
.join('\n');
|
|
371
|
-
const numJoined = pairs
|
|
372
|
-
.map((p) => p.numOutput)
|
|
373
|
-
.filter(Boolean)
|
|
374
|
-
.join('\n');
|
|
375
|
-
return buildDiffSummaryFromGitOutputs(nameJoined, numJoined);
|
|
376
|
-
});
|
|
330
|
+
function commitMessagePassesFilters(message, includeRes, excludeRes) {
|
|
331
|
+
for (const ex of excludeRes) {
|
|
332
|
+
if (ex.test(message))
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
if (includeRes.length > 0 && !includeRes.some((r) => r.test(message)))
|
|
336
|
+
return false;
|
|
337
|
+
return true;
|
|
377
338
|
}
|
|
378
|
-
function
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const fileSet = new Set();
|
|
389
|
-
yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
|
|
390
|
-
const output = yield git.show(['--name-only', '--pretty=format:', c.hash, '--', ...specs]);
|
|
391
|
-
output
|
|
392
|
-
.split(/\r?\n/)
|
|
393
|
-
.map((f) => f.trim())
|
|
394
|
-
.filter(Boolean)
|
|
395
|
-
.forEach((f) => fileSet.add(f));
|
|
396
|
-
})));
|
|
397
|
-
return Array.from(fileSet);
|
|
398
|
-
});
|
|
339
|
+
function filterCommitsByMessageRegexes(commits, includePatterns, excludePatterns) {
|
|
340
|
+
const includes = (includePatterns !== null && includePatterns !== void 0 ? includePatterns : [])
|
|
341
|
+
.map((p) => p.trim())
|
|
342
|
+
.filter((p) => p.length > 0);
|
|
343
|
+
const excludes = (excludePatterns !== null && excludePatterns !== void 0 ? excludePatterns : [])
|
|
344
|
+
.map((p) => p.trim())
|
|
345
|
+
.filter((p) => p.length > 0);
|
|
346
|
+
const includeRes = includes.map((p, i) => compileRegex(p, `commit message include pattern[${i}]`));
|
|
347
|
+
const excludeRes = excludes.map((p, i) => compileRegex(p, `commit message exclude pattern[${i}]`));
|
|
348
|
+
return commits.filter((c) => commitMessagePassesFilters(c.message, includeRes, excludeRes));
|
|
399
349
|
}
|
|
350
|
+
|
|
400
351
|
const GIT_STATUS_BY_FIRST_CHAR = {
|
|
401
|
-
A:
|
|
402
|
-
D:
|
|
403
|
-
R:
|
|
404
|
-
C:
|
|
405
|
-
T:
|
|
406
|
-
M:
|
|
352
|
+
A: "added",
|
|
353
|
+
D: "deleted",
|
|
354
|
+
R: "renamed",
|
|
355
|
+
C: "copied",
|
|
356
|
+
T: "type-changed",
|
|
357
|
+
M: "modified",
|
|
407
358
|
};
|
|
408
359
|
function mapGitStatus(statusCode) {
|
|
409
360
|
var _a;
|
|
410
|
-
return (_a = GIT_STATUS_BY_FIRST_CHAR[statusCode.charAt(0)]) !== null && _a !== void 0 ? _a :
|
|
361
|
+
return (_a = GIT_STATUS_BY_FIRST_CHAR[statusCode.charAt(0)]) !== null && _a !== void 0 ? _a : "unknown";
|
|
411
362
|
}
|
|
412
363
|
function mergeStatus(existing, next) {
|
|
413
364
|
if (existing === next)
|
|
414
365
|
return existing;
|
|
415
|
-
const precedence = [
|
|
416
|
-
|
|
366
|
+
const precedence = [
|
|
367
|
+
"deleted",
|
|
368
|
+
"added",
|
|
369
|
+
"renamed",
|
|
370
|
+
"copied",
|
|
371
|
+
"type-changed",
|
|
372
|
+
"modified",
|
|
373
|
+
"unknown",
|
|
374
|
+
];
|
|
375
|
+
return precedence.indexOf(existing) <= precedence.indexOf(next)
|
|
376
|
+
? existing
|
|
377
|
+
: next;
|
|
417
378
|
}
|
|
379
|
+
|
|
418
380
|
function parseNameStatusLine(line) {
|
|
419
381
|
var _a;
|
|
420
|
-
const parts = line.split(
|
|
382
|
+
const parts = line.split("\t");
|
|
421
383
|
let entry = null;
|
|
422
384
|
if (parts.length >= 2) {
|
|
423
|
-
const statusToken = (_a = parts[0]) !== null && _a !== void 0 ? _a :
|
|
385
|
+
const statusToken = (_a = parts[0]) !== null && _a !== void 0 ? _a : "";
|
|
424
386
|
const status = mapGitStatus(statusToken);
|
|
425
|
-
const isRenameOrCopy = statusToken.startsWith(
|
|
387
|
+
const isRenameOrCopy = statusToken.startsWith("R") || statusToken.startsWith("C");
|
|
426
388
|
if (isRenameOrCopy && parts.length >= 3) {
|
|
427
389
|
const oldPath = parts[1];
|
|
428
390
|
const newPath = parts[2];
|
|
@@ -468,6 +430,7 @@ function mergeNameEntriesByPath(entries) {
|
|
|
468
430
|
}
|
|
469
431
|
return byPath;
|
|
470
432
|
}
|
|
433
|
+
|
|
471
434
|
function numStatPathToLookupKey(pathField) {
|
|
472
435
|
const brace = /^(.*)\{(.+) => (.+)\}$/.exec(pathField);
|
|
473
436
|
if (!brace) {
|
|
@@ -479,14 +442,14 @@ function numStatPathToLookupKey(pathField) {
|
|
|
479
442
|
}
|
|
480
443
|
function parseNumStatLine(line) {
|
|
481
444
|
var _a, _b;
|
|
482
|
-
const parts = line.split(
|
|
445
|
+
const parts = line.split("\t");
|
|
483
446
|
if (parts.length < 3)
|
|
484
447
|
return null;
|
|
485
|
-
const addStr = (_a = parts[0]) !== null && _a !== void 0 ? _a :
|
|
486
|
-
const delStr = (_b = parts[1]) !== null && _b !== void 0 ? _b :
|
|
487
|
-
const pathField = parts.slice(2).join(
|
|
488
|
-
const additions = addStr !==
|
|
489
|
-
const deletions = delStr !==
|
|
448
|
+
const addStr = (_a = parts[0]) !== null && _a !== void 0 ? _a : "";
|
|
449
|
+
const delStr = (_b = parts[1]) !== null && _b !== void 0 ? _b : "";
|
|
450
|
+
const pathField = parts.slice(2).join("\t");
|
|
451
|
+
const additions = addStr !== "-" ? Number.parseInt(addStr, 10) || 0 : 0;
|
|
452
|
+
const deletions = delStr !== "-" ? Number.parseInt(delStr, 10) || 0 : 0;
|
|
490
453
|
const key = numStatPathToLookupKey(pathField);
|
|
491
454
|
return { key, additions, deletions };
|
|
492
455
|
}
|
|
@@ -500,56 +463,35 @@ function accumulateNumStat(numStatOutput, into) {
|
|
|
500
463
|
if (!parsed)
|
|
501
464
|
continue;
|
|
502
465
|
const prev = (_a = into.get(parsed.key)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
|
|
503
|
-
into.set(parsed.key, {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
added: 'A',
|
|
508
|
-
deleted: 'D',
|
|
509
|
-
renamed: 'R100',
|
|
510
|
-
copied: 'C100',
|
|
511
|
-
'type-changed': 'T',
|
|
512
|
-
modified: 'M',
|
|
513
|
-
unknown: 'X',
|
|
514
|
-
};
|
|
515
|
-
function diffStatusToSyntheticPrefix(status) {
|
|
516
|
-
return STATUS_TO_SYNTHETIC_PREFIX[status];
|
|
517
|
-
}
|
|
518
|
-
function buildSyntheticDiffLine(meta, counts) {
|
|
519
|
-
const prefix = diffStatusToSyntheticPrefix(meta.status);
|
|
520
|
-
if (meta.oldPath) {
|
|
521
|
-
return `${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.oldPath}\t${meta.path}`;
|
|
522
|
-
}
|
|
523
|
-
return `${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.path}`;
|
|
524
|
-
}
|
|
525
|
-
function buildDiffSummaryFromGitOutputs(nameStatusOutput, numStatOutput) {
|
|
526
|
-
var _a;
|
|
527
|
-
const numMap = new Map();
|
|
528
|
-
accumulateNumStat(numStatOutput, numMap);
|
|
529
|
-
const mergedName = mergeNameEntriesByPath(parseNameStatusLines(nameStatusOutput));
|
|
530
|
-
const syntheticLines = [];
|
|
531
|
-
for (const [path, meta] of mergedName) {
|
|
532
|
-
const counts = (_a = numMap.get(path)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
|
|
533
|
-
syntheticLines.push(buildSyntheticDiffLine(meta, counts));
|
|
466
|
+
into.set(parsed.key, {
|
|
467
|
+
additions: prev.additions + parsed.additions,
|
|
468
|
+
deletions: prev.deletions + parsed.deletions,
|
|
469
|
+
});
|
|
534
470
|
}
|
|
535
|
-
return parseDiffSummary(syntheticLines.join('\n'));
|
|
536
471
|
}
|
|
472
|
+
|
|
537
473
|
function parseTabDiffSummaryLine(line) {
|
|
538
474
|
var _a;
|
|
539
|
-
const parts = line.split(
|
|
475
|
+
const parts = line.split("\t");
|
|
540
476
|
if (parts.length < 3)
|
|
541
477
|
return null;
|
|
542
|
-
const statusToken = (_a = parts.shift()) !== null && _a !== void 0 ? _a :
|
|
478
|
+
const statusToken = (_a = parts.shift()) !== null && _a !== void 0 ? _a : "";
|
|
543
479
|
const status = mapGitStatus(statusToken);
|
|
544
480
|
const add0 = parts[0];
|
|
545
481
|
const del0 = parts[1];
|
|
546
|
-
const additions = add0 && add0 !==
|
|
547
|
-
const deletions = del0 && del0 !==
|
|
482
|
+
const additions = add0 && add0 !== "-" ? Number.parseInt(add0, 10) || 0 : 0;
|
|
483
|
+
const deletions = del0 && del0 !== "-" ? Number.parseInt(del0, 10) || 0 : 0;
|
|
548
484
|
if (parts.length === 3) {
|
|
549
485
|
return { status, additions, deletions, newPath: parts[2] };
|
|
550
486
|
}
|
|
551
487
|
if (parts.length === 4) {
|
|
552
|
-
return {
|
|
488
|
+
return {
|
|
489
|
+
status,
|
|
490
|
+
additions,
|
|
491
|
+
deletions,
|
|
492
|
+
oldPath: parts[2],
|
|
493
|
+
newPath: parts[3],
|
|
494
|
+
};
|
|
553
495
|
}
|
|
554
496
|
return null;
|
|
555
497
|
}
|
|
@@ -595,11 +537,142 @@ function parseDiffSummary(diffOutput) {
|
|
|
595
537
|
};
|
|
596
538
|
}
|
|
597
539
|
|
|
540
|
+
const STATUS_TO_SYNTHETIC_PREFIX = {
|
|
541
|
+
added: "A",
|
|
542
|
+
deleted: "D",
|
|
543
|
+
renamed: "R100",
|
|
544
|
+
copied: "C100",
|
|
545
|
+
"type-changed": "T",
|
|
546
|
+
modified: "M",
|
|
547
|
+
unknown: "X",
|
|
548
|
+
};
|
|
549
|
+
function diffStatusToSyntheticPrefix(status) {
|
|
550
|
+
return STATUS_TO_SYNTHETIC_PREFIX[status];
|
|
551
|
+
}
|
|
552
|
+
function buildSyntheticDiffLine(meta, counts) {
|
|
553
|
+
const prefix = diffStatusToSyntheticPrefix(meta.status);
|
|
554
|
+
if (meta.oldPath) {
|
|
555
|
+
return `${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.oldPath}\t${meta.path}`;
|
|
556
|
+
}
|
|
557
|
+
return `${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.path}`;
|
|
558
|
+
}
|
|
559
|
+
function buildDiffSummaryFromGitOutputs(nameStatusOutput, numStatOutput) {
|
|
560
|
+
var _a;
|
|
561
|
+
const numMap = new Map();
|
|
562
|
+
accumulateNumStat(numStatOutput, numMap);
|
|
563
|
+
const mergedName = mergeNameEntriesByPath(parseNameStatusLines(nameStatusOutput));
|
|
564
|
+
const syntheticLines = [];
|
|
565
|
+
for (const [path, meta] of mergedName) {
|
|
566
|
+
const counts = (_a = numMap.get(path)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
|
|
567
|
+
syntheticLines.push(buildSyntheticDiffLine(meta, counts));
|
|
568
|
+
}
|
|
569
|
+
return parseDiffSummary(syntheticLines.join("\n"));
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function createGitClient(cwd = process.cwd()) {
|
|
573
|
+
return simpleGit(cwd);
|
|
574
|
+
}
|
|
575
|
+
function getCommits(git, from, to) {
|
|
576
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
577
|
+
const logResult = yield git.log({ from, to });
|
|
578
|
+
return logResult.all;
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
function getRepoRoot(git) {
|
|
582
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
583
|
+
const root = yield git.revparse(["--show-toplevel"]);
|
|
584
|
+
return root.trim();
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
function getDiffPathContext(git, pathFilter, repoRootOverride) {
|
|
588
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
589
|
+
const repoRoot = repoRootOverride !== null && repoRootOverride !== void 0 ? repoRootOverride : (yield getRepoRoot(git));
|
|
590
|
+
const specs = buildDiffPathspecs(repoRoot, pathFilter);
|
|
591
|
+
return { repoRoot, specs };
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
function getDiff(git, query) {
|
|
595
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
596
|
+
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
|
|
597
|
+
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
598
|
+
if (!filterByCommits) {
|
|
599
|
+
return git.diff([`${from}..${to}`, "--", ...specs]);
|
|
600
|
+
}
|
|
601
|
+
const patches = yield Promise.all(commits.map((c) => git.diff([`${c.hash}^!`, "--", ...specs])));
|
|
602
|
+
return patches.filter(Boolean).join("\n");
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
function getDiffSummary(git, query) {
|
|
606
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
607
|
+
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
|
|
608
|
+
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
609
|
+
if (!filterByCommits) {
|
|
610
|
+
const [numOutput, nameOutput] = yield Promise.all([
|
|
611
|
+
git.diff(["--numstat", `${from}..${to}`, "--", ...specs]),
|
|
612
|
+
git.diff(["--name-status", `${from}..${to}`, "--", ...specs]),
|
|
613
|
+
]);
|
|
614
|
+
return buildDiffSummaryFromGitOutputs(nameOutput, numOutput);
|
|
615
|
+
}
|
|
616
|
+
const pairs = yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
|
|
617
|
+
const range = `${c.hash}^!`;
|
|
618
|
+
const [numOutput, nameOutput] = yield Promise.all([
|
|
619
|
+
git.diff(["--numstat", range, "--", ...specs]),
|
|
620
|
+
git.diff(["--name-status", range, "--", ...specs]),
|
|
621
|
+
]);
|
|
622
|
+
return { numOutput, nameOutput };
|
|
623
|
+
})));
|
|
624
|
+
const nameJoined = pairs
|
|
625
|
+
.map((p) => p.nameOutput)
|
|
626
|
+
.filter(Boolean)
|
|
627
|
+
.join("\n");
|
|
628
|
+
const numJoined = pairs
|
|
629
|
+
.map((p) => p.numOutput)
|
|
630
|
+
.filter(Boolean)
|
|
631
|
+
.join("\n");
|
|
632
|
+
return buildDiffSummaryFromGitOutputs(nameJoined, numJoined);
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
function getChangedFiles(git, query) {
|
|
636
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
637
|
+
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
|
|
638
|
+
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
639
|
+
if (!filterByCommits) {
|
|
640
|
+
const output = yield git.diff([
|
|
641
|
+
"--name-only",
|
|
642
|
+
`${from}..${to}`,
|
|
643
|
+
"--",
|
|
644
|
+
...specs,
|
|
645
|
+
]);
|
|
646
|
+
return output
|
|
647
|
+
.split(/\r?\n/)
|
|
648
|
+
.map((f) => f.trim())
|
|
649
|
+
.filter(Boolean);
|
|
650
|
+
}
|
|
651
|
+
const fileSet = new Set();
|
|
652
|
+
yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
|
|
653
|
+
const output = yield git.show([
|
|
654
|
+
"--name-only",
|
|
655
|
+
"--pretty=format:",
|
|
656
|
+
c.hash,
|
|
657
|
+
"--",
|
|
658
|
+
...specs,
|
|
659
|
+
]);
|
|
660
|
+
output
|
|
661
|
+
.split(/\r?\n/)
|
|
662
|
+
.map((f) => f.trim())
|
|
663
|
+
.filter(Boolean)
|
|
664
|
+
.forEach((f) => fileSet.add(f));
|
|
665
|
+
})));
|
|
666
|
+
return Array.from(fileSet);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
|
|
598
670
|
function hasNonEmptyTrimmed(arr) {
|
|
599
671
|
return (arr !== null && arr !== void 0 ? arr : []).some((s) => s.trim().length > 0);
|
|
600
672
|
}
|
|
601
673
|
function shouldFilterByCommits(allCommits, filtered, opts) {
|
|
602
|
-
if (hasNonEmptyTrimmed(opts.commitMessageIncludeRegexes) ||
|
|
674
|
+
if (hasNonEmptyTrimmed(opts.commitMessageIncludeRegexes) ||
|
|
675
|
+
hasNonEmptyTrimmed(opts.commitMessageExcludeRegexes)) {
|
|
603
676
|
return true;
|
|
604
677
|
}
|
|
605
678
|
return filtered.length !== allCommits.length;
|
|
@@ -609,8 +682,9 @@ function summarizeGitDiff(options) {
|
|
|
609
682
|
var _a, _b;
|
|
610
683
|
const git = (_a = options.git) !== null && _a !== void 0 ? _a : createGitClient(options.cwd);
|
|
611
684
|
const from = options.from;
|
|
612
|
-
const to = (_b = options.to) !== null && _b !== void 0 ? _b :
|
|
613
|
-
const pathFilter = hasNonEmptyTrimmed(options.includeFolders) ||
|
|
685
|
+
const to = (_b = options.to) !== null && _b !== void 0 ? _b : "HEAD";
|
|
686
|
+
const pathFilter = hasNonEmptyTrimmed(options.includeFolders) ||
|
|
687
|
+
hasNonEmptyTrimmed(options.excludeFolders)
|
|
614
688
|
? {
|
|
615
689
|
includeFolders: options.includeFolders,
|
|
616
690
|
excludeFolders: options.excludeFolders,
|
|
@@ -619,10 +693,17 @@ function summarizeGitDiff(options) {
|
|
|
619
693
|
const allCommits = yield getCommits(git, from, to);
|
|
620
694
|
const filteredCommits = filterCommitsByMessageRegexes(allCommits, options.commitMessageIncludeRegexes, options.commitMessageExcludeRegexes);
|
|
621
695
|
const filterByCommits = shouldFilterByCommits(allCommits, filteredCommits, options);
|
|
696
|
+
const rangeQuery = {
|
|
697
|
+
from,
|
|
698
|
+
to,
|
|
699
|
+
commits: filteredCommits,
|
|
700
|
+
filterByCommits,
|
|
701
|
+
pathFilter,
|
|
702
|
+
};
|
|
622
703
|
const [diffText, fileNames, diffSummary] = yield Promise.all([
|
|
623
|
-
getDiff(git,
|
|
624
|
-
getChangedFiles(git,
|
|
625
|
-
getDiffSummary(git,
|
|
704
|
+
getDiff(git, rangeQuery),
|
|
705
|
+
getChangedFiles(git, rangeQuery),
|
|
706
|
+
getDiffSummary(git, rangeQuery),
|
|
626
707
|
]);
|
|
627
708
|
const summarizeFlags = {
|
|
628
709
|
from,
|
|
@@ -634,7 +715,14 @@ function summarizeGitDiff(options) {
|
|
|
634
715
|
commitMessageIncludeRegexes: options.commitMessageIncludeRegexes,
|
|
635
716
|
commitMessageExcludeRegexes: options.commitMessageExcludeRegexes,
|
|
636
717
|
};
|
|
637
|
-
return generateSummary(
|
|
718
|
+
return generateSummary({
|
|
719
|
+
diffText,
|
|
720
|
+
fileNames,
|
|
721
|
+
commits: filteredCommits,
|
|
722
|
+
flags: summarizeFlags,
|
|
723
|
+
openAiClientProvider: options.openAiClientProvider,
|
|
724
|
+
diffSummary,
|
|
725
|
+
});
|
|
638
726
|
});
|
|
639
727
|
}
|
|
640
728
|
|