Skip to content

Commit fa323e9

Browse files
committed
fix(loaders): handle quoted noprefix patch paths
1 parent 5b00432 commit fa323e9

2 files changed

Lines changed: 47 additions & 11 deletions

File tree

src/core/loaders.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,33 @@ describe("loadAppBootstrap", () => {
920920
});
921921
});
922922

923+
test("loads quoted noprefix patch text emitted for escaped git paths", async () => {
924+
const dir = createTempRepo("hunk-patch-quoted-noprefix-");
925+
const fileName = "src\tfile.txt";
926+
927+
writeFileSync(join(dir, fileName), "one\n");
928+
git(dir, "add", ".");
929+
git(dir, "commit", "-m", "initial");
930+
931+
writeFileSync(join(dir, fileName), "two\n");
932+
const patchText = git(dir, "-c", "diff.noprefix=true", "diff", "--", fileName);
933+
934+
expect(patchText).toContain('diff --git "src\\tfile.txt" "src\\tfile.txt"');
935+
936+
const bootstrap = await loadAppBootstrap({
937+
kind: "patch",
938+
text: patchText,
939+
options: { mode: "auto" },
940+
});
941+
942+
expect(bootstrap.changeset.files).toHaveLength(1);
943+
expect(bootstrap.changeset.files[0]).toMatchObject({
944+
path: "src\\tfile.txt",
945+
metadata: { name: "src\\tfile.txt", type: "change" },
946+
});
947+
expect(bootstrap.changeset.files[0]?.stats).toEqual({ additions: 1, deletions: 1 });
948+
});
949+
923950
test("does not mangle a deleted SQL `-- comment` line in a noprefix patch", async () => {
924951
// The original source line `-- drop table users;` (a SQL comment) is encoded in a unified
925952
// diff deletion as `--- drop table users;` — three dashes (one for the deletion marker,

src/core/loaders.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,15 @@ function normalizeGitPatchPrefixes(patchText: string) {
217217
function rewriteGitDiffHeader(line: string): { line: string; changed: boolean } {
218218
const rest = line.slice("diff --git ".length).trimEnd();
219219

220-
const quotedMatch = rest.match(/^"([^"]*)" "([^"]*)"$/);
220+
const quotedMatch = rest.match(/^"((?:\\.|[^"\\])*)" "((?:\\.|[^"\\])*)"$/);
221221
if (quotedMatch) {
222-
const [, oldPath, newPath] = quotedMatch;
223-
if (oldPath?.startsWith("a/") && newPath?.startsWith("b/")) {
224-
return { line, changed: false };
225-
}
226-
return { line: `diff --git "a/${oldPath}" "b/${newPath}"`, changed: true };
222+
const [, oldPath = "", newPath = ""] = quotedMatch;
223+
// Pierre's git header parser does not currently handle the quoted `"a/..." "b/..."`
224+
// form, so canonicalize quoted paths to the unquoted form even when prefixes exist.
225+
return {
226+
line: `diff --git ${withGitPrefix(oldPath, "a/")} ${withGitPrefix(newPath, "b/")}`,
227+
changed: true,
228+
};
227229
}
228230

229231
const tokens = rest.split(" ");
@@ -255,16 +257,23 @@ function rewriteGitDiffHeader(line: string): { line: string; changed: boolean }
255257
return { line, changed: false };
256258
}
257259

260+
/** Return a path with the expected Git side prefix while avoiding double-prefixing. */
261+
function withGitPrefix(path: string, prefix: "a/" | "b/") {
262+
return path.startsWith(prefix) ? path : `${prefix}${path}`;
263+
}
264+
258265
/** Insert the canonical `a/` or `b/` prefix on a unified-diff header that is missing it. */
259266
function rewriteUnifiedFileLine(line: string, marker: "--- " | "+++ ", prefix: "a/" | "b/") {
260267
const path = line.slice(marker.length);
261-
if (path === "/dev/null" || path.startsWith("/dev/null\t")) {
268+
const quotedPath = path.match(/^"((?:\\.|[^"\\])*)"(.*)$/);
269+
const pathName = quotedPath?.[1] ?? path;
270+
const suffix = quotedPath?.[2] ?? "";
271+
272+
if (pathName === "/dev/null" || pathName.startsWith("/dev/null\t")) {
262273
return line;
263274
}
264-
if (path.startsWith('"')) {
265-
return `${marker}"${prefix}${path.slice(1)}`;
266-
}
267-
return `${marker}${prefix}${path}`;
275+
276+
return `${marker}${withGitPrefix(pathName, prefix)}${suffix}`;
268277
}
269278

270279
/** Escape only the filename characters that break unified-diff header parsing. */

0 commit comments

Comments
 (0)