From 8690f318599ae0ec67ff166fbbad12eb660f95b9 Mon Sep 17 00:00:00 2001
From: snadrus <snadrus@gmail.com>
Date: Tue, 7 Feb 2017 23:04:40 -0800
Subject: [PATCH 01/12] Simple export of functionality

Please try this (or something similar) and rebuild the NPM so 3-way-merge is available there.
---
 src/index.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/index.js b/src/index.js
index 5c6340a4..a1535824 100644
--- a/src/index.js
+++ b/src/index.js
@@ -27,6 +27,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 {convertChangesToDMP} from './convert/dmp';
@@ -53,6 +54,7 @@ export {
   applyPatch,
   applyPatches,
   parsePatch,
+  merge,
   convertChangesToDMP,
   convertChangesToXML,
   canonicalize

From aced50b83e14950532848f83fa0c62a6255b9315 Mon Sep 17 00:00:00 2001
From: Amila Welihinda <amilajack@users.noreply.github.com>
Date: Sat, 4 Mar 2017 17:16:19 -0800
Subject: [PATCH 02/12] Added better installation methods

---
 README.md | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 03b5845d..e932d2e7 100644
--- a/README.md
+++ b/README.md
@@ -9,13 +9,13 @@ Based on the algorithm proposed in
 ["An O(ND) Difference Algorithm and its Variations" (Myers, 1986)](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.4.6927).
 
 ## Installation
-
-    npm install diff
-
+```bash
+npm install diff --save
+```
 or
-
-    bower install jsdiff
-
+```bash
+bower install jsdiff --save
+```
 
 ## API
 

From 2fe812938a483e5a24355ff819996ee00a2dbddd Mon Sep 17 00:00:00 2001
From: Ramya Achutha Rao <ramyar@microsoft.com>
Date: Thu, 16 Mar 2017 10:42:20 -0700
Subject: [PATCH 03/12] Sanitize filename while parsing patches

---
 src/patch/parse.js  |  6 +++++-
 test/patch/parse.js | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/src/patch/parse.js b/src/patch/parse.js
index 08be7628..1a6cc1f9 100755
--- a/src/patch/parse.js
+++ b/src/patch/parse.js
@@ -57,7 +57,11 @@ export function parsePatch(uniDiff, options = {}) {
     const fileHeader = headerPattern.exec(diffstr[i]);
     if (fileHeader) {
       let keyPrefix = fileHeader[1] === '---' ? 'old' : 'new';
-      index[keyPrefix + 'FileName'] = fileHeader[2];
+      let fileName = fileHeader[2].replace(/\\\\/g, '\\');
+      if (fileName.startsWith('"') && fileName.endsWith('"')) {
+        fileName = fileName.substr(1, fileName.length - 2);
+      }
+      index[keyPrefix + 'FileName'] = fileName;
       index[keyPrefix + 'Header'] = fileHeader[3];
 
       i++;
diff --git a/test/patch/parse.js b/test/patch/parse.js
index f7aa9f98..e63e7efb 100644
--- a/test/patch/parse.js
+++ b/test/patch/parse.js
@@ -276,6 +276,44 @@ Index: test2
         }]);
     });
 
