ApplyCommand: use context lines to determine hunk location
If a hunk does not apply at the position stated in the hunk header
try to determine its position using the old lines (context and
deleted lines).
This is still a far cry from a full git apply: it doesn't do binary
patches, it doesn't handle git's whitespace options, and it's perhaps
not the fastest on big patches. C git hashes the lines and uses these
hashes to speed up matching hunks (and to do its whitespace magic).
Bug: 562348
Change-Id: Id0796bba059d84e648769d5896f497fde0b787dd
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown.patch
new file mode 100644
index 0000000..74c3371
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown.patch
@@ -0,0 +1,14 @@
+diff --git a/ShiftDown b/ShiftDown
+index 8b9727b..25dc192 100644
+--- a/ShiftDown
++++ b/ShiftDown
+@@ -16,6 +16,9 @@
+ something("A.b", "bar");
+ }
+
++ public void methodC() {
++ something("A.c", "bar");
++ }
+ }
+
+ public class B {
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2.patch
new file mode 100644
index 0000000..a2b34b3
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2.patch
@@ -0,0 +1,24 @@
+diff --git a/ShiftDown2 b/ShiftDown2
+index 8b9727b..63353aa 100644
+--- a/ShiftDown2
++++ b/ShiftDown2
+@@ -16,6 +16,9 @@
+ something("A.b", "bar");
+ }
+
++ public void methodC() {
++ something("A.c", "bar");
++ }
+ }
+
+ public class B {
+@@ -28,5 +31,9 @@
+ something("B.b", "bar");
+ }
+
++ public void methodC() {
++ something("B.c", "bar");
++ }
++
+ }
+ }
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2_PostImage
new file mode 100644
index 0000000..738484e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2_PostImage
@@ -0,0 +1,75 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class D {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class E {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class F {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ public void methodC() {
+ something("A.c", "bar");
+ }
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ public void methodC() {
+ something("B.c", "bar");
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2_PreImage
new file mode 100644
index 0000000..e1ee19c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown2_PreImage
@@ -0,0 +1,68 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class D {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class E {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class F {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown_PostImage
new file mode 100644
index 0000000..5c6e9bc
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown_PostImage
@@ -0,0 +1,71 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class D {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class E {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class F {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ public void methodC() {
+ something("A.c", "bar");
+ }
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown_PreImage
new file mode 100644
index 0000000..e1ee19c
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftDown_PreImage
@@ -0,0 +1,68 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class D {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class E {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class F {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp.patch
new file mode 100644
index 0000000..aa994a1
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp.patch
@@ -0,0 +1,14 @@
+diff --git a/ShiftUp b/ShiftUp
+index e1ee19c..5c6e9bc 100644
+--- a/ShiftUp
++++ b/ShiftUp
+@@ -52,6 +52,9 @@
+ something("A.b", "bar");
+ }
+
++ public void methodC() {
++ something("A.c", "bar");
++ }
+ }
+
+ public class B {
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2.patch b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2.patch
new file mode 100644
index 0000000..eca9971
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2.patch
@@ -0,0 +1,23 @@
+diff --git a/ShiftUp2 b/ShiftUp2
+index e1ee19c..f010144 100644
+--- a/ShiftUp2
++++ b/ShiftUp2
+@@ -52,6 +52,9 @@
+ something("A.b", "bar");
+ }
+
++ public void methodC() {
++ something("A.c", "bar");
++ }
+ }
+
+ public class B {
+@@ -64,5 +67,8 @@
+ something("B.b", "bar");
+ }
+
++ public void methodC() {
++ something("B.c", "bar");
++ }
+ }
+ }
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2_PostImage
new file mode 100644
index 0000000..e279ece
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2_PostImage
@@ -0,0 +1,38 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ public void methodC() {
+ something("A.c", "bar");
+ }
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ public void methodC() {
+ something("B.c", "bar");
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2_PreImage
new file mode 100644
index 0000000..8b9727b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp2_PreImage
@@ -0,0 +1,32 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp_PostImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp_PostImage
new file mode 100644
index 0000000..25dc192
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp_PostImage
@@ -0,0 +1,35 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ public void methodC() {
+ something("A.c", "bar");
+ }
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp_PreImage b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp_PreImage
new file mode 100644
index 0000000..8b9727b
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/diff/ShiftUp_PreImage
@@ -0,0 +1,32 @@
+package org.eclipse.jgit.test.apply;
+
+public class TestClass {
+
+ private void something(String prefix, String msg) {
+ System.out.println(prefix + ": " + msg);
+ }
+
+ public class A {
+
+ public void methodA() {
+ something("A.a", "foo");
+ }
+
+ public void methodB() {
+ something("A.b", "bar");
+ }
+
+ }
+
+ public class B {
+
+ public void methodA() {
+ something("B.a", "foo");
+ }
+
+ public void methodB() {
+ something("B.b", "bar");
+ }
+
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
index 63cd21f..055eba7 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/ApplyCommandTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2012, IBM Corporation and others. and others
+ * Copyright (C) 2011, 2020 IBM Corporation and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -280,6 +280,46 @@
b.getString(0, b.size(), false));
}
+ @Test
+ public void testShiftUp() throws Exception {
+ ApplyResult result = init("ShiftUp");
+ assertEquals(1, result.getUpdatedFiles().size());
+ assertEquals(new File(db.getWorkTree(), "ShiftUp"),
+ result.getUpdatedFiles().get(0));
+ checkFile(new File(db.getWorkTree(), "ShiftUp"),
+ b.getString(0, b.size(), false));
+ }
+
+ @Test
+ public void testShiftUp2() throws Exception {
+ ApplyResult result = init("ShiftUp2");
+ assertEquals(1, result.getUpdatedFiles().size());
+ assertEquals(new File(db.getWorkTree(), "ShiftUp2"),
+ result.getUpdatedFiles().get(0));
+ checkFile(new File(db.getWorkTree(), "ShiftUp2"),
+ b.getString(0, b.size(), false));
+ }
+
+ @Test
+ public void testShiftDown() throws Exception {
+ ApplyResult result = init("ShiftDown");
+ assertEquals(1, result.getUpdatedFiles().size());
+ assertEquals(new File(db.getWorkTree(), "ShiftDown"),
+ result.getUpdatedFiles().get(0));
+ checkFile(new File(db.getWorkTree(), "ShiftDown"),
+ b.getString(0, b.size(), false));
+ }
+
+ @Test
+ public void testShiftDown2() throws Exception {
+ ApplyResult result = init("ShiftDown2");
+ assertEquals(1, result.getUpdatedFiles().size());
+ assertEquals(new File(db.getWorkTree(), "ShiftDown2"),
+ result.getUpdatedFiles().get(0));
+ checkFile(new File(db.getWorkTree(), "ShiftDown2"),
+ b.getString(0, b.size(), false));
+ }
+
private static byte[] readFile(String patchFile) throws IOException {
final InputStream in = getTestResource(patchFile);
if (in == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
index 0d4b3da..e228e82 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ApplyCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2012, IBM Corporation and others. and others
+ * Copyright (C) 2011, 2020 IBM Corporation and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -9,18 +9,15 @@
*/
package org.eclipse.jgit.api;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.api.errors.GitAPIException;
@@ -168,71 +165,156 @@
for (int i = 0; i < rt.size(); i++)
oldLines.add(rt.getString(i));
List<String> newLines = new ArrayList<>(oldLines);
+ int afterLastHunk = 0;
+ int lineNumberShift = 0;
+ int lastHunkNewLine = -1;
for (HunkHeader hh : fh.getHunks()) {
+ // We assume hunks to be ordered
+ if (hh.getNewStartLine() <= lastHunkNewLine) {
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ lastHunkNewLine = hh.getNewStartLine();
+
byte[] b = new byte[hh.getEndOffset() - hh.getStartOffset()];
System.arraycopy(hh.getBuffer(), hh.getStartOffset(), b, 0,
b.length);
RawText hrt = new RawText(b);
List<String> hunkLines = new ArrayList<>(hrt.size());
- for (int i = 0; i < hrt.size(); i++)
+ for (int i = 0; i < hrt.size(); i++) {
hunkLines.add(hrt.getString(i));
- int pos = 0;
- for (int j = 1; j < hunkLines.size(); j++) {
+ }
+
+ if (hh.getNewStartLine() == 0) {
+ // Must be the single hunk for clearing all content
+ if (fh.getHunks().size() == 1
+ && canApplyAt(hunkLines, newLines, 0)) {
+ newLines.clear();
+ break;
+ }
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ // Hunk lines as reported by the hunk may be off, so don't rely on
+ // them.
+ int applyAt = hh.getNewStartLine() - 1 + lineNumberShift;
+ // But they definitely should not go backwards.
+ if (applyAt < afterLastHunk && lineNumberShift < 0) {
+ applyAt = hh.getNewStartLine() - 1;
+ lineNumberShift = 0;
+ }
+ if (applyAt < afterLastHunk) {
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ boolean applies = false;
+ int oldLinesInHunk = hh.getLinesContext()
+ + hh.getOldImage().getLinesDeleted();
+ if (oldLinesInHunk <= 1) {
+ // Don't shift hunks without context lines. Just try the
+ // position corrected by the current lineNumberShift, and if
+ // that fails, the position recorded in the hunk header.
+ applies = canApplyAt(hunkLines, newLines, applyAt);
+ if (!applies && lineNumberShift != 0) {
+ applyAt = hh.getNewStartLine() - 1;
+ applies = applyAt >= afterLastHunk
+ && canApplyAt(hunkLines, newLines, applyAt);
+ }
+ } else {
+ int maxShift = applyAt - afterLastHunk;
+ for (int shift = 0; shift <= maxShift; shift++) {
+ if (canApplyAt(hunkLines, newLines, applyAt - shift)) {
+ applies = true;
+ applyAt -= shift;
+ break;
+ }
+ }
+ if (!applies) {
+ // Try shifting the hunk downwards
+ applyAt = hh.getNewStartLine() - 1 + lineNumberShift;
+ maxShift = newLines.size() - applyAt - oldLinesInHunk;
+ for (int shift = 1; shift <= maxShift; shift++) {
+ if (canApplyAt(hunkLines, newLines, applyAt + shift)) {
+ applies = true;
+ applyAt += shift;
+ break;
+ }
+ }
+ }
+ }
+ if (!applies) {
+ throw new PatchApplyException(MessageFormat
+ .format(JGitText.get().patchApplyException, hh));
+ }
+ // Hunk applies at applyAt. Apply it, and update afterLastHunk and
+ // lineNumberShift
+ lineNumberShift = applyAt - hh.getNewStartLine() + 1;
+ int sz = hunkLines.size();
+ for (int j = 1; j < sz; j++) {
String hunkLine = hunkLines.get(j);
switch (hunkLine.charAt(0)) {
case ' ':
- if (!newLines.get(hh.getNewStartLine() - 1 + pos).equals(
- hunkLine.substring(1))) {
- throw new PatchApplyException(MessageFormat.format(
- JGitText.get().patchApplyException, hh));
- }
- pos++;
+ applyAt++;
break;
case '-':
- if (hh.getNewStartLine() == 0) {
- newLines.clear();
- } else {
- if (!newLines.get(hh.getNewStartLine() - 1 + pos)
- .equals(hunkLine.substring(1))) {
- throw new PatchApplyException(MessageFormat.format(
- JGitText.get().patchApplyException, hh));
- }
- newLines.remove(hh.getNewStartLine() - 1 + pos);
- }
+ newLines.remove(applyAt);
break;
case '+':
- newLines.add(hh.getNewStartLine() - 1 + pos,
- hunkLine.substring(1));
- pos++;
+ newLines.add(applyAt++, hunkLine.substring(1));
+ break;
+ default:
break;
}
}
+ afterLastHunk = applyAt;
}
- if (!isNoNewlineAtEndOfFile(fh))
+ if (!isNoNewlineAtEndOfFile(fh)) {
newLines.add(""); //$NON-NLS-1$
- if (!rt.isMissingNewlineAtEnd())
+ }
+ if (!rt.isMissingNewlineAtEnd()) {
oldLines.add(""); //$NON-NLS-1$
- if (!isChanged(oldLines, newLines))
- return; // don't touch the file
- StringBuilder sb = new StringBuilder();
- for (String l : newLines) {
- // don't bother handling line endings - if it was windows, the \r is
- // still there!
- sb.append(l).append('\n');
}
- if (sb.length() > 0) {
- sb.deleteCharAt(sb.length() - 1);
+ if (!isChanged(oldLines, newLines)) {
+ return; // Don't touch the file
}
- try (Writer fw = new OutputStreamWriter(new FileOutputStream(f),
- UTF_8)) {
- fw.write(sb.toString());
+ try (Writer fw = Files.newBufferedWriter(f.toPath())) {
+ for (Iterator<String> l = newLines.iterator(); l.hasNext();) {
+ fw.write(l.next());
+ if (l.hasNext()) {
+ // Don't bother handling line endings - if it was Windows,
+ // the \r is still there!
+ fw.write('\n');
+ }
+ }
}
-
getRepository().getFS().setExecute(f, fh.getNewMode() == FileMode.EXECUTABLE_FILE);
}
+ private boolean canApplyAt(List<String> hunkLines, List<String> newLines,
+ int line) {
+ int sz = hunkLines.size();
+ int limit = newLines.size();
+ int pos = line;
+ for (int j = 1; j < sz; j++) {
+ String hunkLine = hunkLines.get(j);
+ switch (hunkLine.charAt(0)) {
+ case ' ':
+ case '-':
+ if (pos >= limit
+ || !newLines.get(pos).equals(hunkLine.substring(1))) {
+ return false;
+ }
+ pos++;
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+ }
+
private static boolean isChanged(List<String> ol, List<String> nl) {
if (ol.size() != nl.size())
return true;