From 2de3afdf68e78b8ed055baacf5005c4c02fa11b9 Mon Sep 17 00:00:00 2001 From: youdie006 Date: Tue, 8 Sep 2026 23:01:23 +0900 Subject: [PATCH] Quote the Index line like the other file-name headers formatPatch runs the file name through quoteFileNameIfNeeded on every header line it writes -- rename from/to, copy from/to, --- and +++ -- except Index:, which got the raw name. A name needing quotes then produced a patch parsePatch could not read back: createPatch('x\n--- evil', 'foo\n', 'bar\n') -> parsePatch throws: Missing "+++ ..." file header for evil parsePatch gains the matching unquoteIfQuoted, the same inverse it already applies to rename from/to. --- src/patch/create.ts | 2 +- src/patch/parse.ts | 3 ++- test/patch/create.js | 27 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/patch/create.ts b/src/patch/create.ts index 52b5ed07..28cf292b 100644 --- a/src/patch/create.ts +++ b/src/patch/create.ts @@ -413,7 +413,7 @@ export function formatPatch(patch: StructuredPatch | StructuredPatch[], headerOp } } else { if (headerOptions.includeIndex && patch.oldFileName == patch.newFileName && patch.oldFileName !== undefined) { - ret.push('Index: ' + patch.oldFileName); + ret.push('Index: ' + quoteFileNameIfNeeded(patch.oldFileName)); } if (headerOptions.includeUnderline) { ret.push('==================================================================='); diff --git a/src/patch/parse.ts b/src/patch/parse.ts index d48f2d9a..16c1c625 100755 --- a/src/patch/parse.ts +++ b/src/patch/parse.ts @@ -196,7 +196,8 @@ export function parsePatch(uniDiff: string): StructuredPatch[] { // -- ExplodingCabbage const headerMatch = (/^(?:Index:|diff(?: -r \w+)+)\s+/).exec(line); if (headerMatch) { - index.index = line.substring(headerMatch[0].length).trim(); + // Inverse of the quoteFileNameIfNeeded applied to the `Index:` line by formatPatch + index.index = unquoteIfQuoted(line.substring(headerMatch[0].length).trim()); } } diff --git a/test/patch/create.js b/test/patch/create.js index d9771678..ba07951b 100644 --- a/test/patch/create.js +++ b/test/patch/create.js @@ -704,6 +704,33 @@ describe('patch/create', function() { const diffResult = createTwoFilesPatch('testFileName', 'testFileName', 'foo\n', 'bar\n', undefined, undefined, {headerOptions: OMIT_HEADERS}); expect(diffResult).to.equal(expectedResult); }); + + it('should quote the Index line for file names that require quoting, like the --- and +++ lines', function() { + const fileName = 'x\n--- evil'; + const patch = createPatch(fileName, 'foo\n', 'bar\n'); + expect(patch).to.equal( + 'Index: "x\\n--- evil"\n' + + '===================================================================\n' + + '--- "x\\n--- evil"\n' + + '+++ "x\\n--- evil"\n' + + '@@ -1,1 +1,1 @@\n' + + '-foo\n' + + '+bar\n' + ); + expect(parsePatch(patch)[0].oldFileName).to.equal(fileName); + }); + + it('should unquote a quoted Index line when parsing', function() { + const patch = + 'Index: "x\\n--- evil"\n' + + '===================================================================\n' + + '--- "x\\n--- evil"\n' + + '+++ "x\\n--- evil"\n' + + '@@ -1,1 +1,1 @@\n' + + '-foo\n' + + '+bar\n'; + expect(parsePatch(patch)[0].index).to.equal('x\n--- evil'); + }); }); it('should respect maxEditLength', function() {