+ it('should parse patches with filenames having quotes and back slashes', function() {
+      expect(parsePatch(
+`Index: test
+===================================================================
+--- "from\\\\a\\\\file\\\\with\\\\quotes\\\\and\\\\backslash"\theader1
++++ "to\\\\a\\\\file\\\\with\\\\quotes\\\\and\\\\backslash"\theader2
+@@ -1,3 +1,4 @@
+ line2
+ line3
++line4
+ line5`))
+        .to.eql([{
+          index: 'test',
+          oldFileName: 'from\\a\\file\\with\\quotes\\and\\backslash',
+          oldHeader: 'header1',
+          newFileName: 'to\\a\\file\\with\\quotes\\and\\backslash',
+          newHeader: 'header2',
+          hunks: [
+            {
+              oldStart: 1, oldLines: 3,
+              newStart: 1, newLines: 4,
+              lines: [
+                ' line2',
+                ' line3',
+                '+line4',
+                ' line5'
+              ],
+              linedelimiters: [
+                '\n',
+                '\n',
+                '\n',
+                '\n'
+              ]
+            }
+          ]
+        }]);
+    });
+
     it('should note added EOFNL', function() {
       expect(parsePatch(
 `@@ -1,3 +1,4 @@

From 3cbfbb5bab714f62ec70ee4bf9a87c6a2957633e Mon Sep 17 00:00:00 2001
From: Loreto Parisi <loretoparisi@gmail.com>
Date: Mon, 28 Nov 2016 17:02:24 +0100
Subject: [PATCH 04/12] ignoreCase option

---
 src/diff/base.js | 3 ++-
 src/diff/word.js | 4 +++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/diff/base.js b/src/diff/base.js
index 7ce7e320..4a514b4d 100644
--- a/src/diff/base.js
+++ b/src/diff/base.js
@@ -144,7 +144,8 @@ Diff.prototype = {
   },
 
   equals(left, right) {
-    return left === right;
+    return left === right
+      || (this.options.ignoreCase && left.toLowerCase() === right.toLowerCase());
   },
   removeEmpty(array) {
     let ret = [];
diff --git a/src/diff/word.js b/src/diff/word.js
index 4c846abd..01ab4700 100644
--- a/src/diff/word.js
+++ b/src/diff/word.js
@@ -25,7 +25,9 @@ const reWhitespace = /\S/;
 
 export const wordDiff = new Diff();
 wordDiff.equals = function(left, right) {
-  return left === right || (this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right));
+  return (left === right)
+    || (this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right))
+    || (this.options.ignoreCase && left.toLowerCase() === right.toLowerCase());
 };
 wordDiff.tokenize = function(value) {
   let tokens = value.split(/(\s+|\b)/);

From 3c78fd05dcb9e69bcec722db8043cc8818e6f6b1 Mon Sep 17 00:00:00 2001
From: Artem Vasiliev <artem.job@gmail.com>
Date: Wed, 8 Feb 2017 18:32:05 +0300
Subject: [PATCH 05/12] fix: some corrections re ignoreCase option

.. to make existing tests pass

also added couple tests for ignoreCase option (but it's < 100% coverage still)
+ docs
---
 README.md              |  6 ++++++
 src/diff/character.js  |  2 +-
 src/diff/json.js       |  2 +-
 src/diff/word.js       | 32 +++++++++++++++++++++++++-------
 test/diff/character.js | 12 ++++++++++++
 test/diff/word.js      | 12 ++++++++++++
 6 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index e932d2e7..ec2363fe 100644
--- a/README.md
+++ b/README.md
@@ -23,10 +23,16 @@ bower install jsdiff --save
 
     Returns a list of change objects (See below).
 
+    Options
+    * `ignoreCase`: `true` to ignore casing difference. Defaults to `false`.
+
 * `JsDiff.diffWords(oldStr, newStr[, options])` - diffs two blocks of text, comparing word by word, ignoring whitespace.
 
     Returns a list of change objects (See below).
 
+    Options
+    * `ignoreCase`: Same as in `diffChars`.
+
 * `JsDiff.diffWordsWithSpace(oldStr, newStr[, options])` - diffs two blocks of text, comparing word by word, treating whitespace as significant.
 
     Returns a list of change objects (See below).
diff --git a/src/diff/character.js b/src/diff/character.js
index 6911e3ee..e9f17b11 100644
--- a/src/diff/character.js
+++ b/src/diff/character.js
@@ -1,4 +1,4 @@
 import Diff from './base';
 
 export const characterDiff = new Diff();
-export function diffChars(oldStr, newStr, callback) { return characterDiff.diff(oldStr, newStr, callback); }
+export function diffChars(oldStr, newStr, options) { return characterDiff.diff(oldStr, newStr, options); }
diff --git a/src/diff/json.js b/src/diff/json.js
index 68d85ccb..126351b7 100644
--- a/src/diff/json.js
+++ b/src/diff/json.js
@@ -22,7 +22,7 @@ jsonDiff.castInput = function(value) {
   }, '  ');
 };
 jsonDiff.equals = function(left, right) {
-  return Diff.prototype.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
+  return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'));
 };
 
 export function diffJson(oldObj, newObj, options) { return jsonDiff.diff(oldObj, newObj, options); }
diff --git a/src/diff/word.js b/src/diff/word.js
index 01ab4700..0d667f93 100644
--- a/src/diff/word.js
+++ b/src/diff/word.js
@@ -25,9 +25,26 @@ const reWhitespace = /\S/;
 
 export const wordDiff = new Diff();
 wordDiff.equals = function(left, right) {
-  return (left === right)
-    || (this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right))
-    || (this.options.ignoreCase && left.toLowerCase() === right.toLowerCase());
+  if (left === right) {
+    return true;
+  }
+
+  let ignoreWhitespaceComparator = (left, right) => {
+    return this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);
+  };
+
+  if (this.options.ignoreCase) {
+      left = left.toLowerCase();
+      right = right.toLowerCase();
+      if (left === right) {
+        return true;
+      }
+      if (ignoreWhitespaceComparator(left, right)) {
+        return true;
+      }
+  } else {
+    return ignoreWhitespaceComparator(left, right);
+  }
 };
 wordDiff.tokenize = function(value) {
   let tokens = value.split(/(\s+|\b)/);
@@ -47,10 +64,11 @@ wordDiff.tokenize = function(value) {
   return tokens;
 };
 
-export function diffWords(oldStr, newStr, callback) {
-  let options = generateOptions(callback, {ignoreWhitespace: true});
+export function diffWords(oldStr, newStr, options) {
+  options = generateOptions(options, {ignoreWhitespace: true});
   return wordDiff.diff(oldStr, newStr, options);
 }
-export function diffWordsWithSpace(oldStr, newStr, callback) {
-  return wordDiff.diff(oldStr, newStr, callback);
+
+export function diffWordsWithSpace(oldStr, newStr, options) {
+  return wordDiff.diff(oldStr, newStr, options);
 }
diff --git a/test/diff/character.js b/test/diff/character.js
index e0fb8be5..9061e69b 100644
--- a/test/diff/character.js
+++ b/test/diff/character.js
@@ -9,5 +9,17 @@ describe('diff/character', function() {
       const diffResult = diffChars('New Value.', 'New ValueMoreData.');
       expect(convertChangesToXML(diffResult)).to.equal('New Value<ins>MoreData</ins>.');
     });
+
+    describe('case insensitivity', function() {
+      it("is considered when there's no difference", function() {
+        const diffResult = diffChars('New Value.', 'New value.', {ignoreCase: true});
+        expect(convertChangesToXML(diffResult)).to.equal('New value.');
+      });
+
+      it("is considered when there's a difference", function() {
+        const diffResult = diffChars('New Values.', 'New value.', {ignoreCase: true});
+        expect(convertChangesToXML(diffResult)).to.equal('New value<del>s</del>.');
+      });
+    });
   });
 });
diff --git a/test/diff/word.js b/test/diff/word.js
index 1df2fd76..e860bfd0 100644
--- a/test/diff/word.js
+++ b/test/diff/word.js
@@ -190,5 +190,17 @@ describe('WordDiff', function() {
         done();
       });
     });
+
+    describe('case insensitivity', function() {
+      it("is considered when there's a difference", function() {
+        const diffResult = diffWordsWithSpace('new value', 'New  ValueMoreData', {ignoreCase: true});
+        expect(convertChangesToXML(diffResult)).to.equal('New<del> value</del><ins>  ValueMoreData</ins>');
+      });
+
+      it("is considered when there's no difference", function() {
+        const diffResult = diffWordsWithSpace('new value', 'New Value', {ignoreCase: true});
+        expect(convertChangesToXML(diffResult)).to.equal('New Value');
+      });
+    });
   });
 });

From b094a6f9a1aff1d82a1340e940bcede8482c63dc Mon Sep 17 00:00:00 2001
From: Artem Vasiliev <artem.job@gmail.com>
Date: Wed, 8 Feb 2017 20:58:50 +0300
Subject: [PATCH 06/12] refactor: simplified code a bit

which makes coverage 100%, yay!
---
 src/diff/word.js | 20 ++++++--------------
 1 file changed, 6 insertions(+), 14 deletions(-)

diff --git a/src/diff/word.js b/src/diff/word.js
index 0d667f93..4db44dff 100644
--- a/src/diff/word.js
+++ b/src/diff/word.js
@@ -29,22 +29,14 @@ wordDiff.equals = function(left, right) {
     return true;
   }
 
-  let ignoreWhitespaceComparator = (left, right) => {
-    return this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);
-  };
-
   if (this.options.ignoreCase) {
-      left = left.toLowerCase();
-      right = right.toLowerCase();
-      if (left === right) {
-        return true;
-      }
-      if (ignoreWhitespaceComparator(left, right)) {
-        return true;
-      }
-  } else {
-    return ignoreWhitespaceComparator(left, right);
+    left = left.toLowerCase();
+    right = right.toLowerCase();
+    if (left === right) {
+      return true;
+    }
   }
+  return this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);
 };
 wordDiff.tokenize = function(value) {
   let tokens = value.split(/(\s+|\b)/);

From 8f8e0f296a6935a1854d1f2c0ea7ddac7e89a0ae Mon Sep 17 00:00:00 2001
From: Artem Vasiliev <artem.job@gmail.com>
Date: Wed, 8 Feb 2017 21:03:08 +0300
Subject: [PATCH 07/12] refactor: simplified code a bit more

---
 src/diff/word.js | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/src/diff/word.js b/src/diff/word.js
index 4db44dff..9ba3b31e 100644
--- a/src/diff/word.js
+++ b/src/diff/word.js
@@ -25,18 +25,11 @@ const reWhitespace = /\S/;
 
 export const wordDiff = new Diff();
 wordDiff.equals = function(left, right) {
-  if (left === right) {
-    return true;
-  }
-
   if (this.options.ignoreCase) {
     left = left.toLowerCase();
     right = right.toLowerCase();
-    if (left === right) {
-      return true;
-    }
   }
-  return this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right);
+  return left === right || (this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right));
 };
 wordDiff.tokenize = function(value) {
   let tokens = value.split(/(\s+|\b)/);

From e64f6749e54b9323296a3af64ffaa424df316357 Mon Sep 17 00:00:00 2001
From: Kevin Decker <kpdecker@gmail.com>
Date: Wed, 5 Jul 2017 21:25:31 -0500
Subject: [PATCH 08/12] Add browser flag

Fixes #185
---
 package.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/package.json b/package.json
index 99a4fea4..b52dffba 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
     "node": ">=0.3.1"
   },
   "main": "./lib",
+  "browser": "./dist/diff.js",
   "scripts": {
     "test": "grunt"
   },

From 6cab62ce07b0261c07dedf9df4f01b877b2d6f63 Mon Sep 17 00:00:00 2001
From: Kevin Decker <kpdecker@gmail.com>
Date: Wed, 5 Jul 2017 21:28:40 -0500
Subject: [PATCH 09/12] Use regex rather than starts/ends with for parsePatch

---
 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 1a6cc1f9..310e3a94 100755
--- a/src/patch/parse.js
+++ b/src/patch/parse.js
@@ -58,7 +58,7 @@ export function parsePatch(uniDiff, options = {}) {
     if (fileHeader) {
       let keyPrefix = fileHeader[1] === '---' ? 'old' : 'new';
       let fileName = fileHeader[2].replace(/\\\\/g, '\\');
-      if (fileName.startsWith('"') && fileName.endsWith('"')) {
+      if (/^".*"$/.test(fileName)) {
         fileName = fileName.substr(1, fileName.length - 2);
       }
       index[keyPrefix + 'FileName'] = fileName;

From d8a363516ec35462998b407adcd710c0a2d21365 Mon Sep 17 00:00:00 2001
From: Artyom Skrobov <artyom@hola.org>
Date: Mon, 29 May 2017 15:30:54 +0300
Subject: [PATCH 10/12] Gracefully accept invalid newStart in hunks, same as
 patch(1) does.

---
 src/patch/apply.js  |  5 +++--
 test/patch/apply.js | 45 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/src/patch/apply.js b/src/patch/apply.js
index 3cf472ab..e6509260 100644
--- a/src/patch/apply.js
+++ b/src/patch/apply.js
@@ -79,10 +79,11 @@ export function applyPatch(source, uniDiff, options = {}) {
   }
 
   // Apply patch hunks
+  let diffOffset = 0;
   for (let i = 0; i < hunks.length; i++) {
     let hunk = hunks[i],
-        toPos = hunk.offset + hunk.newStart - 1;
-    if (hunk.newLines == 0) { toPos++; }
+        toPos = hunk.oldStart + hunk.offset + diffOffset - 1;
+    diffOffset += hunk.newLines - hunk.oldLines;
 
     for (let j = 0; j < hunk.lines.length; j++) {
       let line = hunk.lines[j],
diff --git a/test/patch/apply.js b/test/patch/apply.js
index 7088af7f..96ff4703 100755
--- a/test/patch/apply.js
+++ b/test/patch/apply.js
@@ -416,6 +416,51 @@ describe('patch/apply', function() {
           + 'line5\n');
     });
 
+    it('should succeed when 1st hunk specifies invalid newStart', function() {
+      expect(applyPatch(
+          'line1\n'
+          + 'line2\n'
+          + 'line3\n'
+          + 'line5\n',
+
+          '--- test\theader1\n'
+          + '+++ test\theader2\n'
+          + '@@ -1,2 +2,3 @@\n'
+          + ' line3\n'
+          + '+line4\n'
+          + ' line5\n'))
+        .to.equal(
+          'line1\n'
+          + 'line2\n'
+          + 'line3\n'
+          + 'line4\n'
+          + 'line5\n');
+    });
+
+    it('should succeed when 2nd hunk specifies invalid newStart', function() {
+      expect(applyPatch(
+          'line1\n'
+          + 'line2\n'
+          + 'line3\n'
+          + 'line5\n',
+
+          '--- test\theader1\n'
+          + '+++ test\theader2\n'
+          + '@@ -1,3 +1,2 @@\n'
+          + ' line1\n'
+          + '-line2\n'
+          + ' line3\n'
+          + '@@ -3,2 +3,3 @@\n'
+          + ' line3\n'
+          + '+line4\n'
+          + ' line5\n'))
+        .to.equal(
+          'line1\n'
+          + 'line3\n'
+          + 'line4\n'
+          + 'line5\n');
+    });
+
     it('should erase a file', function() {
       expect(applyPatch(
           'line1\n'

From f93e3544173a37b5bff5de16ae620a37e819ea59 Mon Sep 17 00:00:00 2001
From: Kevin Decker <kpdecker@gmail.com>
Date: Wed, 5 Jul 2017 21:40:15 -0500
Subject: [PATCH 11/12] Update release notes

---
 release-notes.md | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/release-notes.md b/release-notes.md
index dedc8145..63de9ac1 100644
--- a/release-notes.md
+++ b/release-notes.md
@@ -2,7 +2,22 @@
 
 ## Development
 
-[Commits](https://github.com/kpdecker/jsdiff/compare/v3.2.0...master)
+[Commits](https://github.com/kpdecker/jsdiff/compare/v3.3.0...master)
+
+## v3.3.0 - July 5th, 2017
+- [#114](https://github.com/kpdecker/jsdiff/issues/114) - /patch/merge not exported
+- Gracefully accept invalid newStart in hunks, same as patch(1) does. - d8a3635
+- Use regex rather than starts/ends with for parsePatch - 6cab62c
+- Add browser flag - e64f674
+- refactor: simplified code a bit more - 8f8e0f2
+- refactor: simplified code a bit - b094a6f
+- fix: some corrections re ignoreCase option - 3c78fd0
+- ignoreCase option - 3cbfbb5
+- Sanitize filename while parsing patches - 2fe8129
+- Added better installation methods - aced50b
+- Simple export of functionality - 8690f31
+
+[Commits](https://github.com/kpdecker/jsdiff/compare/v3.2.0...v3.3.0)
 
 ## v3.2.0 - December 26th, 2016
 - [#156](https://github.com/kpdecker/jsdiff/pull/156) - Add `undefinedReplacement` option to `diffJson` ([@ewnd9](https://api.github.com/users/ewnd9))

From 2a7ae3edf035c1a6be2f28443b67cf3da757410b Mon Sep 17 00:00:00 2001
From: Kevin Decker <kpdecker@gmail.com>
Date: Wed, 5 Jul 2017 21:40:28 -0500
Subject: [PATCH 12/12] v3.3.0

---
 components/bower.json     | 2 +-
 components/component.json | 2 +-
 package.json              | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/components/bower.json b/components/bower.json
index 78cb7c91..722047ba 100644
--- a/components/bower.json
+++ b/components/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "jsdiff",
-  "version": "3.2.0",
+  "version": "3.3.0",
   "main": [
     "diff.js"
   ],
diff --git a/components/component.json b/components/component.json
index 8b025f7e..7e140fca 100644
--- a/components/component.json
+++ b/components/component.json
@@ -6,7 +6,7 @@
     "diff",
     "text"
   ],
-  "version": "3.2.0",
+  "version": "3.3.0",
   "scripts": [ "diff.js" ],
   "main":  "diff.js",
   "license": "BSD"
diff --git a/package.json b/package.json
index b52dffba..8b13337e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "diff",
-  "version": "3.2.0",
+  "version": "3.3.0",
   "description": "A javascript text diff implementation.",
   "keywords": [
     "diff",
