@mcarvin/smart-diff 2.3.2 → 3.0.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 +42 -8
- package/dist/index.cjs +85 -37
- 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 +86 -38
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +87 -40
- package/dist/index.umd.js.map +1 -1
- package/dist/typings/git/diffNumstatParse.d.ts +1 -0
- package/dist/typings/git/diffTypes.d.ts +1 -0
- package/dist/typings/git/gitDiff.d.ts +1 -0
- package/dist/typings/git/gitDiffOps.d.ts +9 -7
- package/dist/typings/index.d.ts +3 -3
- package/package.json +4 -6
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://codecov.io/gh/mcarvin8/smart-diff)
|
|
8
8
|
[](https://dashboard.stryker-mutator.io/reports/github.com/mcarvin8/smart-diff/main)
|
|
9
9
|
|
|
10
|
-
TypeScript library that turns a **git revision range** into a **Markdown summary** using any LLM provider supported by the [Vercel AI SDK](https://sdk.vercel.ai) — OpenAI, Anthropic, Google Gemini, Amazon Bedrock, Mistral, Cohere, Groq, xAI, DeepSeek, or any OpenAI-compatible gateway. It
|
|
10
|
+
TypeScript library that turns a **git revision range** into a **Markdown summary** using any LLM provider supported by the [Vercel AI SDK](https://sdk.vercel.ai) — OpenAI, Anthropic, Google Gemini, Amazon Bedrock, Mistral, Cohere, Groq, xAI, DeepSeek, or any OpenAI-compatible gateway. It shells out to `git` to read the repo, respects **path includes/excludes** and **commit message include/exclude regexes**, and sends commits, paths, structured diff stats, and unified diff text to the model.
|
|
11
11
|
|
|
12
12
|
## Requirements
|
|
13
13
|
|
|
@@ -21,7 +21,28 @@ TypeScript library that turns a **git revision range** into a **Markdown summary
|
|
|
21
21
|
npm install @mcarvin/smart-diff
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
All provider packages are **optional** — only install the one(s) you need:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# OpenAI
|
|
28
|
+
npm install @ai-sdk/openai
|
|
29
|
+
|
|
30
|
+
# Anthropic
|
|
31
|
+
npm install @ai-sdk/anthropic
|
|
32
|
+
|
|
33
|
+
# Google Gemini
|
|
34
|
+
npm install @ai-sdk/google
|
|
35
|
+
|
|
36
|
+
# Amazon Bedrock
|
|
37
|
+
npm install @ai-sdk/amazon-bedrock
|
|
38
|
+
|
|
39
|
+
# OpenAI-compatible gateway (Azure, Ollama, Together, etc.)
|
|
40
|
+
npm install @ai-sdk/openai-compatible
|
|
41
|
+
|
|
42
|
+
# Others: @ai-sdk/mistral @ai-sdk/cohere @ai-sdk/groq @ai-sdk/xai @ai-sdk/deepseek
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If the package for the selected provider is missing at runtime, smart-diff throws a clear error telling you which one to install.
|
|
25
46
|
|
|
26
47
|
## Provider configuration
|
|
27
48
|
|
|
@@ -29,15 +50,15 @@ smart-diff is "configured" when [`isLlmProviderConfigured()`](#lower-level-api)
|
|
|
29
50
|
|
|
30
51
|
### Selecting a provider
|
|
31
52
|
|
|
32
|
-
`LLM_PROVIDER` explicitly selects a provider. When unset, the resolver auto-detects in this order: `LLM_BASE_URL`/`OPENAI_BASE_URL` → `openai-compatible`, `OPENAI_API_KEY`/`LLM_API_KEY` → `openai`, then `ANTHROPIC_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY` (or `GOOGLE_API_KEY`), `MISTRAL_API_KEY`, `COHERE_API_KEY`, `GROQ_API_KEY`, `XAI_API_KEY`, `DEEPSEEK_API_KEY`, and finally `OPENAI_DEFAULT_HEADERS`/`LLM_DEFAULT_HEADERS` → `openai`.
|
|
53
|
+
`LLM_PROVIDER` explicitly selects a provider. When unset, the resolver auto-detects in this order: `LLM_BASE_URL`/`OPENAI_BASE_URL` → `openai-compatible`, `OPENAI_API_KEY`/`LLM_API_KEY` → `openai`, then `ANTHROPIC_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY` (or `GOOGLE_API_KEY`), `MISTRAL_API_KEY`, `COHERE_API_KEY`, `GROQ_API_KEY`, `XAI_API_KEY`, `DEEPSEEK_API_KEY`, `AWS_ACCESS_KEY_ID`/`AWS_PROFILE` → `bedrock`, and finally `OPENAI_DEFAULT_HEADERS`/`LLM_DEFAULT_HEADERS` → `openai`.
|
|
33
54
|
|
|
34
55
|
| Provider (`LLM_PROVIDER`) | Package | Credential env vars | Default model |
|
|
35
56
|
|---|---|---|---|
|
|
36
57
|
| `openai` | `@ai-sdk/openai` | `OPENAI_API_KEY` or `LLM_API_KEY` | `gpt-4o-mini` |
|
|
37
58
|
| `openai-compatible` | `@ai-sdk/openai-compatible` | `LLM_BASE_URL` or `OPENAI_BASE_URL` (required); `OPENAI_API_KEY`/`LLM_API_KEY` or custom headers | `gpt-4o-mini` |
|
|
38
|
-
| `anthropic` | `@ai-sdk/anthropic` | `ANTHROPIC_API_KEY` | `claude-
|
|
59
|
+
| `anthropic` | `@ai-sdk/anthropic` | `ANTHROPIC_API_KEY` | `claude-haiku-4-5-20251001` |
|
|
39
60
|
| `google` | `@ai-sdk/google` | `GOOGLE_GENERATIVE_AI_API_KEY` or `GOOGLE_API_KEY` | `gemini-2.0-flash` |
|
|
40
|
-
| `bedrock` | `@ai-sdk/amazon-bedrock` |
|
|
61
|
+
| `bedrock` | `@ai-sdk/amazon-bedrock` | `AWS_ACCESS_KEY_ID` / `AWS_PROFILE` (auto-detects); full AWS credential chain supported | `anthropic.claude-3-5-haiku-20241022-v1:0` |
|
|
41
62
|
| `mistral` | `@ai-sdk/mistral` | `MISTRAL_API_KEY` | `mistral-small-latest` |
|
|
42
63
|
| `cohere` | `@ai-sdk/cohere` | `COHERE_API_KEY` | `command-r-08-2024` |
|
|
43
64
|
| `groq` | `@ai-sdk/groq` | `GROQ_API_KEY` | `llama-3.1-8b-instant` |
|
|
@@ -57,6 +78,7 @@ smart-diff is "configured" when [`isLlmProviderConfigured()`](#lower-level-api)
|
|
|
57
78
|
| `LLM_PROVIDER_NAME` | Display name used when `openai-compatible` is active (defaults to `openai-compatible`). |
|
|
58
79
|
| `OPENAI_MAX_DIFF_CHARS` / `LLM_MAX_DIFF_CHARS` | Max size of unified diff text sent to the model (default ~120k characters). |
|
|
59
80
|
| `OPENAI_MAX_TOKENS` / `LLM_MAX_TOKENS` | Max completion tokens (default 4000). |
|
|
81
|
+
| `LLM_TEMPERATURE` | Sampling temperature, clamped to 0–2 (default 0.2). Lower = more deterministic; higher = more varied prose. |
|
|
60
82
|
|
|
61
83
|
### Example: native OpenAI
|
|
62
84
|
|
|
@@ -113,7 +135,7 @@ const markdown = await summarizeGitDiff({
|
|
|
113
135
|
| Option | Description |
|
|
114
136
|
|--------|-------------|
|
|
115
137
|
| `from` / `to` | Git refs for the range; `to` defaults to `HEAD`. |
|
|
116
|
-
| `cwd` / `git` | Working
|
|
138
|
+
| `cwd` / `git` | Working directory path, or inject your own `GitClient` instance. |
|
|
117
139
|
| `includeFolders` | Limit diff to these paths relative to repo root (omit for full repo minus excludes). |
|
|
118
140
|
| `excludeFolders` | Excluded paths (git `:(exclude)` pathspecs), e.g. `node_modules`. |
|
|
119
141
|
| `commitMessageIncludeRegexes` | If any pattern is non-empty, only commits whose **full message** matches at least one pattern are kept (after excludes). Case-insensitive. |
|
|
@@ -173,10 +195,22 @@ const md = await summarizeGitDiff({
|
|
|
173
195
|
|
|
174
196
|
The package also exports helpers for building a custom pipeline on top of the same git and LLM behavior:
|
|
175
197
|
|
|
176
|
-
- **Git**: `createGitClient`, `getRepoRoot`, `getCommits`, `getDiff`, `getDiffSummary`, `getChangedFiles`, `filterCommitsByMessageRegexes`, `buildDiffPathspecs`, `buildDiffShapingGitArgs`, `shapeUnifiedDiff`, `DEFAULT_NOISE_EXCLUDES`
|
|
198
|
+
- **Git**: `createGitClient(cwd?, timeout?)`, `getRepoRoot`, `getCommits`, `getDiff`, `getDiffSummary`, `getChangedFiles`, `filterCommitsByMessageRegexes`, `buildDiffPathspecs`, `buildDiffShapingGitArgs`, `shapeUnifiedDiff`, `DEFAULT_NOISE_EXCLUDES` — `timeout` is in milliseconds, forwarded to `execFile`; omit for no timeout
|
|
177
199
|
- **AI**: `generateSummary`, `resolveLlmMaxDiffChars`, `truncateUnifiedDiffForLlm`
|
|
178
200
|
- **Provider resolution**: `resolveLanguageModel`, `detectLlmProvider`, `isLlmProviderConfigured`, `defaultModelForProvider`, `resolveLlmBaseUrl`, `parseLlmDefaultHeadersFromEnv`
|
|
179
|
-
- **Constants / types**: `DEFAULT_GIT_DIFF_SYSTEM_PROMPT`, `LLM_GATEWAY_REQUIRED_MESSAGE`, `LlmProviderId`, `LlmModelProvider`, `ResolveLanguageModelOptions`, `GenerateSummaryInput`, `SummarizeFlags`
|
|
201
|
+
- **Constants / types**: `DEFAULT_GIT_DIFF_SYSTEM_PROMPT`, `LLM_GATEWAY_REQUIRED_MESSAGE`, `LlmProviderId`, `LlmModelProvider`, `ResolveLanguageModelOptions`, `GenerateSummaryInput`, `SummarizeFlags`, `DiffFileSummary`, `DiffSummary`, `CommitInfo`, `GitClient`, `GitDiffRangeQuery`, `DiffPathFilter`, `DiffShapingOptions` — `DiffFileSummary.binary?: boolean` is set to `true` when git reports `-` for additions/deletions (binary file); absent for text files
|
|
202
|
+
|
|
203
|
+
## Migrating from 2.x → 3.x
|
|
204
|
+
|
|
205
|
+
`@ai-sdk/openai` and `@ai-sdk/openai-compatible` are no longer bundled as direct dependencies. If you use either, add them explicitly:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
npm install @ai-sdk/openai
|
|
209
|
+
# or
|
|
210
|
+
npm install @ai-sdk/openai-compatible
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Everything else — env vars, auto-detection, the public API — is unchanged.
|
|
180
214
|
|
|
181
215
|
## Migrating from 1.x → 2.x
|
|
182
216
|
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var ai = require('ai');
|
|
4
4
|
var node_path = require('node:path');
|
|
5
|
-
var
|
|
5
|
+
var node_child_process = require('node:child_process');
|
|
6
|
+
var node_util = require('node:util');
|
|
6
7
|
|
|
7
8
|
/******************************************************************************
|
|
8
9
|
Copyright (c) Microsoft Corporation.
|
|
@@ -53,7 +54,7 @@ const LLM_GATEWAY_REQUIRED_MESSAGE = "No LLM provider configured. Set LLM_PROVID
|
|
|
53
54
|
const DEFAULT_MODEL_BY_PROVIDER = {
|
|
54
55
|
openai: "gpt-4o-mini",
|
|
55
56
|
"openai-compatible": "gpt-4o-mini",
|
|
56
|
-
anthropic: "claude-
|
|
57
|
+
anthropic: "claude-haiku-4-5-20251001",
|
|
57
58
|
google: "gemini-2.0-flash",
|
|
58
59
|
bedrock: "anthropic.claude-3-5-haiku-20241022-v1:0",
|
|
59
60
|
mistral: "mistral-small-latest",
|
|
@@ -120,7 +121,7 @@ function resolveOpenAiApiKey() {
|
|
|
120
121
|
return (_a = readEnv("LLM_API_KEY")) !== null && _a !== void 0 ? _a : readEnv("OPENAI_API_KEY");
|
|
121
122
|
}
|
|
122
123
|
function detectLlmProvider() {
|
|
123
|
-
var _a, _b;
|
|
124
|
+
var _a, _b, _c;
|
|
124
125
|
const explicit = (_a = readEnv("LLM_PROVIDER")) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
125
126
|
if (explicit && isValidProviderId(explicit)) {
|
|
126
127
|
return explicit;
|
|
@@ -145,6 +146,8 @@ function detectLlmProvider() {
|
|
|
145
146
|
return "xai";
|
|
146
147
|
if (readEnv("DEEPSEEK_API_KEY"))
|
|
147
148
|
return "deepseek";
|
|
149
|
+
if ((_c = readEnv("AWS_ACCESS_KEY_ID")) !== null && _c !== void 0 ? _c : readEnv("AWS_PROFILE"))
|
|
150
|
+
return "bedrock";
|
|
148
151
|
if (parseLlmDefaultHeadersFromEnv())
|
|
149
152
|
return "openai";
|
|
150
153
|
return undefined;
|
|
@@ -157,17 +160,17 @@ function defaultModelForProvider(provider) {
|
|
|
157
160
|
}
|
|
158
161
|
function createOpenAiModel(modelId) {
|
|
159
162
|
return __awaiter(this, void 0, void 0, function* () {
|
|
160
|
-
const
|
|
163
|
+
const mod = yield importOptional("openai", "@ai-sdk/openai", () => import('@ai-sdk/openai'));
|
|
161
164
|
const apiKey = resolveOpenAiApiKey();
|
|
162
165
|
const headers = parseLlmDefaultHeadersFromEnv();
|
|
163
|
-
const provider = createOpenAI(Object.assign(Object.assign({}, (apiKey ? { apiKey } : {})), (headers ? { headers } : {})));
|
|
166
|
+
const provider = mod.createOpenAI(Object.assign(Object.assign({}, (apiKey ? { apiKey } : {})), (headers ? { headers } : {})));
|
|
164
167
|
return provider(modelId);
|
|
165
168
|
});
|
|
166
169
|
}
|
|
167
170
|
function createOpenAiCompatibleModel(modelId) {
|
|
168
171
|
return __awaiter(this, void 0, void 0, function* () {
|
|
169
172
|
var _a;
|
|
170
|
-
const { createOpenAICompatible } = yield import('@ai-sdk/openai-compatible');
|
|
173
|
+
const { createOpenAICompatible } = yield importOptional("openai-compatible", "@ai-sdk/openai-compatible", () => import('@ai-sdk/openai-compatible'));
|
|
171
174
|
const baseURL = resolveLlmBaseUrl();
|
|
172
175
|
if (!baseURL) {
|
|
173
176
|
throw new Error("openai-compatible provider requires LLM_BASE_URL or OPENAI_BASE_URL to be set.");
|
|
@@ -330,6 +333,17 @@ function resolveMaxOutputTokens() {
|
|
|
330
333
|
const parsed = raw !== undefined ? Number.parseInt(raw, 10) : 4000;
|
|
331
334
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : 4000;
|
|
332
335
|
}
|
|
336
|
+
function resolveLlmTemperature() {
|
|
337
|
+
var _a;
|
|
338
|
+
const raw = (_a = process.env.LLM_TEMPERATURE) === null || _a === void 0 ? void 0 : _a.trim();
|
|
339
|
+
if (raw) {
|
|
340
|
+
const parsed = Number.parseFloat(raw);
|
|
341
|
+
if (Number.isFinite(parsed)) {
|
|
342
|
+
return Math.min(2, Math.max(0, parsed));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return 0.2;
|
|
346
|
+
}
|
|
333
347
|
function generateSummary(input) {
|
|
334
348
|
return __awaiter(this, void 0, void 0, function* () {
|
|
335
349
|
var _a;
|
|
@@ -409,7 +423,7 @@ function callLlm(userContent, systemPrompt, maxOutputTokens, llmModelProvider, f
|
|
|
409
423
|
model,
|
|
410
424
|
system: systemPrompt,
|
|
411
425
|
prompt: userContent,
|
|
412
|
-
temperature:
|
|
426
|
+
temperature: resolveLlmTemperature(),
|
|
413
427
|
maxOutputTokens,
|
|
414
428
|
});
|
|
415
429
|
const text = result.text.trim();
|
|
@@ -424,10 +438,9 @@ function normalizeRepoRelativePath(p) {
|
|
|
424
438
|
return noTrailingSlash.length > 0 ? noTrailingSlash : ".";
|
|
425
439
|
}
|
|
426
440
|
function assertPathUnderRepo(repoRoot, userPath) {
|
|
441
|
+
const absRoot = node_path.resolve(repoRoot);
|
|
427
442
|
const abs = node_path.resolve(repoRoot, userPath);
|
|
428
|
-
|
|
429
|
-
const segments = rel.split(/[/\\]/);
|
|
430
|
-
if (segments.includes("..")) {
|
|
443
|
+
if (abs !== absRoot && !abs.startsWith(absRoot + node_path.sep)) {
|
|
431
444
|
throw new Error(`Path escapes repository root: ${JSON.stringify(userPath)}`);
|
|
432
445
|
}
|
|
433
446
|
}
|
|
@@ -705,10 +718,11 @@ function parseNumStatLine(line) {
|
|
|
705
718
|
const addStr = parts[0];
|
|
706
719
|
const delStr = parts[1];
|
|
707
720
|
const pathField = parts.slice(2).join("\t");
|
|
721
|
+
const binary = addStr === "-" || delStr === "-" ? true : undefined;
|
|
708
722
|
const additions = addStr !== "-" ? Number.parseInt(addStr, 10) || 0 : 0;
|
|
709
723
|
const deletions = delStr !== "-" ? Number.parseInt(delStr, 10) || 0 : 0;
|
|
710
724
|
const key = numStatPathToLookupKey(pathField);
|
|
711
|
-
return { key, additions, deletions };
|
|
725
|
+
return Object.assign({ key, additions, deletions }, (binary ? { binary } : {}));
|
|
712
726
|
}
|
|
713
727
|
function accumulateNumStat(numStatOutput, into) {
|
|
714
728
|
var _a;
|
|
@@ -720,10 +734,8 @@ function accumulateNumStat(numStatOutput, into) {
|
|
|
720
734
|
if (!parsed)
|
|
721
735
|
continue;
|
|
722
736
|
const prev = (_a = into.get(parsed.key)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
deletions: prev.deletions + parsed.deletions,
|
|
726
|
-
});
|
|
737
|
+
const binary = parsed.binary || prev.binary || undefined;
|
|
738
|
+
into.set(parsed.key, Object.assign({ additions: prev.additions + parsed.additions, deletions: prev.deletions + parsed.deletions }, (binary ? { binary } : {})));
|
|
727
739
|
}
|
|
728
740
|
}
|
|
729
741
|
|
|
@@ -813,7 +825,7 @@ function buildSyntheticDiffLine(meta, counts) {
|
|
|
813
825
|
return `${prefix}\t${counts.additions}\t${counts.deletions}\t${meta.path}`;
|
|
814
826
|
}
|
|
815
827
|
function buildDiffSummaryFromGitOutputs(nameStatusOutput, numStatOutput) {
|
|
816
|
-
var _a;
|
|
828
|
+
var _a, _b;
|
|
817
829
|
const numMap = new Map();
|
|
818
830
|
accumulateNumStat(numStatOutput, numMap);
|
|
819
831
|
const mergedName = mergeNameEntriesByPath(parseNameStatusLines(nameStatusOutput));
|
|
@@ -822,21 +834,39 @@ function buildDiffSummaryFromGitOutputs(nameStatusOutput, numStatOutput) {
|
|
|
822
834
|
const counts = (_a = numMap.get(path)) !== null && _a !== void 0 ? _a : { additions: 0, deletions: 0 };
|
|
823
835
|
syntheticLines.push(buildSyntheticDiffLine(meta, counts));
|
|
824
836
|
}
|
|
825
|
-
|
|
837
|
+
const summary = parseDiffSummary(syntheticLines.join("\n"));
|
|
838
|
+
for (const file of summary.files) {
|
|
839
|
+
if ((_b = numMap.get(file.path)) === null || _b === void 0 ? void 0 : _b.binary) {
|
|
840
|
+
file.binary = true;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return summary;
|
|
826
844
|
}
|
|
827
845
|
|
|
828
|
-
|
|
829
|
-
|
|
846
|
+
const execFileAsync = node_util.promisify(node_child_process.execFile);
|
|
847
|
+
function createGitClient(cwd = process.cwd(), timeout) {
|
|
848
|
+
return {
|
|
849
|
+
run: (args) => execFileAsync("git", args, Object.assign({ cwd, maxBuffer: 100 * 1024 * 1024 }, (timeout !== undefined ? { timeout } : {}))).then(({ stdout }) => stdout),
|
|
850
|
+
};
|
|
830
851
|
}
|
|
831
852
|
function getCommits(git, from, to) {
|
|
832
853
|
return __awaiter(this, void 0, void 0, function* () {
|
|
833
|
-
const
|
|
834
|
-
return
|
|
854
|
+
const output = yield git.run(["log", "--format=%H%x1f%s", `${from}..${to}`]);
|
|
855
|
+
return output
|
|
856
|
+
.split("\n")
|
|
857
|
+
.filter(Boolean)
|
|
858
|
+
.map((line) => {
|
|
859
|
+
const sep = line.indexOf("\x1f");
|
|
860
|
+
return {
|
|
861
|
+
hash: sep >= 0 ? line.slice(0, sep) : line,
|
|
862
|
+
message: sep >= 0 ? line.slice(sep + 1) : "",
|
|
863
|
+
};
|
|
864
|
+
});
|
|
835
865
|
});
|
|
836
866
|
}
|
|
837
867
|
function getRepoRoot(git) {
|
|
838
868
|
return __awaiter(this, void 0, void 0, function* () {
|
|
839
|
-
const root = yield git.
|
|
869
|
+
const root = yield git.run(["rev-parse", "--show-toplevel"]);
|
|
840
870
|
return root.trim();
|
|
841
871
|
});
|
|
842
872
|
}
|
|
@@ -853,7 +883,8 @@ function getDiff(git, query) {
|
|
|
853
883
|
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
854
884
|
const shapingArgs = buildDiffShapingGitArgs(shaping);
|
|
855
885
|
if (!filterByCommits) {
|
|
856
|
-
const raw = yield git.
|
|
886
|
+
const raw = yield git.run([
|
|
887
|
+
"diff",
|
|
857
888
|
...shapingArgs,
|
|
858
889
|
`${from}..${to}`,
|
|
859
890
|
"--",
|
|
@@ -861,7 +892,7 @@ function getDiff(git, query) {
|
|
|
861
892
|
]);
|
|
862
893
|
return shapeUnifiedDiff(raw, shaping);
|
|
863
894
|
}
|
|
864
|
-
const patches = yield Promise.all(commits.map((c) => git.
|
|
895
|
+
const patches = yield Promise.all(commits.map((c) => git.run(["diff", ...shapingArgs, `${c.hash}^!`, "--", ...specs])));
|
|
865
896
|
return patches
|
|
866
897
|
.map((p) => shapeUnifiedDiff(p, shaping))
|
|
867
898
|
.filter(Boolean)
|
|
@@ -875,14 +906,16 @@ function getDiffSummary(git, query) {
|
|
|
875
906
|
const whitespaceArgs = (shaping === null || shaping === void 0 ? void 0 : shaping.ignoreWhitespace) ? ["-w"] : [];
|
|
876
907
|
if (!filterByCommits) {
|
|
877
908
|
const [numOutput, nameOutput] = yield Promise.all([
|
|
878
|
-
git.
|
|
909
|
+
git.run([
|
|
910
|
+
"diff",
|
|
879
911
|
...whitespaceArgs,
|
|
880
912
|
"--numstat",
|
|
881
913
|
`${from}..${to}`,
|
|
882
914
|
"--",
|
|
883
915
|
...specs,
|
|
884
916
|
]),
|
|
885
|
-
git.
|
|
917
|
+
git.run([
|
|
918
|
+
"diff",
|
|
886
919
|
...whitespaceArgs,
|
|
887
920
|
"--name-status",
|
|
888
921
|
`${from}..${to}`,
|
|
@@ -895,8 +928,22 @@ function getDiffSummary(git, query) {
|
|
|
895
928
|
const pairs = yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
|
|
896
929
|
const range = `${c.hash}^!`;
|
|
897
930
|
const [numOutput, nameOutput] = yield Promise.all([
|
|
898
|
-
git.
|
|
899
|
-
|
|
931
|
+
git.run([
|
|
932
|
+
"diff",
|
|
933
|
+
...whitespaceArgs,
|
|
934
|
+
"--numstat",
|
|
935
|
+
range,
|
|
936
|
+
"--",
|
|
937
|
+
...specs,
|
|
938
|
+
]),
|
|
939
|
+
git.run([
|
|
940
|
+
"diff",
|
|
941
|
+
...whitespaceArgs,
|
|
942
|
+
"--name-status",
|
|
943
|
+
range,
|
|
944
|
+
"--",
|
|
945
|
+
...specs,
|
|
946
|
+
]),
|
|
900
947
|
]);
|
|
901
948
|
return { numOutput, nameOutput };
|
|
902
949
|
})));
|
|
@@ -916,7 +963,8 @@ function getChangedFiles(git, query) {
|
|
|
916
963
|
const { from, to, commits, filterByCommits, pathFilter, repoRootOverride } = query;
|
|
917
964
|
const { specs } = yield getDiffPathContext(git, pathFilter, repoRootOverride);
|
|
918
965
|
if (!filterByCommits) {
|
|
919
|
-
const output = yield git.
|
|
966
|
+
const output = yield git.run([
|
|
967
|
+
"diff",
|
|
920
968
|
"--name-only",
|
|
921
969
|
`${from}..${to}`,
|
|
922
970
|
"--",
|
|
@@ -925,24 +973,24 @@ function getChangedFiles(git, query) {
|
|
|
925
973
|
return output
|
|
926
974
|
.split(/\r?\n/)
|
|
927
975
|
.map((f) => f.trim())
|
|
928
|
-
.filter(Boolean)
|
|
976
|
+
.filter(Boolean)
|
|
977
|
+
.sort();
|
|
929
978
|
}
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
979
|
+
const results = yield Promise.all(commits.map((c) => __awaiter(this, void 0, void 0, function* () {
|
|
980
|
+
const output = yield git.run([
|
|
981
|
+
"show",
|
|
933
982
|
"--name-only",
|
|
934
983
|
"--pretty=format:",
|
|
935
984
|
c.hash,
|
|
936
985
|
"--",
|
|
937
986
|
...specs,
|
|
938
987
|
]);
|
|
939
|
-
output
|
|
988
|
+
return output
|
|
940
989
|
.split(/\r?\n/)
|
|
941
990
|
.map((f) => f.trim())
|
|
942
|
-
.filter(Boolean)
|
|
943
|
-
.forEach((f) => fileSet.add(f));
|
|
991
|
+
.filter(Boolean);
|
|
944
992
|
})));
|
|
945
|
-
return
|
|
993
|
+
return [...new Set(results.flat())].sort();
|
|
946
994
|
});
|
|
947
995
|
}
|
|
948
996
|
|