From 0c9dd6d0e622d8a32b441b45baa797a7e86a4c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Thu, 15 Oct 2015 10:08:45 +0200 Subject: [PATCH 01/13] Allow patchs in diff format --- src/patch/parse.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/patch/parse.js b/src/patch/parse.js index e00619a6..f00b474a 100644 --- a/src/patch/parse.js +++ b/src/patch/parse.js @@ -9,13 +9,13 @@ export function parsePatch(uniDiff, options = {}) { // Ignore any leading junk while (i < diffstr.length) { - if ((/^Index:/.test(diffstr[i])) || (/^@@/.test(diffstr[i]))) { + if (/^(Index:|diff -r|@@)/.test(diffstr[i])) { break; } i++; } - let header = (/^Index: (.*)/.exec(diffstr[i])); + let header = (/^(?:Index:|diff -r (?:\w+)) (.*)/.exec(diffstr[i])); if (header) { index.index = header[1]; i++; @@ -35,7 +35,7 @@ export function parsePatch(uniDiff, options = {}) { index.hunks = []; while (i < diffstr.length) { - if (/^Index:/.test(diffstr[i])) { + if (/^(Index:|diff -r)/.test(diffstr[i])) { break; } else if (/^@@/.test(diffstr[i])) { index.hunks.push(parseHunk()); From d501881150e4f93b3a47c9a82b04c6767b8165c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Thu, 15 Oct 2015 12:10:27 +0200 Subject: [PATCH 02/13] Allow headers with several revisions --- src/patch/parse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/patch/parse.js b/src/patch/parse.js index f00b474a..4d0466db 100644 --- a/src/patch/parse.js +++ b/src/patch/parse.js @@ -15,7 +15,7 @@ export function parsePatch(uniDiff, options = {}) { i++; } - let header = (/^(?:Index:|diff -r (?:\w+)) (.*)/.exec(diffstr[i])); + let header = (/^(?:Index:|diff(?: -r \w+)+) (.*)/.exec(diffstr[i])); if (header) { index.index = header[1]; i++; From c238f85e34627cf020a7ce174733ff0dcb89d1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Fri, 16 Oct 2015 00:47:14 +0200 Subject: [PATCH 03/13] Fix call to 'options.loadFile' with empty index --- src/patch/apply.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index c86cae26..d3a85a2d 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -84,7 +84,7 @@ export function applyPatches(uniDiff, options) { function processIndex() { let index = uniDiff[currentIndex++]; if (!index) { - options.complete(); + return options.complete(); } options.loadFile(index, function(err, data) { From 54edda72a054729c302152a1991a0fda894ca9c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Fri, 16 Oct 2015 01:44:27 +0200 Subject: [PATCH 04/13] Separated hunk sanity check from applyPatch algorythm --- src/patch/apply.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index d3a85a2d..5e2d2e3d 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -24,25 +24,40 @@ export function applyPatch(source, uniDiff, options = {}) { removeEOFNL, addEOFNL; - for (let i = 0; i < hunks.length; i++) { - let hunk = hunks[i], - toPos = hunk.newStart - 1; - - // Sanity check the input string. Bail if we don't match. + // Sanity check the input string. Fail if we don't match. + function sanityCheck(hunk, toPos) { for (let j = 0; j < hunk.lines.length; j++) { let line = hunk.lines[j], operation = line[0], content = line.substr(1); + if (operation === ' ' || operation === '-') { // Context sanity check if (!compareLine(toPos + 1, lines[toPos], operation, content)) { errorCount++; if (errorCount > fuzzFactor) { - return false; + return true; } } + toPos++; } + } + } + + for (let i = 0; i < hunks.length; i++) { + let hunk = hunks[i], + toPos = hunk.newStart - 1; + + // Sanity check the input string. Fail if we don't match. + if (sanityCheck(hunk, toPos)) { + return false; + } + + for (let j = 0; j < hunk.lines.length; j++) { + let line = hunk.lines[j], + operation = line[0], + content = line.substr(1); if (operation === ' ') { toPos++; From 836a2f88b4b41291dd0d79f4157fd1b3b50860eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Fri, 16 Oct 2015 02:48:00 +0200 Subject: [PATCH 05/13] Do sanity check of all hunks on the diff at once before start applying them --- src/patch/apply.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index 5e2d2e3d..a5ddb58f 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -24,8 +24,7 @@ export function applyPatch(source, uniDiff, options = {}) { removeEOFNL, addEOFNL; - // Sanity check the input string. Fail if we don't match. - function sanityCheck(hunk, toPos) { + function hunkFits(hunk, toPos) { for (let j = 0; j < hunk.lines.length; j++) { let line = hunk.lines[j], operation = line[0], @@ -37,22 +36,28 @@ export function applyPatch(source, uniDiff, options = {}) { errorCount++; if (errorCount > fuzzFactor) { - return true; + return false; } } toPos++; } } + + return true; } - for (let i = 0; i < hunks.length; i++) { - let hunk = hunks[i], - toPos = hunk.newStart - 1; + // Sanity check the input string. Fail if we don't match. + for (let i = 0; i < hunks.length; i++) { + let hunk = hunks[i]; - // Sanity check the input string. Fail if we don't match. - if (sanityCheck(hunk, toPos)) { + if (!hunkFits(hunk, hunk.oldStart - 1)) { return false; } + } + + for (let i = 0; i < hunks.length; i++) { + let hunk = hunks[i], + toPos = hunk.newStart - 1; for (let j = 0; j < hunk.lines.length; j++) { let line = hunk.lines[j], From 720f5aede23e20223f46cd0d3ef534efdcf56dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Fri, 16 Oct 2015 13:16:37 +0200 Subject: [PATCH 06/13] Calculate and apply location offset where hunk context can fit --- src/patch/apply.js | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index a5ddb58f..b2a727a6 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -19,6 +19,7 @@ export function applyPatch(source, uniDiff, options = {}) { compareLine = options.compareLine || ((lineNumber, line, operation, patchContent) => line === patchContent), errorCount = 0, + offset = 0, fuzzFactor = options.fuzzFactor || 0, removeEOFNL, @@ -46,18 +47,34 @@ export function applyPatch(source, uniDiff, options = {}) { return true; } - // Sanity check the input string. Fail if we don't match. + // Search best fit offsets for each hunk based on the previous ones for (let i = 0; i < hunks.length; i++) { - let hunk = hunks[i]; + let hunk = hunks[i], + minLine = 0, + toPos = offset + hunk.oldStart - 1; + + for (let localOffset = 0; ; localOffset++) { + if (toPos - localOffset < minLine + || lines.length < toPos + localOffset + hunk.oldLines) { + return false; + } - if (!hunkFits(hunk, hunk.oldStart - 1)) { - return false; + if (hunkFits(hunk, toPos - localOffset)) { + hunk.offset = offset -= localOffset; + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; + break; + } + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; + break; + } } } for (let i = 0; i < hunks.length; i++) { let hunk = hunks[i], - toPos = hunk.newStart - 1; + toPos = hunk.offset + hunk.newStart - 1; for (let j = 0; j < hunk.lines.length; j++) { let line = hunk.lines[j], From 0c2af7b132a578c3741404bbf6313b6fdb5c0a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Fri, 16 Oct 2015 15:07:15 +0200 Subject: [PATCH 07/13] Consider correctly both text limits instead of fail when reaching one --- src/patch/apply.js | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index b2a727a6..8d044042 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -50,28 +50,43 @@ export function applyPatch(source, uniDiff, options = {}) { // Search best fit offsets for each hunk based on the previous ones for (let i = 0; i < hunks.length; i++) { let hunk = hunks[i], + outOfLimits = 0, + localOffset = 0, minLine = 0, toPos = offset + hunk.oldStart - 1; - for (let localOffset = 0; ; localOffset++) { - if (toPos - localOffset < minLine - || lines.length < toPos + localOffset + hunk.oldLines) { - return false; + for (;;) { + if (toPos + localOffset + hunk.oldLines <= lines.length) { + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + break; + } + } else { + outOfLimits++; } - if (hunkFits(hunk, toPos - localOffset)) { - hunk.offset = offset -= localOffset; - minLine = hunk.offset + hunk.oldStart + hunk.oldLines; - break; + // If trying to fit hunk outside both limits, return error + if (outOfLimits === 2) { + return false; } - if (hunkFits(hunk, toPos + localOffset)) { - hunk.offset = offset += localOffset; - minLine = hunk.offset + hunk.oldStart + hunk.oldLines; - break; + + outOfLimits = 0; + localOffset++; + + if (minLine <= toPos - localOffset) { + if (hunkFits(hunk, toPos - localOffset)) { + hunk.offset = offset -= localOffset; + break; + } + } else { + outOfLimits++; } } + + minLine = hunk.offset + hunk.oldStart + hunk.oldLines; } + // Apply patch hunks for (let i = 0; i < hunks.length; i++) { let hunk = hunks[i], toPos = hunk.offset + hunk.newStart - 1; From d723875cd90b973c5ed89d06a6bfe99bc774d621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Fri, 16 Oct 2015 15:17:37 +0200 Subject: [PATCH 08/13] Test for hunk offsets --- test/patch/apply.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/patch/apply.js b/test/patch/apply.js index d15d8d0f..d952ef8f 100644 --- a/test/patch/apply.js +++ b/test/patch/apply.js @@ -374,6 +374,27 @@ describe('patch/apply', function() { + 'line5\n'); }); + it('should succeed when hunk has an offset', function() { + expect(applyPatch( + 'line1\n' + + 'line3\n' + + 'line4\n' + + 'line5\n', + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -3,2 +3,3 @@\n' + + ' line1\n' + + '+line2\n' + + ' line3\n')) + .to.equal( + 'line1\n' + + 'line2\n' + + 'line3\n' + + 'line4\n' + + 'line5\n'); + }); + it('should allow custom line comparison', function() { expect(applyPatch( 'line2\n' From c6b6a02b0f7847762a781abf812b7d657b364676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Wed, 21 Oct 2015 13:29:57 +0200 Subject: [PATCH 09/13] Improved code documentation --- src/patch/apply.js | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index 8d044042..805d07b2 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -19,12 +19,16 @@ export function applyPatch(source, uniDiff, options = {}) { compareLine = options.compareLine || ((lineNumber, line, operation, patchContent) => line === patchContent), errorCount = 0, - offset = 0, fuzzFactor = options.fuzzFactor || 0, + minLine = 0, + offset = 0, removeEOFNL, addEOFNL; + /** + * Checks if the hunk exactly fits on the provided location + */ function hunkFits(hunk, toPos) { for (let j = 0; j < hunk.lines.length; j++) { let line = hunk.lines[j], @@ -47,42 +51,46 @@ export function applyPatch(source, uniDiff, options = {}) { return true; } - // Search best fit offsets for each hunk based on the previous ones - for (let i = 0; i < hunks.length; i++) { + // Search best fit offsets for each hunk based on the previous ones + for (let i = 0; i < hunks.length; i++) { let hunk = hunks[i], outOfLimits = 0, localOffset = 0, - minLine = 0, toPos = offset + hunk.oldStart - 1; for (;;) { - if (toPos + localOffset + hunk.oldLines <= lines.length) { - if (hunkFits(hunk, toPos + localOffset)) { - hunk.offset = offset += localOffset; - break; - } - } else { + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) + if (lines.length < toPos + localOffset + hunk.oldLines) { outOfLimits++; + } else if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; + break; } - // If trying to fit hunk outside both limits, return error + // If we tried to fit hunk before text beginning and beyond text lenght, + // then hunk can't be fit on the text so we raise an error if (outOfLimits === 2) { return false; } + // Reset checks of trying to fit outside text limits and increase offset + // of the current hunk relative to its desired location outOfLimits = 0; localOffset++; - if (minLine <= toPos - localOffset) { - if (hunkFits(hunk, toPos - localOffset)) { - hunk.offset = offset -= localOffset; - break; - } - } else { + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location + if (toPos - localOffset < minLine) { outOfLimits++; + } else if (hunkFits(hunk, toPos - localOffset)) { + hunk.offset = offset -= localOffset; + break; } } + // Set lower text limit to end of the current hunk, so next ones don't try + // to fit over already patched text minLine = hunk.offset + hunk.oldStart + hunk.oldLines; } From 3908658d05a1d853d5069445772c1e6cda79247e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Mon, 26 Oct 2015 11:52:12 +0100 Subject: [PATCH 10/13] Made code easier to understand & minor optimizations --- src/patch/apply.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index 805d07b2..53488df9 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -54,36 +54,42 @@ export function applyPatch(source, uniDiff, options = {}) { // Search best fit offsets for each hunk based on the previous ones for (let i = 0; i < hunks.length; i++) { let hunk = hunks[i], - outOfLimits = 0, + backwardExhausted = false, + forwardExhausted = false, localOffset = 0, - toPos = offset + hunk.oldStart - 1; + toPos = offset + hunk.oldStart - 1, + maxLine = lines.length - hunk.oldLines, + toCheck; for (;;) { // Check if trying to fit beyond text length, and if not, check it fits // after offset location (or desired location on first iteration) - if (lines.length < toPos + localOffset + hunk.oldLines) { - outOfLimits++; - } else if (hunkFits(hunk, toPos + localOffset)) { + toCheck = toPos + localOffset; + if (maxLine < toCheck) { + forwardExhausted = true; + } else if (hunkFits(hunk, toCheck)) { hunk.offset = offset += localOffset; break; } // If we tried to fit hunk before text beginning and beyond text lenght, // then hunk can't be fit on the text so we raise an error - if (outOfLimits === 2) { + if (backwardExhausted && forwardExhausted) { return false; } // Reset checks of trying to fit outside text limits and increase offset // of the current hunk relative to its desired location - outOfLimits = 0; + backwardExhausted = false; + forwardExhausted = false; localOffset++; // Check if trying to fit before text beginning, and if not, check it fits // before offset location - if (toPos - localOffset < minLine) { - outOfLimits++; - } else if (hunkFits(hunk, toPos - localOffset)) { + toCheck = toPos - localOffset; + if (toCheck < minLine) { + backwardExhausted = true; + } else if (hunkFits(hunk, toCheck)) { hunk.offset = offset -= localOffset; break; } From 09b7efe54f6cc26ba3bb8b8a2c31fe911b0a51ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Mon, 26 Oct 2015 16:51:04 +0100 Subject: [PATCH 11/13] Split logic for fit candidates on an iterator --- src/patch/apply.js | 81 ++++++++++++++++++++++++++++----------------- test/patch/apply.js | 23 ++++++++++++- 2 files changed, 72 insertions(+), 32 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index 53488df9..e8af5cd7 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -51,50 +51,69 @@ export function applyPatch(source, uniDiff, options = {}) { return true; } - // Search best fit offsets for each hunk based on the previous ones - for (let i = 0; i < hunks.length; i++) { - let hunk = hunks[i], + function distanceIterator(toPos, minLine, maxLine) { + let wantForward = true, backwardExhausted = false, forwardExhausted = false, - localOffset = 0, - toPos = offset + hunk.oldStart - 1, - maxLine = lines.length - hunk.oldLines, - toCheck; + localOffset = 1; + + return function iterator() { + if (wantForward && !forwardExhausted) { + if (backwardExhausted) { + localOffset++; + } else { + wantForward = false; + } + + // Check if trying to fit beyond text length, and if not, check it fits + // after offset location (or desired location on first iteration) + if (toPos + localOffset <= maxLine) { + return localOffset; + } - for (;;) { - // Check if trying to fit beyond text length, and if not, check it fits - // after offset location (or desired location on first iteration) - toCheck = toPos + localOffset; - if (maxLine < toCheck) { forwardExhausted = true; - } else if (hunkFits(hunk, toCheck)) { - hunk.offset = offset += localOffset; - break; } - // If we tried to fit hunk before text beginning and beyond text lenght, - // then hunk can't be fit on the text so we raise an error - if (backwardExhausted && forwardExhausted) { - return false; - } + if (!backwardExhausted) { + if (!forwardExhausted) { + wantForward = true; + } - // Reset checks of trying to fit outside text limits and increase offset - // of the current hunk relative to its desired location - backwardExhausted = false; - forwardExhausted = false; - localOffset++; + // Check if trying to fit before text beginning, and if not, check it fits + // before offset location + if (minLine <= toPos - localOffset) { + return -localOffset++; + } - // Check if trying to fit before text beginning, and if not, check it fits - // before offset location - toCheck = toPos - localOffset; - if (toCheck < minLine) { backwardExhausted = true; - } else if (hunkFits(hunk, toCheck)) { - hunk.offset = offset -= localOffset; + return iterator(); + } + + // We tried to fit hunk before text beginning and beyond text lenght, then + // hunk can't fit on the text. Return undefined + }; + } + + // Search best fit offsets for each hunk based on the previous ones + for (let i = 0; i < hunks.length; i++) { + let hunk = hunks[i], + maxLine = lines.length - hunk.oldLines, + localOffset = 0, + toPos = offset + hunk.oldStart - 1; + + let iterator = distanceIterator(toPos, minLine, maxLine); + + for (; localOffset != undefined; localOffset = iterator()) { + if (hunkFits(hunk, toPos + localOffset)) { + hunk.offset = offset += localOffset; break; } } + if (localOffset == undefined) { + return false; + } + // Set lower text limit to end of the current hunk, so next ones don't try // to fit over already patched text minLine = hunk.offset + hunk.oldStart + hunk.oldLines; diff --git a/test/patch/apply.js b/test/patch/apply.js index d952ef8f..23675315 100644 --- a/test/patch/apply.js +++ b/test/patch/apply.js @@ -374,7 +374,7 @@ describe('patch/apply', function() { + 'line5\n'); }); - it('should succeed when hunk has an offset', function() { + it('should succeed when hunk needs a negative offset', function() { expect(applyPatch( 'line1\n' + 'line3\n' @@ -395,6 +395,27 @@ describe('patch/apply', function() { + 'line5\n'); }); + it('should succeed when hunk needs a positive offset', function() { + expect(applyPatch( + 'line1\n' + + 'line2\n' + + 'line3\n' + + 'line5\n', + + '--- test\theader1\n' + + '+++ test\theader2\n' + + '@@ -1,2 +1,3 @@\n' + + ' line3\n' + + '+line4\n' + + ' line5\n')) + .to.equal( + 'line1\n' + + 'line2\n' + + 'line3\n' + + 'line4\n' + + 'line5\n'); + }); + it('should allow custom line comparison', function() { expect(applyPatch( 'line2\n' From f91668bff74061082228dd36ca8c012ae8a1350c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Mon, 26 Oct 2015 16:52:09 +0100 Subject: [PATCH 12/13] Generate library on 'npm install' and 'npm publish' automatically --- package.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 97e8e35a..77ca7091 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ }, "main": "./lib", "scripts": { - "test": "grunt" + "prepublish": "grunt build", + "test": "grunt test" }, - "dependencies": {}, "devDependencies": { "async": "^1.4.2", "babel": "^5.8.23", @@ -60,6 +60,5 @@ "semver": "^5.0.3", "webpack": "^1.12.2", "webpack-dev-server": "^1.12.0" - }, - "optionalDependencies": {} + } } From 3958558c36e2ce23813669cc06b1395c070ce3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s=20Combarro=20=22piranna?= Date: Mon, 26 Oct 2015 18:51:02 +0100 Subject: [PATCH 13/13] Use explicit undefined --- src/patch/apply.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/patch/apply.js b/src/patch/apply.js index e8af5cd7..d4ce8b77 100644 --- a/src/patch/apply.js +++ b/src/patch/apply.js @@ -103,14 +103,14 @@ export function applyPatch(source, uniDiff, options = {}) { let iterator = distanceIterator(toPos, minLine, maxLine); - for (; localOffset != undefined; localOffset = iterator()) { + for (; localOffset !== undefined; localOffset = iterator()) { if (hunkFits(hunk, toPos + localOffset)) { hunk.offset = offset += localOffset; break; } } - if (localOffset == undefined) { + if (localOffset === undefined) { return false; }