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