Skip to content

Commit

Permalink
Expose formatPatch on diff object and document (#451)
Browse files Browse the repository at this point in the history
* Support parsePatch output in formatPatch; add tests

* Document formatPatch

* Add release notes
  • Loading branch information
ExplodingCabbage authored Dec 29, 2023
1 parent 8bd13d6 commit 56c6a8a
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 1 deletion.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ npm install diff --save

Just like Diff.createTwoFilesPatch, but with oldFileName being equal to newFileName.

* `Diff.formatPatch(patch)` - creates a unified diff patch.

The argument provided can either be an object representing a structured patch (like returned by `structuredPatch`) or an array of such objects (like returned by `parsePatch`).

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

Expand Down
1 change: 1 addition & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [#448](https://github.com/kpdecker/jsdiff/pull/411) Performance improvement. Diagonals whose furthest-reaching D-path would go off the edge of the edit graph are now skipped, rather than being pointlessly considered as called for by the original Myers diff algorithm. This dramatically speeds up computing diffs where the new text just appends or truncates content at the end of the old text.
- [#351](https://github.com/kpdecker/jsdiff/issues/351) Importing from the lib folder - e.g. `require("diff/lib/diff/word.js")` - will work again now. This had been broken for users on the latest version of Node since Node 17.5.0, which changed how Node interprets the `exports` property in jsdiff's `package.json` file.
- [#344](https://github.com/kpdecker/jsdiff/issues/344) `diffLines`, `createTwoFilesPatch`, and other patch-creation methods now take an optional `stripTrailingCr: true` option which causes Windows-style `\r\n` line endings to be replaced with Unix-style `\n` line endings before calculating the diff, just like GNU `diff`'s `--strip-trailing-cr` flag.
- [#451](https://github.com/kpdecker/jsdiff/pull/451) Added `diff.formatPatch`.

## v5.1.0

Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {diffArrays} from './diff/array';
import {applyPatch, applyPatches} from './patch/apply';
import {parsePatch} from './patch/parse';
import {merge} from './patch/merge';
import {structuredPatch, createTwoFilesPatch, createPatch} from './patch/create';
import {structuredPatch, createTwoFilesPatch, createPatch, formatPatch} from './patch/create';

import {convertChangesToDMP} from './convert/dmp';
import {convertChangesToXML} from './convert/xml';
Expand All @@ -51,6 +51,7 @@ export {
structuredPatch,
createTwoFilesPatch,
createPatch,
formatPatch,
applyPatch,
applyPatches,
parsePatch,
Expand Down
4 changes: 4 additions & 0 deletions src/patch/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHea
}

export function formatPatch(diff) {
if (Array.isArray(diff)) {
return diff.map(formatPatch).join('\n');
}

const ret = [];
if (diff.oldFileName == diff.newFileName) {
ret.push('Index: ' + diff.oldFileName);
Expand Down
98 changes: 98 additions & 0 deletions test/patch/create.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {diffWords} from '../../lib';
import {createPatch, createTwoFilesPatch, formatPatch, structuredPatch} from '../../lib/patch/create';
import {parsePatch} from '../../lib/patch/parse';

import {expect} from 'chai';

Expand Down Expand Up @@ -788,5 +789,102 @@ describe('patch/create', function() {
'header1', 'header2'
));
});
it('supports serializing an array of structured patch objects into a single patch file in unified diff format', function() {
const patch = [
{
oldFileName: 'foo',
oldHeader: '2023-12-29 15:48:17.976616966 +0000',
newFileName: 'bar',
newHeader: '2023-12-29 15:48:21.400516845 +0000',
hunks: [
{
oldStart: 1,
oldLines: 1,
newStart: 1,
newLines: 1,
lines: [
'-xxx',
'+yyy'
],
linedelimiters: [
'\n',
'\n'
]
}
]
},
{
oldFileName: 'baz',
oldHeader: '2023-12-29 15:48:29.376283616 +0000',
newFileName: 'qux',
newHeader: '2023-12-29 15:48:32.908180343 +0000',
hunks: [
{
oldStart: 1,
oldLines: 1,
newStart: 1,
newLines: 1,
lines: [
'-aaa',
'+bbb'
],
linedelimiters: [
'\n',
'\n'
]
}
]
}
];
expect(formatPatch(patch)).to.equal(
'===================================================================\n' +
'--- foo\t2023-12-29 15:48:17.976616966 +0000\n' +
'+++ bar\t2023-12-29 15:48:21.400516845 +0000\n' +
'@@ -1,1 +1,1 @@\n' +
'-xxx\n' +
'+yyy\n' +
'\n' +
'===================================================================\n' +
'--- baz\t2023-12-29 15:48:29.376283616 +0000\n' +
'+++ qux\t2023-12-29 15:48:32.908180343 +0000\n' +
'@@ -1,1 +1,1 @@\n' +
'-aaa\n' +
'+bbb\n'
);
});
it('should roughly be the inverse of parsePatch', function() {
// There are so many differences in how a semantically-equivalent patch
// can be formatted in unified diff format, AND in JsDiff's structured
// patch format as long as https://github.com/kpdecker/jsdiff/issues/434
// goes unresolved, that a stronger claim than "roughly the inverse" is
// sadly not possible here.

// Check 1: starting with a patch in uniform diff format, does
// formatPatch(parsePatch(...)) round-trip?
const uniformPatch = '===================================================================\n' +
'--- baz\t2023-12-29 15:48:29.376283616 +0000\n' +
'+++ qux\t2023-12-29 15:48:32.908180343 +0000\n' +
'@@ -1,1 +1,1 @@\n' +
'-aaa\n' +
'+bbb\n';
expect(formatPatch(parsePatch(uniformPatch))).to.equal(uniformPatch);

// Check 2: starting with a structuredPatch, does formatting and then
// parsing again basically round-trip as long as we wrap it in an array
// to match the output of parsePatch and delete the linedelimiters that
// parsePatch puts in?
const patchObj = structuredPatch(
'oldfile', 'newfile',
'line2\nline3\nline4\n', 'line2\nline3\nline5',
'header1', 'header2'
);

const roundTrippedPatch = parsePatch(formatPatch([patchObj]));
for (const hunk of roundTrippedPatch[0].hunks) {
delete hunk.linedelimiters;
}

expect(roundTrippedPatch).to.deep.equal([patchObj]);
});
});
});

0 comments on commit 56c6a8a

Please sign in to comment.