Skip to content

Expose formatPatch on diff object and document #451

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 29, 2023
Merged
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
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]);
});
});
});