Skip to content

Commit 6fface2

Browse files
authored
fix: normalize git patch prefixes for parser compatibility (#106)
1 parent 1fe977c commit 6fface2

2 files changed

Lines changed: 102 additions & 3 deletions

File tree

src/core/git.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,24 @@ export function appendGitPathspecs(args: string[], pathspecs?: string[]) {
1919
args.push("--", ...pathspecs);
2020
}
2121

22+
// @pierre/diffs currently assumes git-style a/ and b/ prefixes when parsing patch headers.
23+
// Force canonical prefixes for git-backed review commands so user/repo git diff config
24+
// (noprefix, mnemonicPrefix, custom src/dst prefixes) cannot break parsing.
25+
const DIFF_PREFIX_NORMALIZATION_ARGS = [
26+
"-c",
27+
"diff.noprefix=false",
28+
"-c",
29+
"diff.mnemonicPrefix=false",
30+
"-c",
31+
"diff.srcPrefix=a/",
32+
"-c",
33+
"diff.dstPrefix=b/",
34+
];
35+
36+
function withNormalizedDiffPrefixes(args: string[]) {
37+
return [...DIFF_PREFIX_NORMALIZATION_ARGS, ...args];
38+
}
39+
2240
/** Build the exact `git diff` arguments used for the shared working-tree and range review path. */
2341
export function buildGitDiffArgs(input: GitCommandInput) {
2442
const args = ["diff", "--no-ext-diff", "--find-renames", "--no-color"];
@@ -32,7 +50,7 @@ export function buildGitDiffArgs(input: GitCommandInput) {
3250
}
3351

3452
appendGitPathspecs(args, input.pathspecs);
35-
return args;
53+
return withNormalizedDiffPrefixes(args);
3654
}
3755

3856
/** Build the exact `git show` arguments used for commit review. */
@@ -44,7 +62,7 @@ export function buildGitShowArgs(input: ShowCommandInput) {
4462
}
4563

4664
appendGitPathspecs(args, input.pathspecs);
47-
return args;
65+
return withNormalizedDiffPrefixes(args);
4866
}
4967

5068
/** Build the exact `git stash show -p` arguments used for stash review. */
@@ -55,7 +73,7 @@ export function buildGitStashShowArgs(input: StashShowCommandInput) {
5573
args.push(input.ref);
5674
}
5775

58-
return args;
76+
return withNormalizedDiffPrefixes(args);
5977
}
6078

6179
export function formatGitCommandLabel(input: GitBackedInput) {

test/loaders.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,46 @@ describe("loadAppBootstrap", () => {
119119
expect(bootstrap.changeset.files[0]?.stats.additions).toBeGreaterThan(0);
120120
});
121121

122+
test("loads git working tree changes when diff.noprefix is enabled", async () => {
123+
const dir = createTempRepo("hunk-git-noprefix-");
124+
125+
writeFileSync(join(dir, "example.ts"), "export const value = 1;\n");
126+
git(dir, "add", "example.ts");
127+
git(dir, "commit", "-m", "initial");
128+
129+
git(dir, "config", "--local", "diff.noprefix", "true");
130+
writeFileSync(join(dir, "example.ts"), "export const value = 2;\nexport const extra = true;\n");
131+
132+
const bootstrap = await loadFromRepo(dir, {
133+
kind: "git",
134+
staged: false,
135+
options: { mode: "auto" },
136+
});
137+
138+
expect(bootstrap.changeset.files).toHaveLength(1);
139+
expect(bootstrap.changeset.files[0]?.path).toBe("example.ts");
140+
});
141+
142+
test("loads git working tree changes when diff.mnemonicPrefix is enabled", async () => {
143+
const dir = createTempRepo("hunk-git-mnemonic-");
144+
145+
writeFileSync(join(dir, "example.ts"), "export const value = 1;\n");
146+
git(dir, "add", "example.ts");
147+
git(dir, "commit", "-m", "initial");
148+
149+
git(dir, "config", "--local", "diff.mnemonicPrefix", "true");
150+
writeFileSync(join(dir, "example.ts"), "export const value = 2;\nexport const extra = true;\n");
151+
152+
const bootstrap = await loadFromRepo(dir, {
153+
kind: "git",
154+
staged: false,
155+
options: { mode: "auto" },
156+
});
157+
158+
expect(bootstrap.changeset.files).toHaveLength(1);
159+
expect(bootstrap.changeset.files[0]?.path).toBe("example.ts");
160+
});
161+
122162
test("reports a friendly error when git review runs outside a repository", async () => {
123163
const dir = mkdtempSync(join(tmpdir(), "hunk-nonrepo-"));
124164
tempDirs.push(dir);
@@ -214,6 +254,28 @@ describe("loadAppBootstrap", () => {
214254
expect(bootstrap.changeset.files.map((file) => file.path)).toEqual(["alpha.ts"]);
215255
});
216256

257+
test("loads staged-only git diffs when diff.noprefix is enabled", async () => {
258+
const dir = createTempRepo("hunk-git-staged-noprefix-");
259+
260+
writeFileSync(join(dir, "alpha.ts"), "export const alpha = 1;\n");
261+
writeFileSync(join(dir, "beta.ts"), "export const beta = 1;\n");
262+
git(dir, "add", "alpha.ts", "beta.ts");
263+
git(dir, "commit", "-m", "initial");
264+
265+
git(dir, "config", "--local", "diff.noprefix", "true");
266+
writeFileSync(join(dir, "alpha.ts"), "export const alpha = 2;\n");
267+
git(dir, "add", "alpha.ts");
268+
writeFileSync(join(dir, "beta.ts"), "export const beta = 2;\n");
269+
270+
const bootstrap = await loadFromRepo(dir, {
271+
kind: "git",
272+
staged: true,
273+
options: { mode: "auto" },
274+
});
275+
276+
expect(bootstrap.changeset.files.map((file) => file.path)).toEqual(["alpha.ts"]);
277+
});
278+
217279
test("loads pathspec-limited git diffs from the full UI command path", async () => {
218280
const dir = createTempRepo("hunk-git-pathspec-");
219281

@@ -323,6 +385,25 @@ describe("loadAppBootstrap", () => {
323385
expect(bootstrap.changeset.title).toContain("stash");
324386
});
325387

388+
test("loads stash show output when diff.noprefix is enabled", async () => {
389+
const dir = createTempRepo("hunk-stash-noprefix-");
390+
391+
writeFileSync(join(dir, "alpha.ts"), "export const alpha = 1;\n");
392+
git(dir, "add", "alpha.ts");
393+
git(dir, "commit", "-m", "initial");
394+
395+
git(dir, "config", "--local", "diff.noprefix", "true");
396+
writeFileSync(join(dir, "alpha.ts"), "export const alpha = 2;\n");
397+
git(dir, "stash", "push", "-m", "update alpha");
398+
399+
const bootstrap = await loadFromRepo(dir, {
400+
kind: "stash-show",
401+
options: { mode: "auto" },
402+
});
403+
404+
expect(bootstrap.changeset.files.map((file) => file.path)).toEqual(["alpha.ts"]);
405+
});
406+
326407
test("reports a friendly error when no stash entries exist", async () => {
327408
const dir = createTempRepo("hunk-stash-empty-");
328409

0 commit comments

Comments
 (0)