Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ jsdiff's diff functions all take an old text and a new text and perform three st

`patch` may be either a single structured patch object (as returned by `structuredPatch`) or an array of them (as returned by `parsePatch`). The optional `headerOptions` argument behaves the same as the `headerOptions` option of `createTwoFilesPatch`, except that it is ignored for Git patches (i.e. patches where `isGit` is `true`).

When a patch has `isGit: true`, `formatPatch` output is changed to more closely match Git's output: it emits a `diff --git` header, emits Git extended headers as appropriate based on properties like `isRename`, `isCreate`, `newMode`, etc, and always emits `---`/`+++` file headers when hunks are present but omits them when there are no hunks (e.g. renames without content changes). The `headerOptions` parameter has no effect on Git patches since the header format is fully determined by the Git extended header properties.
When a patch has `isGit: true`, `formatPatch` output is changed to more closely match Git's output: it emits a `diff --git` header, emits Git extended headers as appropriate based on properties like `isRename`, `isCreate`, `newMode`, etc, and always emits `---`/`+++` file headers when hunks are present but omits them when there are no hunks (e.g. renames without content changes). The `headerOptions` parameter has no effect on Git patches since the header format is fully determined by the Git extended header properties. Binary patches retain a `Binary files ... differ` marker; the formatted patch does not contain the binary file data.

* `structuredPatch(oldFileName, newFileName, oldStr, newStr[, oldHeader[, newHeader[, options]]])` - returns an object with an array of hunk objects.

Expand Down
5 changes: 5 additions & 0 deletions src/patch/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,11 @@ export function formatPatch(patch: StructuredPatch | StructuredPatch[], headerOp
ret.push('copy from ' + quoteFileNameIfNeeded((patch.oldFileName ?? '').replace(/^a\//, '')));
ret.push('copy to ' + quoteFileNameIfNeeded((patch.newFileName ?? '').replace(/^b\//, '')));
}
if (patch.isBinary) {
const oldName = patch.isCreate ? '/dev/null' : patch.oldFileName;
const newName = patch.isDelete ? '/dev/null' : patch.newFileName;
ret.push('Binary files ' + quoteFileNameIfNeeded(oldName) + ' and ' + quoteFileNameIfNeeded(newName) + ' differ');
}
} else {
if (headerOptions.includeIndex && patch.oldFileName == patch.newFileName && patch.oldFileName !== undefined) {
ret.push('Index: ' + quoteFileNameIfNeeded(patch.oldFileName));
Expand Down
54 changes: 54 additions & 0 deletions test/patch/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,60 @@ describe('patch/create', function() {
expect(formatPatch(patches, OMIT_HEADERS)).to.equal(expected);
});

describe('binary Git patches', function() {
const fixtures = [
['modification',
'diff --git a/old.bin b/new.bin\n' +
'Binary files a/old.bin and b/new.bin differ\n'],
['creation',
'diff --git a/new.bin b/new.bin\n' +
'new file mode 100644\n' +
'Binary files /dev/null and b/new.bin differ\n'],
['deletion',
'diff --git a/old.bin b/old.bin\n' +
'deleted file mode 100644\n' +
'Binary files a/old.bin and /dev/null differ\n'],
['rename',
'diff --git a/old.bin b/new.bin\n' +
'rename from old.bin\n' +
'rename to new.bin\n' +
'Binary files a/old.bin and b/new.bin differ\n'],
['copy',
'diff --git a/old.bin b/new.bin\n' +
'copy from old.bin\n' +
'copy to new.bin\n' +
'Binary files a/old.bin and b/new.bin differ\n'],
['quoted filename',
'diff --git "a/caf\\303\\251\\timage.bin" "b/caf\\303\\251\\timage.bin"\n' +
'Binary files "a/caf\\303\\251\\timage.bin" and "b/caf\\303\\251\\timage.bin" differ\n']
];

for (const [name, text] of fixtures) {
it(`should preserve a binary ${name} when formatting`, function() {
const parsed = parsePatch(text);
expect(parsed[0].isBinary).to.equal(true);
expect(formatPatch(parsed)).to.equal(text);
expect(parsePatch(formatPatch(parsed))).to.deep.equal(parsed);
});
}

it('should preserve binary flags in a mixed multi-file patch', function() {
const text = fixtures[0][1] +
'\n' +
'diff --git a/text.txt b/text.txt\n' +
'--- a/text.txt\n' +
'+++ b/text.txt\n' +
'@@ -1,1 +1,1 @@\n' +
'-before\n' +
'+after\n' +
'\n' + fixtures[1][1];
const parsed = parsePatch(text);
expect(parsed).to.have.length(3);
expect(parsed.map(patch => patch.isBinary)).to.deep.equal([true, undefined, true]);
expect(parsePatch(formatPatch(parsed))).to.deep.equal(parsed);
});
});

it('should emit rename headers for patches with isGit and isRename', function() {
const patch = {
oldFileName: 'a/old.txt',
Expand Down