Refactor diff sequence API

Instead of making the sequence itself responsible for the equivalence
function, use an external function that is supplied by the caller.
This cleans up the code because we now say cmp.equals(a, ai, b, bi)
instead of a.equals(ai, b, bi).

This refactoring also removes the odd concept of creating different
types of sequences to have different behaviors for whitespace
ignoring.  Instead DiffComparator now supports singleton functions
that apply a particular equivalence algorithm to a type of sequence.

Change-Id: I559f494d81cdc6f06bfb4208f60780c0ae251df9
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Shawn O. Pearce 2010-09-02 12:53:27 -07:00 committed by Chris Aniszczyk
parent 18aadc826d
commit 67263e2056
26 changed files with 573 additions and 688 deletions

View File

@ -77,6 +77,7 @@
import org.eclipse.jgit.diff.EditList; import org.eclipse.jgit.diff.EditList;
import org.eclipse.jgit.diff.MyersDiff; import org.eclipse.jgit.diff.MyersDiff;
import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.iplog.Committer.ActiveRange; import org.eclipse.jgit.iplog.Committer.ActiveRange;
import org.eclipse.jgit.lib.BlobBasedConfig; import org.eclipse.jgit.lib.BlobBasedConfig;
@ -383,7 +384,8 @@ private void scanProjectCommits(Project proj, RevCommit start)
else else
oldImage = new byte[0]; oldImage = new byte[0];
EditList edits = new MyersDiff(new RawText(oldImage), EditList edits = new MyersDiff<RawText>(
RawTextComparator.DEFAULT, new RawText(oldImage),
new RawText(openBlob(1))).getEdits(); new RawText(openBlob(1))).getEdits();
for (Edit e : edits) for (Edit e : edits)
addedLines += e.getEndB() - e.getBeginB(); addedLines += e.getEndB() - e.getBeginB();

View File

@ -55,10 +55,7 @@
import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawTextIgnoreAllWhitespace; import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.diff.RawTextIgnoreLeadingWhitespace;
import org.eclipse.jgit.diff.RawTextIgnoreTrailingWhitespace;
import org.eclipse.jgit.diff.RawTextIgnoreWhitespaceChange;
import org.eclipse.jgit.diff.RenameDetector; import org.eclipse.jgit.diff.RenameDetector;
import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
@ -109,22 +106,22 @@ void noRenames(@SuppressWarnings("unused") boolean on) {
@Option(name = "--ignore-space-at-eol") @Option(name = "--ignore-space-at-eol")
void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) { void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreTrailingWhitespace.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_TRAILING);
} }
@Option(name = "--ignore-leading-space") @Option(name = "--ignore-leading-space")
void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) { void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreLeadingWhitespace.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_LEADING);
} }
@Option(name = "-b", aliases = { "--ignore-space-change" }) @Option(name = "-b", aliases = { "--ignore-space-change" })
void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) { void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreWhitespaceChange.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_CHANGE);
} }
@Option(name = "-w", aliases = { "--ignore-all-space" }) @Option(name = "-w", aliases = { "--ignore-all-space" })
void ignoreAllSpace(@SuppressWarnings("unused") boolean on) { void ignoreAllSpace(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreAllWhitespace.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
} }
@Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext") @Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext")

View File

@ -58,10 +58,7 @@
import java.util.TimeZone; import java.util.TimeZone;
import org.eclipse.jgit.diff.DiffFormatter; import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawTextIgnoreAllWhitespace; import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.diff.RawTextIgnoreLeadingWhitespace;
import org.eclipse.jgit.diff.RawTextIgnoreTrailingWhitespace;
import org.eclipse.jgit.diff.RawTextIgnoreWhitespaceChange;
import org.eclipse.jgit.diff.RenameDetector; import org.eclipse.jgit.diff.RenameDetector;
import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
@ -106,22 +103,22 @@ void noRenames(@SuppressWarnings("unused") boolean on) {
@Option(name = "--ignore-space-at-eol") @Option(name = "--ignore-space-at-eol")
void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) { void ignoreSpaceAtEol(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreTrailingWhitespace.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_TRAILING);
} }
@Option(name = "--ignore-leading-space") @Option(name = "--ignore-leading-space")
void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) { void ignoreLeadingSpace(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreLeadingWhitespace.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_LEADING);
} }
@Option(name = "-b", aliases = { "--ignore-space-change" }) @Option(name = "-b", aliases = { "--ignore-space-change" })
void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) { void ignoreSpaceChange(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreWhitespaceChange.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_CHANGE);
} }
@Option(name = "-w", aliases = { "--ignore-all-space" }) @Option(name = "-w", aliases = { "--ignore-all-space" })
void ignoreAllSpace(@SuppressWarnings("unused") boolean on) { void ignoreAllSpace(@SuppressWarnings("unused") boolean on) {
diffFmt.setRawTextFactory(RawTextIgnoreAllWhitespace.FACTORY); diffFmt.setDiffComparator(RawTextComparator.WS_IGNORE_ALL);
} }
@Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext") @Option(name = "-U", aliases = { "--unified" }, metaVar = "metaVar_linesOfContext")

View File

@ -163,14 +163,15 @@ private PerfData test(int characters) {
String b = DiffTestDataGenerator.generateSequence(characters, 1621, 5); String b = DiffTestDataGenerator.generateSequence(characters, 1621, 5);
CharArray ac = new CharArray(a); CharArray ac = new CharArray(a);
CharArray bc = new CharArray(b); CharArray bc = new CharArray(b);
MyersDiff myersDiff = null; CharCmp cmp = new CharCmp();
MyersDiff<CharArray> myersDiff = null;
int cpuTimeChanges = 0; int cpuTimeChanges = 0;
long lastReadout = 0; long lastReadout = 0;
long interimTime = 0; long interimTime = 0;
int repetitions = 0; int repetitions = 0;
stopwatch.start(); stopwatch.start();
while (cpuTimeChanges < minCPUTimerTicks && interimTime < longTaskBoundary) { while (cpuTimeChanges < minCPUTimerTicks && interimTime < longTaskBoundary) {
myersDiff = new MyersDiff(ac, bc); myersDiff = new MyersDiff<CharArray>(cmp, ac, bc);
repetitions++; repetitions++;
interimTime = stopwatch.readout(); interimTime = stopwatch.readout();
if (interimTime != lastReadout) { if (interimTime != lastReadout) {
@ -179,26 +180,34 @@ private PerfData test(int characters) {
} }
} }
ret.runningTime = stopwatch.stop() / repetitions; ret.runningTime = stopwatch.stop() / repetitions;
ret.N = (ac.size() + bc.size()); ret.N = ac.size() + bc.size();
ret.D = myersDiff.getEdits().size(); ret.D = myersDiff.getEdits().size();
return ret; return ret;
} }
private static class CharArray implements Sequence { private static class CharArray extends Sequence {
private final char[] array; final char[] array;
public CharArray(String s) { public CharArray(String s) {
array = s.toCharArray(); array = s.toCharArray();
} }
@Override
public int size() { public int size() {
return array.length; return array.length;
} }
}
public boolean equals(int i, Sequence other, int j) { private static class CharCmp extends SequenceComparator<CharArray> {
CharArray o = (CharArray) other; @Override
return array[i] == o.array[j]; public boolean equals(CharArray a, int ai, CharArray b, int bi) {
return a.array[ai] == b.array[bi];
}
@Override
public int hash(CharArray seq, int ptr) {
return seq.array[ptr];
} }
} }
} }

View File

@ -63,7 +63,8 @@ public void testSimple() {
} }
public void assertDiff(String a, String b, String edits) { public void assertDiff(String a, String b, String edits) {
MyersDiff diff = new MyersDiff(toCharArray(a), toCharArray(b)); MyersDiff diff = new MyersDiff<CharArray>(new CharCmp(),
toCharArray(a), toCharArray(b));
assertEquals(edits, toString(diff.getEdits())); assertEquals(edits, toString(diff.getEdits()));
} }
@ -80,8 +81,7 @@ private static CharArray toCharArray(String s) {
return new CharArray(s); return new CharArray(s);
} }
protected static String toString(Sequence seq, int begin, int end) { protected static String toString(CharArray a, int begin, int end) {
CharArray a = (CharArray)seq;
return new String(a.array, begin, end - begin); return new String(a.array, begin, end - begin);
} }
@ -97,13 +97,21 @@ protected static String toString(CharArray a, CharArray b,
+ ")"; + ")";
} }
private static class CharArray implements Sequence { private static class CharArray extends Sequence {
char[] array; char[] array;
public CharArray(String s) { array = s.toCharArray(); } public CharArray(String s) { array = s.toCharArray(); }
public int size() { return array.length; } public int size() { return array.length; }
public boolean equals(int i, Sequence other, int j) { }
CharArray o = (CharArray)other;
return array[i] == o.array[j]; private static class CharCmp extends SequenceComparator<CharArray> {
@Override
public boolean equals(CharArray a, int ai, CharArray b, int bi) {
return a.array[ai] == b.array[bi];
}
@Override
public int hash(CharArray seq, int ptr) {
return seq.array[ptr];
} }
} }
} }

View File

@ -49,48 +49,50 @@
import junit.framework.TestCase; import junit.framework.TestCase;
public class RawTextIgnoreAllWhitespaceTest extends TestCase { public class RawTextIgnoreAllWhitespaceTest extends TestCase {
private final RawTextComparator cmp = RawTextComparator.WS_IGNORE_ALL;
public void testEqualsWithoutWhitespace() { public void testEqualsWithoutWhitespace() {
final RawText a = new RawTextIgnoreAllWhitespace(Constants final RawText a = new RawText(cmp, Constants
.encodeASCII("foo-a\nfoo-b\nfoo\n")); .encodeASCII("foo-a\nfoo-b\nfoo\n"));
final RawText b = new RawTextIgnoreAllWhitespace(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-b\nfoo-c\nf\n")); .encodeASCII("foo-b\nfoo-c\nf\n"));
assertEquals(3, a.size()); assertEquals(3, a.size());
assertEquals(3, b.size()); assertEquals(3, b.size());
// foo-a != foo-b // foo-a != foo-b
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// foo-b == foo-b // foo-b == foo-b
assertTrue(a.equals(1, b, 0)); assertTrue(cmp.equals(a, 1, b, 0));
assertTrue(b.equals(0, a, 1)); assertTrue(cmp.equals(b, 0, a, 1));
// foo != f // foo != f
assertFalse(a.equals(2, b, 2)); assertFalse(cmp.equals(a, 2, b, 2));
assertFalse(b.equals(2, a, 2)); assertFalse(cmp.equals(b, 2, a, 2));
} }
public void testEqualsWithWhitespace() { public void testEqualsWithWhitespace() {
final RawText a = new RawTextIgnoreAllWhitespace(Constants final RawText a = new RawText(cmp, Constants
.encodeASCII("foo-a\n \n a b c\na \n")); .encodeASCII("foo-a\n \n a b c\na \n"));
final RawText b = new RawTextIgnoreAllWhitespace(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-a b\n\nab c\na\n")); .encodeASCII("foo-a b\n\nab c\na\n"));
// "foo-a" != "foo-a b" // "foo-a" != "foo-a b"
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// " " == "" // " " == ""
assertTrue(a.equals(1, b, 1)); assertTrue(cmp.equals(a, 1, b, 1));
assertTrue(b.equals(1, a, 1)); assertTrue(cmp.equals(b, 1, a, 1));
// " a b c" == "ab c" // " a b c" == "ab c"
assertTrue(a.equals(2, b, 2)); assertTrue(cmp.equals(a, 2, b, 2));
assertTrue(b.equals(2, a, 2)); assertTrue(cmp.equals(b, 2, a, 2));
// "a " == "a" // "a " == "a"
assertTrue(a.equals(3, b, 3)); assertTrue(cmp.equals(a, 3, b, 3));
assertTrue(b.equals(3, a, 3)); assertTrue(cmp.equals(b, 3, a, 3));
} }
} }

View File

@ -49,52 +49,54 @@
import junit.framework.TestCase; import junit.framework.TestCase;
public class RawTextIgnoreLeadingWhitespaceTest extends TestCase { public class RawTextIgnoreLeadingWhitespaceTest extends TestCase {
private final RawTextComparator cmp = RawTextComparator.WS_IGNORE_LEADING;
public void testEqualsWithoutWhitespace() { public void testEqualsWithoutWhitespace() {
final RawText a = new RawTextIgnoreLeadingWhitespace(Constants final RawText a = new RawText(cmp, Constants
.encodeASCII("foo-a\nfoo-b\nfoo\n")); .encodeASCII("foo-a\nfoo-b\nfoo\n"));
final RawText b = new RawTextIgnoreLeadingWhitespace(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-b\nfoo-c\nf\n")); .encodeASCII("foo-b\nfoo-c\nf\n"));
assertEquals(3, a.size()); assertEquals(3, a.size());
assertEquals(3, b.size()); assertEquals(3, b.size());
// foo-a != foo-b // foo-a != foo-b
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// foo-b == foo-b // foo-b == foo-b
assertTrue(a.equals(1, b, 0)); assertTrue(cmp.equals(a, 1, b, 0));
assertTrue(b.equals(0, a, 1)); assertTrue(cmp.equals(b, 0, a, 1));
// foo != f // foo != f
assertFalse(a.equals(2, b, 2)); assertFalse(cmp.equals(a, 2, b, 2));
assertFalse(b.equals(2, a, 2)); assertFalse(cmp.equals(b, 2, a, 2));
} }
public void testEqualsWithWhitespace() { public void testEqualsWithWhitespace() {
final RawText a = new RawTextIgnoreLeadingWhitespace(Constants final RawText a = new RawText(cmp, Constants
.encodeASCII("foo-a\n \n a b c\n a\nb \n")); .encodeASCII("foo-a\n \n a b c\n a\nb \n"));
final RawText b = new RawTextIgnoreLeadingWhitespace(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-a b\n\nab c\na\nb\n")); .encodeASCII("foo-a b\n\nab c\na\nb\n"));
// "foo-a" != "foo-a b" // "foo-a" != "foo-a b"
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// " " == "" // " " == ""
assertTrue(a.equals(1, b, 1)); assertTrue(cmp.equals(a, 1, b, 1));
assertTrue(b.equals(1, a, 1)); assertTrue(cmp.equals(b, 1, a, 1));
// " a b c" != "ab c" // " a b c" != "ab c"
assertFalse(a.equals(2, b, 2)); assertFalse(cmp.equals(a, 2, b, 2));
assertFalse(b.equals(2, a, 2)); assertFalse(cmp.equals(b, 2, a, 2));
// " a" == "a" // " a" == "a"
assertTrue(a.equals(3, b, 3)); assertTrue(cmp.equals(a, 3, b, 3));
assertTrue(b.equals(3, a, 3)); assertTrue(cmp.equals(b, 3, a, 3));
// "b " != "b" // "b " != "b"
assertFalse(a.equals(4, b, 4)); assertFalse(cmp.equals(a, 4, b, 4));
assertFalse(b.equals(4, a, 4)); assertFalse(cmp.equals(b, 4, a, 4));
} }
} }

View File

@ -49,52 +49,54 @@
import junit.framework.TestCase; import junit.framework.TestCase;
public class RawTextIgnoreTrailingWhitespaceTest extends TestCase { public class RawTextIgnoreTrailingWhitespaceTest extends TestCase {
private final RawTextComparator cmp = RawTextComparator.WS_IGNORE_TRAILING;
public void testEqualsWithoutWhitespace() { public void testEqualsWithoutWhitespace() {
final RawText a = new RawTextIgnoreTrailingWhitespace(Constants final RawText a = new RawText(cmp, Constants
.encodeASCII("foo-a\nfoo-b\nfoo\n")); .encodeASCII("foo-a\nfoo-b\nfoo\n"));
final RawText b = new RawTextIgnoreTrailingWhitespace(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-b\nfoo-c\nf\n")); .encodeASCII("foo-b\nfoo-c\nf\n"));
assertEquals(3, a.size()); assertEquals(3, a.size());
assertEquals(3, b.size()); assertEquals(3, b.size());
// foo-a != foo-b // foo-a != foo-b
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// foo-b == foo-b // foo-b == foo-b
assertTrue(a.equals(1, b, 0)); assertTrue(cmp.equals(a, 1, b, 0));
assertTrue(b.equals(0, a, 1)); assertTrue(cmp.equals(b, 0, a, 1));
// foo != f // foo != f
assertFalse(a.equals(2, b, 2)); assertFalse(cmp.equals(a, 2, b, 2));
assertFalse(b.equals(2, a, 2)); assertFalse(cmp.equals(b, 2, a, 2));
} }
public void testEqualsWithWhitespace() { public void testEqualsWithWhitespace() {
final RawText a = new RawTextIgnoreTrailingWhitespace(Constants final RawText a = new RawText(cmp, Constants
.encodeASCII("foo-a\n \n a b c\na \n b\n")); .encodeASCII("foo-a\n \n a b c\na \n b\n"));
final RawText b = new RawTextIgnoreTrailingWhitespace(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-a b\n\nab c\na\nb\n")); .encodeASCII("foo-a b\n\nab c\na\nb\n"));
// "foo-a" != "foo-a b" // "foo-a" != "foo-a b"
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// " " == "" // " " == ""
assertTrue(a.equals(1, b, 1)); assertTrue(cmp.equals(a, 1, b, 1));
assertTrue(b.equals(1, a, 1)); assertTrue(cmp.equals(b, 1, a, 1));
// " a b c" != "ab c" // " a b c" != "ab c"
assertFalse(a.equals(2, b, 2)); assertFalse(cmp.equals(a, 2, b, 2));
assertFalse(b.equals(2, a, 2)); assertFalse(cmp.equals(b, 2, a, 2));
// "a " == "a" // "a " == "a"
assertTrue(a.equals(3, b, 3)); assertTrue(cmp.equals(a, 3, b, 3));
assertTrue(b.equals(3, a, 3)); assertTrue(cmp.equals(b, 3, a, 3));
// " b" != "b" // " b" != "b"
assertFalse(a.equals(4, b, 4)); assertFalse(cmp.equals(a, 4, b, 4));
assertFalse(b.equals(4, a, 4)); assertFalse(cmp.equals(b, 4, a, 4));
} }
} }

View File

@ -49,57 +49,58 @@
import junit.framework.TestCase; import junit.framework.TestCase;
public class RawTextIgnoreWhitespaceChangeTest extends TestCase { public class RawTextIgnoreWhitespaceChangeTest extends TestCase {
private final RawTextComparator cmp = RawTextComparator.WS_IGNORE_CHANGE;
public void testEqualsWithoutWhitespace() { public void testEqualsWithoutWhitespace() {
final RawText a = new RawTextIgnoreWhitespaceChange(Constants final RawText a = new RawText(cmp, Constants
.encodeASCII("foo-a\nfoo-b\nfoo\n")); .encodeASCII("foo-a\nfoo-b\nfoo\n"));
final RawText b = new RawTextIgnoreWhitespaceChange(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-b\nfoo-c\nf\n")); .encodeASCII("foo-b\nfoo-c\nf\n"));
assertEquals(3, a.size()); assertEquals(3, a.size());
assertEquals(3, b.size()); assertEquals(3, b.size());
// foo-a != foo-b // foo-a != foo-b
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// foo-b == foo-b // foo-b == foo-b
assertTrue(a.equals(1, b, 0)); assertTrue(cmp.equals(a, 1, b, 0));
assertTrue(b.equals(0, a, 1)); assertTrue(cmp.equals(b, 0, a, 1));
// foo != f // foo != f
assertFalse(a.equals(2, b, 2)); assertFalse(cmp.equals(a, 2, b, 2));
assertFalse(b.equals(2, a, 2)); assertFalse(cmp.equals(b, 2, a, 2));
} }
public void testEqualsWithWhitespace() { public void testEqualsWithWhitespace() {
final RawText a = new RawTextIgnoreWhitespaceChange( final RawText a = new RawText(cmp, Constants
Constants
.encodeASCII("foo-a\n \n a b c\na \n foo\na b c\n")); .encodeASCII("foo-a\n \n a b c\na \n foo\na b c\n"));
final RawText b = new RawTextIgnoreWhitespaceChange(Constants final RawText b = new RawText(cmp, Constants
.encodeASCII("foo-a b\n\nab c\na\nfoo\na b c \n")); .encodeASCII("foo-a b\n\nab c\na\nfoo\na b c \n"));
// "foo-a" != "foo-a b" // "foo-a" != "foo-a b"
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// " " == "" // " " == ""
assertTrue(a.equals(1, b, 1)); assertTrue(cmp.equals(a, 1, b, 1));
assertTrue(b.equals(1, a, 1)); assertTrue(cmp.equals(b, 1, a, 1));
// " a b c" != "ab c" // " a b c" != "ab c"
assertFalse(a.equals(2, b, 2)); assertFalse(cmp.equals(a, 2, b, 2));
assertFalse(b.equals(2, a, 2)); assertFalse(cmp.equals(b, 2, a, 2));
// "a " == "a" // "a " == "a"
assertTrue(a.equals(3, b, 3)); assertTrue(cmp.equals(a, 3, b, 3));
assertTrue(b.equals(3, a, 3)); assertTrue(cmp.equals(b, 3, a, 3));
// " foo" != "foo" // " foo" != "foo"
assertFalse(a.equals(4, b, 4)); assertFalse(cmp.equals(a, 4, b, 4));
assertFalse(b.equals(4, a, 4)); assertFalse(cmp.equals(b, 4, a, 4));
// "a b c" == "a b c " // "a b c" == "a b c "
assertTrue(a.equals(5, b, 5)); assertTrue(cmp.equals(a, 5, b, 5));
assertTrue(b.equals(5, a, 5)); assertTrue(cmp.equals(b, 5, a, 5));
} }
} }

View File

@ -61,17 +61,18 @@ public void testEmpty() {
public void testEquals() { public void testEquals() {
final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b\n")); final RawText a = new RawText(Constants.encodeASCII("foo-a\nfoo-b\n"));
final RawText b = new RawText(Constants.encodeASCII("foo-b\nfoo-c\n")); final RawText b = new RawText(Constants.encodeASCII("foo-b\nfoo-c\n"));
RawTextComparator cmp = RawTextComparator.DEFAULT;
assertEquals(2, a.size()); assertEquals(2, a.size());
assertEquals(2, b.size()); assertEquals(2, b.size());
// foo-a != foo-b // foo-a != foo-b
assertFalse(a.equals(0, b, 0)); assertFalse(cmp.equals(a, 0, b, 0));
assertFalse(b.equals(0, a, 0)); assertFalse(cmp.equals(b, 0, a, 0));
// foo-b == foo-b // foo-b == foo-b
assertTrue(a.equals(1, b, 0)); assertTrue(cmp.equals(a, 1, b, 0));
assertTrue(b.equals(0, a, 1)); assertTrue(cmp.equals(b, 0, a, 1));
} }
public void testWriteLine1() throws IOException { public void testWriteLine1() throws IOException {

View File

@ -49,6 +49,7 @@
import junit.framework.TestCase; import junit.framework.TestCase;
import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
public class MergeAlgorithmTest extends TestCase { public class MergeAlgorithmTest extends TestCase {
@ -190,7 +191,7 @@ public void testSameModification() throws IOException {
} }
private String merge(String commonBase, String ours, String theirs) throws IOException { private String merge(String commonBase, String ours, String theirs) throws IOException {
MergeResult r=MergeAlgorithm.merge(new RawText(Constants.encode(commonBase)), new RawText(Constants.encode(ours)), new RawText(Constants.encode(theirs))); MergeResult r=MergeAlgorithm.merge(RawTextComparator.DEFAULT, new RawText(Constants.encode(commonBase)), new RawText(Constants.encode(ours)), new RawText(Constants.encode(theirs)));
ByteArrayOutputStream bo=new ByteArrayOutputStream(50); ByteArrayOutputStream bo=new ByteArrayOutputStream(50);
fmt.formatMerge(bo, r, "B", "O", "T", Constants.CHARACTER_ENCODING); fmt.formatMerge(bo, r, "B", "O", "T", Constants.CHARACTER_ENCODING);
return new String(bo.toByteArray(), Constants.CHARACTER_ENCODING); return new String(bo.toByteArray(), Constants.CHARACTER_ENCODING);

View File

@ -173,7 +173,7 @@ public MergeResult call() throws NoHeadException,
ThreeWayMerger merger = (ThreeWayMerger) mergeStrategy ThreeWayMerger merger = (ThreeWayMerger) mergeStrategy
.newMerger(repo); .newMerger(repo);
boolean noProblems; boolean noProblems;
Map<String, org.eclipse.jgit.merge.MergeResult> lowLevelResults = null; Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults = null;
Map<String, MergeFailureReason> failingPaths = null; Map<String, MergeFailureReason> failingPaths = null;
if (merger instanceof ResolveMerger) { if (merger instanceof ResolveMerger) {
ResolveMerger resolveMerger = (ResolveMerger) merger; ResolveMerger resolveMerger = (ResolveMerger) merger;

View File

@ -134,7 +134,7 @@ public String toString() {
*/ */
public MergeResult(ObjectId newHead, ObjectId base, public MergeResult(ObjectId newHead, ObjectId base,
ObjectId[] mergedCommits, MergeStatus mergeStatus, ObjectId[] mergedCommits, MergeStatus mergeStatus,
Map<String, org.eclipse.jgit.merge.MergeResult> lowLevelResults, Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults,
MergeStrategy mergeStrategy) { MergeStrategy mergeStrategy) {
this(newHead, base, mergedCommits, mergeStatus, mergeStrategy, lowLevelResults, null); this(newHead, base, mergedCommits, mergeStatus, mergeStrategy, lowLevelResults, null);
} }
@ -160,7 +160,7 @@ public MergeResult(ObjectId newHead, ObjectId base,
public MergeResult(ObjectId newHead, ObjectId base, public MergeResult(ObjectId newHead, ObjectId base,
ObjectId[] mergedCommits, MergeStatus mergeStatus, ObjectId[] mergedCommits, MergeStatus mergeStatus,
MergeStrategy mergeStrategy, MergeStrategy mergeStrategy,
Map<String, org.eclipse.jgit.merge.MergeResult> lowLevelResults, Map<String, org.eclipse.jgit.merge.MergeResult<?>> lowLevelResults,
String description) { String description) {
this.newHead = newHead; this.newHead = newHead;
this.mergedCommits = mergedCommits; this.mergedCommits = mergedCommits;
@ -243,7 +243,7 @@ public void addConflict(String path, int[][] conflictingRanges) {
* @param path * @param path
* @param lowLevelResult * @param lowLevelResult
*/ */
public void addConflict(String path, org.eclipse.jgit.merge.MergeResult lowLevelResult) { public void addConflict(String path, org.eclipse.jgit.merge.MergeResult<?> lowLevelResult) {
if (conflicts == null) if (conflicts == null)
conflicts = new HashMap<String, int[][]>(); conflicts = new HashMap<String, int[][]>();
int nrOfConflicts = 0; int nrOfConflicts = 0;

View File

@ -78,8 +78,8 @@
import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.patch.FileHeader; import org.eclipse.jgit.patch.FileHeader;
import org.eclipse.jgit.patch.HunkHeader;
import org.eclipse.jgit.patch.FileHeader.PatchType; import org.eclipse.jgit.patch.FileHeader.PatchType;
import org.eclipse.jgit.patch.HunkHeader;
import org.eclipse.jgit.revwalk.FollowFilter; import org.eclipse.jgit.revwalk.FollowFilter;
import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
@ -118,7 +118,7 @@ public class DiffFormatter {
private int abbreviationLength = 7; private int abbreviationLength = 7;
private RawText.Factory rawTextFactory = RawText.FACTORY; private RawTextComparator comparator = RawTextComparator.DEFAULT;
private int binaryFileThreshold = DEFAULT_BINARY_FILE_THRESHOLD; private int binaryFileThreshold = DEFAULT_BINARY_FILE_THRESHOLD;
@ -207,20 +207,20 @@ public void setAbbreviationLength(final int count) {
} }
/** /**
* Set the helper that constructs difference output. * Set the line equivalence function for text file differences.
* *
* @param type * @param cmp
* the factory to create different output. Different types of * The equivalence function used to determine if two lines of
* factories can produce different whitespace behavior, for * text are identical. The function can be changed to ignore
* example. * various types of whitespace.
* @see RawText#FACTORY * @see RawTextComparator#DEFAULT
* @see RawTextIgnoreAllWhitespace#FACTORY * @see RawTextComparator#WS_IGNORE_ALL
* @see RawTextIgnoreLeadingWhitespace#FACTORY * @see RawTextComparator#WS_IGNORE_CHANGE
* @see RawTextIgnoreTrailingWhitespace#FACTORY * @see RawTextComparator#WS_IGNORE_LEADING
* @see RawTextIgnoreWhitespaceChange#FACTORY * @see RawTextComparator#WS_IGNORE_TRAILING
*/ */
public void setRawTextFactory(RawText.Factory type) { public void setDiffComparator(RawTextComparator cmp) {
rawTextFactory = type; comparator = cmp;
} }
/** /**
@ -869,9 +869,9 @@ private FormatResult createFormatResult(DiffEntry ent) throws IOException,
type = PatchType.BINARY; type = PatchType.BINARY;
} else { } else {
res.a = rawTextFactory.create(aRaw); res.a = new RawText(comparator, aRaw);
res.b = rawTextFactory.create(bRaw); res.b = new RawText(comparator, bRaw);
editList = new MyersDiff(res.a, res.b).getEdits(); editList = diff(res.a, res.b);
type = PatchType.UNIFIED; type = PatchType.UNIFIED;
switch (ent.getChangeType()) { switch (ent.getChangeType()) {
@ -892,6 +892,10 @@ private FormatResult createFormatResult(DiffEntry ent) throws IOException,
return res; return res;
} }
private EditList diff(RawText a, RawText b) {
return new MyersDiff<RawText>(comparator, a, b).getEdits();
}
private void assertHaveRepository() { private void assertHaveRepository() {
if (db == null) if (db == null)
throw new IllegalStateException(JGitText.get().repositoryIsRequired); throw new IllegalStateException(JGitText.get().repositoryIsRequired);

View File

@ -101,30 +101,38 @@
* *
* So the overall runtime complexity stays the same with linear space, * So the overall runtime complexity stays the same with linear space,
* albeit with a larger constant factor. * albeit with a larger constant factor.
*
* @param <S>
* type of sequence.
*/ */
public class MyersDiff { public class MyersDiff<S extends Sequence> {
/** /**
* The list of edits found during the last call to {@link #calculateEdits()} * The list of edits found during the last call to {@link #calculateEdits()}
*/ */
protected EditList edits; protected EditList edits;
/** Comparison function for sequences. */
protected SequenceComparator<S> cmp;
/** /**
* The first text to be compared. Referred to as "Text A" in the comments * The first text to be compared. Referred to as "Text A" in the comments
*/ */
protected Sequence a; protected S a;
/** /**
* The second text to be compared. Referred to as "Text B" in the comments * The second text to be compared. Referred to as "Text B" in the comments
*/ */
protected Sequence b; protected S b;
/** /**
* The only constructor * The only constructor
* *
* @param cmp comparison method for this execution.
* @param a the text A which should be compared * @param a the text A which should be compared
* @param b the text B which should be compared * @param b the text B which should be compared
*/ */
public MyersDiff(Sequence a, Sequence b) { public MyersDiff(SequenceComparator<S> cmp, S a, S b) {
this.cmp = cmp;
this.a = a; this.a = a;
this.b = b; this.b = b;
calculateEdits(); calculateEdits();
@ -436,7 +444,7 @@ boolean calculate(int d) {
class ForwardEditPaths extends EditPaths { class ForwardEditPaths extends EditPaths {
final int snake(int k, int x) { final int snake(int k, int x) {
for (; x < endA && k + x < endB; x++) for (; x < endA && k + x < endB; x++)
if (!a.equals(x, b, k + x)) if (!cmp.equals(a, x, b, k + x))
break; break;
return x; return x;
} }
@ -478,7 +486,7 @@ final boolean meets(int d, int k, int x, long snake) {
class BackwardEditPaths extends EditPaths { class BackwardEditPaths extends EditPaths {
final int snake(int k, int x) { final int snake(int k, int x) {
for (; x > beginA && k + x > beginB; x--) for (; x > beginA && k + x > beginB; x--)
if (!a.equals(x - 1, b, k + x - 1)) if (!cmp.equals(a, x - 1, b, k + x - 1))
break; break;
return x; return x;
} }
@ -529,7 +537,7 @@ public static void main(String[] args) {
try { try {
RawText a = new RawText(new java.io.File(args[0])); RawText a = new RawText(new java.io.File(args[0]));
RawText b = new RawText(new java.io.File(args[1])); RawText b = new RawText(new java.io.File(args[1]));
MyersDiff diff = new MyersDiff(a, b); MyersDiff diff = new MyersDiff(RawTextComparator.DEFAULT, a, b);
System.out.println(diff.getEdits().toString()); System.out.println(diff.getEdits().toString());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -65,26 +65,7 @@
* line number 1. Callers may need to subtract 1 prior to invoking methods if * line number 1. Callers may need to subtract 1 prior to invoking methods if
* they are converting from "line number" to "element index". * they are converting from "line number" to "element index".
*/ */
public class RawText implements Sequence { public class RawText extends Sequence {
/** Creates a RawText instance. */
public static interface Factory {
/**
* Construct a RawText instance for the content.
*
* @param input
* the content array.
* @return a RawText instance wrapping this content.
*/
RawText create(byte[] input);
}
/** Creates RawText that does not treat whitespace specially. */
public static final Factory FACTORY = new Factory() {
public RawText create(byte[] input) {
return new RawText(input);
}
};
/** Number of bytes to check for heuristics in {@link #isBinary(byte[])} */ /** Number of bytes to check for heuristics in {@link #isBinary(byte[])} */
private static final int FIRST_FEW_BYTES = 8000; private static final int FIRST_FEW_BYTES = 8000;
@ -107,9 +88,24 @@ public RawText create(byte[] input) {
* through cached arrays is safe. * through cached arrays is safe.
*/ */
public RawText(final byte[] input) { public RawText(final byte[] input) {
this(RawTextComparator.DEFAULT, input);
}
/**
* Create a new sequence from an existing content byte array.
*
* The entire array (indexes 0 through length-1) is used as the content.
*
* @param cmp
* comparator that will later be used to compare texts.
* @param input
* the content array. The array is never modified, so passing
* through cached arrays is safe.
*/
public RawText(RawTextComparator cmp, byte[] input) {
content = input; content = input;
lines = RawParseUtils.lineMap(content, 0, content.length); lines = RawParseUtils.lineMap(content, 0, content.length);
hashes = computeHashes(); hashes = computeHashes(cmp);
} }
/** /**
@ -126,6 +122,7 @@ public RawText(File file) throws IOException {
this(IO.readFully(file)); this(IO.readFully(file));
} }
/** @return total number of items in the sequence. */
public int size() { public int size() {
// The line map is always 2 entries larger than the number of lines in // The line map is always 2 entries larger than the number of lines in
// the file. Index 0 is padded out/unused. The last index is the total // the file. Index 0 is padded out/unused. The last index is the total
@ -134,30 +131,6 @@ public int size() {
return lines.size() - 2; return lines.size() - 2;
} }
public boolean equals(final int i, final Sequence other, final int j) {
return equals(this, i + 1, (RawText) other, j + 1);
}
private static boolean equals(final RawText a, final int ai,
final RawText b, final int bi) {
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
final int ae = a.lines.get(ai + 1);
final int be = b.lines.get(bi + 1);
if (ae - as != be - bs)
return false;
while (as < ae) {
if (a.content[as++] != b.content[bs++])
return false;
}
return true;
}
/** /**
* Write a specific line to the output stream, without its trailing LF. * Write a specific line to the output stream, without its trailing LF.
* <p> * <p>
@ -197,34 +170,16 @@ public boolean isMissingNewlineAtEnd() {
return content[end - 1] != '\n'; return content[end - 1] != '\n';
} }
private int[] computeHashes() { private int[] computeHashes(RawTextComparator cmp) {
final int[] r = new int[lines.size()]; final int[] r = new int[lines.size()];
for (int lno = 1; lno < lines.size() - 1; lno++) { for (int lno = 1; lno < lines.size() - 1; lno++) {
final int ptr = lines.get(lno); final int ptr = lines.get(lno);
final int end = lines.get(lno + 1); final int end = lines.get(lno + 1);
r[lno] = hashLine(content, ptr, end); r[lno] = cmp.hashRegion(content, ptr, end);
} }
return r; return r;
} }
/**
* Compute a hash code for a single line.
*
* @param raw
* the raw file content.
* @param ptr
* first byte of the content line to hash.
* @param end
* 1 past the last byte of the content line.
* @return hash code for the region <code>[ptr, end)</code> of raw.
*/
protected int hashLine(final byte[] raw, int ptr, final int end) {
int hash = 5381;
for (; ptr < end; ptr++)
hash = (hash << 5) ^ (raw[ptr] & 0xff);
return hash;
}
/** /**
* Determine heuristically whether a byte array represents binary (as * Determine heuristically whether a byte array represents binary (as
* opposed to text) content. * opposed to text) content.

View File

@ -0,0 +1,290 @@
/*
* Copyright (C) 2009-2010, Google Inc.
* Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.diff;
import static org.eclipse.jgit.util.RawCharUtil.isWhitespace;
import static org.eclipse.jgit.util.RawCharUtil.trimLeadingWhitespace;
import static org.eclipse.jgit.util.RawCharUtil.trimTrailingWhitespace;
/** Equivalence function for {@link RawText}. */
public abstract class RawTextComparator extends SequenceComparator<RawText> {
/** No special treatment. */
public static final RawTextComparator DEFAULT = new RawTextComparator() {
@Override
public boolean equals(RawText a, int ai, RawText b, int bi) {
ai++;
bi++;
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
final int ae = a.lines.get(ai + 1);
final int be = b.lines.get(bi + 1);
if (ae - as != be - bs)
return false;
while (as < ae) {
if (a.content[as++] != b.content[bs++])
return false;
}
return true;
}
@Override
public int hashRegion(final byte[] raw, int ptr, final int end) {
int hash = 5381;
for (; ptr < end; ptr++)
hash = (hash << 5) ^ (raw[ptr] & 0xff);
return hash;
}
};
/** Ignores all whitespace. */
public static final RawTextComparator WS_IGNORE_ALL = new RawTextComparator() {
@Override
public boolean equals(RawText a, int ai, RawText b, int bi) {
ai++;
bi++;
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
ae = trimTrailingWhitespace(a.content, as, ae);
be = trimTrailingWhitespace(b.content, bs, be);
while (as < ae && bs < be) {
byte ac = a.content[as];
byte bc = b.content[bs];
while (as < ae - 1 && isWhitespace(ac)) {
as++;
ac = a.content[as];
}
while (bs < be - 1 && isWhitespace(bc)) {
bs++;
bc = b.content[bs];
}
if (ac != bc)
return false;
as++;
bs++;
}
return as == ae && bs == be;
}
@Override
public int hashRegion(byte[] raw, int ptr, int end) {
int hash = 5381;
for (; ptr < end; ptr++) {
byte c = raw[ptr];
if (!isWhitespace(c))
hash = (hash << 5) ^ (c & 0xff);
}
return hash;
}
};
/** Ignores leading whitespace. */
public static final RawTextComparator WS_IGNORE_LEADING = new RawTextComparator() {
@Override
public boolean equals(RawText a, int ai, RawText b, int bi) {
ai++;
bi++;
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
as = trimLeadingWhitespace(a.content, as, ae);
bs = trimLeadingWhitespace(b.content, bs, be);
if (ae - as != be - bs)
return false;
while (as < ae) {
if (a.content[as++] != b.content[bs++])
return false;
}
return true;
}
@Override
public int hashRegion(final byte[] raw, int ptr, int end) {
int hash = 5381;
ptr = trimLeadingWhitespace(raw, ptr, end);
for (; ptr < end; ptr++) {
hash = (hash << 5) ^ (raw[ptr] & 0xff);
}
return hash;
}
};
/** Ignores trailing whitespace. */
public static final RawTextComparator WS_IGNORE_TRAILING = new RawTextComparator() {
@Override
public boolean equals(RawText a, int ai, RawText b, int bi) {
ai++;
bi++;
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
ae = trimTrailingWhitespace(a.content, as, ae);
be = trimTrailingWhitespace(b.content, bs, be);
if (ae - as != be - bs)
return false;
while (as < ae) {
if (a.content[as++] != b.content[bs++])
return false;
}
return true;
}
@Override
public int hashRegion(final byte[] raw, int ptr, int end) {
int hash = 5381;
end = trimTrailingWhitespace(raw, ptr, end);
for (; ptr < end; ptr++) {
hash = (hash << 5) ^ (raw[ptr] & 0xff);
}
return hash;
}
};
/** Ignores trailing whitespace. */
public static final RawTextComparator WS_IGNORE_CHANGE = new RawTextComparator() {
@Override
public boolean equals(RawText a, int ai, RawText b, int bi) {
ai++;
bi++;
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
ae = trimTrailingWhitespace(a.content, as, ae);
be = trimTrailingWhitespace(b.content, bs, be);
while (as < ae && bs < be) {
byte ac = a.content[as];
byte bc = b.content[bs];
if (ac != bc)
return false;
if (isWhitespace(ac))
as = trimLeadingWhitespace(a.content, as, ae);
else
as++;
if (isWhitespace(bc))
bs = trimLeadingWhitespace(b.content, bs, be);
else
bs++;
}
return as == ae && bs == be;
}
@Override
public int hashRegion(final byte[] raw, int ptr, int end) {
int hash = 5381;
end = trimTrailingWhitespace(raw, ptr, end);
while (ptr < end) {
byte c = raw[ptr];
hash = (hash << 5) ^ (c & 0xff);
if (isWhitespace(c))
ptr = trimLeadingWhitespace(raw, ptr, end);
else
ptr++;
}
return hash;
}
};
@Override
public int hash(RawText seq, int ptr) {
return seq.hashes[ptr + 1];
}
/**
* Compute a hash code for a region.
*
* @param raw
* the raw file content.
* @param ptr
* first byte of the region to hash.
* @param end
* 1 past the last byte of the region.
* @return hash code for the region <code>[ptr, end)</code> of raw.
*/
public abstract int hashRegion(byte[] raw, int ptr, int end);
}

View File

@ -1,127 +0,0 @@
/*
* Copyright (C) 2009-2010, Google Inc.
* Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.diff;
import static org.eclipse.jgit.util.RawCharUtil.isWhitespace;
import static org.eclipse.jgit.util.RawCharUtil.trimTrailingWhitespace;
/**
* A version of {@link RawText} that ignores all whitespace.
*/
public class RawTextIgnoreAllWhitespace extends RawText {
/** Creates RawText that ignores all whitespace. */
@SuppressWarnings("hiding")
public static final Factory FACTORY = new Factory() {
public RawText create(byte[] input) {
return new RawTextIgnoreAllWhitespace(input);
}
};
/**
* Create a new sequence from an existing content byte array.
* <p>
* The entire array (indexes 0 through length-1) is used as the content.
*
* @param input
* the content array. The array is never modified, so passing
* through cached arrays is safe.
*/
public RawTextIgnoreAllWhitespace(byte[] input) {
super(input);
}
@Override
public boolean equals(final int i, final Sequence other, final int j) {
return equals(this, i + 1, (RawText) other, j + 1);
}
private static boolean equals(final RawText a, final int ai,
final RawText b, final int bi) {
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
ae = trimTrailingWhitespace(a.content, as, ae);
be = trimTrailingWhitespace(b.content, bs, be);
while (as < ae && bs < be) {
byte ac = a.content[as];
byte bc = b.content[bs];
while (as < ae - 1 && isWhitespace(ac)) {
as++;
ac = a.content[as];
}
while (bs < be - 1 && isWhitespace(bc)) {
bs++;
bc = b.content[bs];
}
if (ac != bc)
return false;
as++;
bs++;
}
return as == ae && bs == be;
}
@Override
protected int hashLine(final byte[] raw, int ptr, final int end) {
int hash = 5381;
for (; ptr < end; ptr++) {
byte c = raw[ptr];
if (!isWhitespace(c))
hash = (hash << 5) ^ (c & 0xff);
}
return hash;
}
}

View File

@ -1,111 +0,0 @@
/*
* Copyright (C) 2009-2010, Google Inc.
* Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.diff;
import static org.eclipse.jgit.util.RawCharUtil.trimTrailingWhitespace;
/**
* A version of {@link RawText} that ignores trailing whitespace.
*/
public class RawTextIgnoreTrailingWhitespace extends RawText {
/** Creates RawText that ignores only trailing whitespace. */
@SuppressWarnings("hiding")
public static final Factory FACTORY = new Factory() {
public RawText create(byte[] input) {
return new RawTextIgnoreTrailingWhitespace(input);
}
};
/**
* Create a new sequence from an existing content byte array.
* <p>
* The entire array (indexes 0 through length-1) is used as the content.
*
* @param input
* the content array. The array is never modified, so passing
* through cached arrays is safe.
*/
public RawTextIgnoreTrailingWhitespace(byte[] input) {
super(input);
}
@Override
public boolean equals(final int i, final Sequence other, final int j) {
return equals(this, i + 1, (RawText) other, j + 1);
}
private static boolean equals(final RawText a, final int ai,
final RawText b, final int bi) {
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
ae = trimTrailingWhitespace(a.content, as, ae);
be = trimTrailingWhitespace(b.content, bs, be);
if (ae - as != be - bs)
return false;
while (as < ae) {
if (a.content[as++] != b.content[bs++])
return false;
}
return true;
}
@Override
protected int hashLine(final byte[] raw, int ptr, int end) {
int hash = 5381;
end = trimTrailingWhitespace(raw, ptr, end);
for (; ptr < end; ptr++) {
hash = (hash << 5) ^ (raw[ptr] & 0xff);
}
return hash;
}
}

View File

@ -1,129 +0,0 @@
/*
* Copyright (C) 2009-2010, Google Inc.
* Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.diff;
import static org.eclipse.jgit.util.RawCharUtil.isWhitespace;
import static org.eclipse.jgit.util.RawCharUtil.trimLeadingWhitespace;
import static org.eclipse.jgit.util.RawCharUtil.trimTrailingWhitespace;
/**
* A version of {@link RawText} that ignores changes in the amount of
* whitespace, as well as trailing whitespace.
*/
public class RawTextIgnoreWhitespaceChange extends RawText {
/** Creates RawText that ignores only whitespace changes. */
@SuppressWarnings("hiding")
public static final Factory FACTORY = new Factory() {
public RawText create(byte[] input) {
return new RawTextIgnoreWhitespaceChange(input);
}
};
/**
* Create a new sequence from an existing content byte array.
* <p>
* The entire array (indexes 0 through length-1) is used as the content.
*
* @param input
* the content array. The array is never modified, so passing
* through cached arrays is safe.
*/
public RawTextIgnoreWhitespaceChange(byte[] input) {
super(input);
}
@Override
public boolean equals(final int i, final Sequence other, final int j) {
return equals(this, i + 1, (RawText) other, j + 1);
}
private static boolean equals(final RawText a, final int ai,
final RawText b, final int bi) {
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
ae = trimTrailingWhitespace(a.content, as, ae);
be = trimTrailingWhitespace(b.content, bs, be);
while (as < ae && bs < be) {
byte ac = a.content[as];
byte bc = b.content[bs];
if (ac != bc)
return false;
if (isWhitespace(ac))
as = trimLeadingWhitespace(a.content, as, ae);
else
as++;
if (isWhitespace(bc))
bs = trimLeadingWhitespace(b.content, bs, be);
else
bs++;
}
return as == ae && bs == be;
}
@Override
protected int hashLine(final byte[] raw, int ptr, int end) {
int hash = 5381;
end = trimTrailingWhitespace(raw, ptr, end);
while (ptr < end) {
byte c = raw[ptr];
hash = (hash << 5) ^ (c & 0xff);
if (isWhitespace(c))
ptr = trimLeadingWhitespace(raw, ptr, end);
else
ptr++;
}
return hash;
}
}

View File

@ -1,4 +1,5 @@
/* /*
* Copyright (C) 2010, Google Inc.
* Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de> * Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* and other copyright owners as documented in the project's IP log. * and other copyright owners as documented in the project's IP log.
* *
@ -44,41 +45,20 @@
package org.eclipse.jgit.diff; package org.eclipse.jgit.diff;
/** /**
* Arbitrary sequence of elements with fast comparison support. * Arbitrary sequence of elements.
* <p> *
* A sequence of elements is defined to contain elements in the index range * A sequence of elements is defined to contain elements in the index range
* <code>[0, {@link #size()})</code>, like a standard Java List implementation. * <code>[0, {@link #size()})</code>, like a standard Java List implementation.
* Unlike a List, the members of the sequence are not directly obtainable, but * Unlike a List, the members of the sequence are not directly obtainable.
* element equality can be tested if two Sequences are the same implementation. *
* <p>
* An implementation may chose to implement the equals semantic as necessary,
* including fuzzy matching rules such as ignoring insignificant sub-elements,
* e.g. ignoring whitespace differences in text.
* <p>
* Implementations of Sequence are primarily intended for use in content * Implementations of Sequence are primarily intended for use in content
* difference detection algorithms, to produce an {@link EditList} of * difference detection algorithms, to produce an {@link EditList} of
* {@link Edit} instances describing how two Sequence instances differ. * {@link Edit} instances describing how two Sequence instances differ.
*/
public interface Sequence {
/** @return total number of items in the sequence. */
public int size();
/**
* Determine if the i-th member is equal to the j-th member.
* <p>
* Implementations must ensure <code>equals(thisIdx,other,otherIdx)</code>
* returns the same as <code>other.equals(otherIdx,this,thisIdx)</code>.
* *
* @param thisIdx * To be compared against another Sequence of the same type, a supporting
* index within <code>this</code> sequence; must be in the range * {@link SequenceComparator} must also be supplied.
* <code>[ 0, this.size() )</code>.
* @param other
* another sequence; must be the same implementation class, that
* is <code>this.getClass() == other.getClass()</code>.
* @param otherIdx
* index within <code>other</code> sequence; must be in the range
* <code>[ 0, other.size() )</code>.
* @return true if the elements are equal; false if they are not equal.
*/ */
public boolean equals(int thisIdx, Sequence other, int otherIdx); public abstract class Sequence {
/** @return total number of items in the sequence. */
public abstract int size();
} }

View File

@ -1,6 +1,5 @@
/* /*
* Copyright (C) 2009-2010, Google Inc. * Copyright (C) 2010, Google Inc.
* Copyright (C) 2008-2009, Johannes E. Schindelin <johannes.schindelin@gmx.de>
* and other copyright owners as documented in the project's IP log. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -44,68 +43,53 @@
package org.eclipse.jgit.diff; package org.eclipse.jgit.diff;
import static org.eclipse.jgit.util.RawCharUtil.trimLeadingWhitespace;
/** /**
* A version of {@link RawText} that ignores leading whitespace. * Equivalence function for a {@link Sequence} compared by difference algorithm.
*
* Difference algorithms can use a comparator to compare portions of two
* sequences and discover the minimal edits required to transform from one
* sequence to the other sequence.
*
* Indexes within a sequence are zero-based.
*
* @param <S>
* type of sequence the comparator supports.
*/ */
public class RawTextIgnoreLeadingWhitespace extends RawText { public abstract class SequenceComparator<S extends Sequence> {
/** Creates RawText that ignores only leading whitespace. */ /**
@SuppressWarnings("hiding") * Compare two items to determine if they are equivalent.
public static final Factory FACTORY = new Factory() { *
public RawText create(byte[] input) { * It is permissible to compare sequence {@code a} with itself (by passing
return new RawTextIgnoreLeadingWhitespace(input); * {@code a} again in position {@code b}).
} *
}; * @param a
* the first sequence.
* @param ai
* item of {@code ai} to compare.
* @param b
* the second sequence.
* @param bi
* item of {@code bi} to compare.
* @return true if the two items are identical according to this function's
* equivalence rule.
*/
public abstract boolean equals(S a, int ai, S b, int bi);
/** /**
* Create a new sequence from an existing content byte array. * Get a hash value for an item in a sequence.
* <p>
* The entire array (indexes 0 through length-1) is used as the content.
* *
* @param input * If two items are equal according to this comparator's
* the content array. The array is never modified, so passing * {@link #equals(Sequence, int, Sequence, int)} method, then this hash
* through cached arrays is safe. * method must produce the same integer result for both items.
*
* It is not required for two items to have different hash values if they
* are are unequal according to the {@code equals()} method.
*
* @param seq
* the sequence.
* @param ptr
* the item to obtain the hash for.
* @return hash the hash value.
*/ */
public RawTextIgnoreLeadingWhitespace(byte[] input) { public abstract int hash(S seq, int ptr);
super(input);
}
@Override
public boolean equals(final int i, final Sequence other, final int j) {
return equals(this, i + 1, (RawText) other, j + 1);
}
private static boolean equals(final RawText a, final int ai,
final RawText b, final int bi) {
if (a.hashes[ai] != b.hashes[bi])
return false;
int as = a.lines.get(ai);
int bs = b.lines.get(bi);
int ae = a.lines.get(ai + 1);
int be = b.lines.get(bi + 1);
as = trimLeadingWhitespace(a.content, as, ae);
bs = trimLeadingWhitespace(b.content, bs, be);
if (ae - as != be - bs)
return false;
while (as < ae) {
if (a.content[as++] != b.content[bs++])
return false;
}
return true;
}
@Override
protected int hashLine(final byte[] raw, int ptr, int end) {
int hash = 5381;
ptr = trimLeadingWhitespace(raw, ptr, end);
for (; ptr < end; ptr++) {
hash = (hash << 5) ^ (raw[ptr] & 0xff);
}
return hash;
}
} }

View File

@ -51,6 +51,7 @@
import org.eclipse.jgit.diff.EditList; import org.eclipse.jgit.diff.EditList;
import org.eclipse.jgit.diff.MyersDiff; import org.eclipse.jgit.diff.MyersDiff;
import org.eclipse.jgit.diff.Sequence; import org.eclipse.jgit.diff.Sequence;
import org.eclipse.jgit.diff.SequenceComparator;
import org.eclipse.jgit.merge.MergeChunk.ConflictState; import org.eclipse.jgit.merge.MergeChunk.ConflictState;
/** /**
@ -74,21 +75,24 @@ private MergeAlgorithm() {
/** /**
* Does the three way merge between a common base and two sequences. * Does the three way merge between a common base and two sequences.
* *
* @param <S>
* type of sequence.
* @param cmp comparison method for this execution.
* @param base the common base sequence * @param base the common base sequence
* @param ours the first sequence to be merged * @param ours the first sequence to be merged
* @param theirs the second sequence to be merged * @param theirs the second sequence to be merged
* @return the resulting content * @return the resulting content
*/ */
public static MergeResult merge(Sequence base, Sequence ours, public static <S extends Sequence> MergeResult<S> merge(
Sequence theirs) { SequenceComparator<S> cmp, S base, S ours, S theirs) {
List<Sequence> sequences = new ArrayList<Sequence>(3); List<S> sequences = new ArrayList<S>(3);
sequences.add(base); sequences.add(base);
sequences.add(ours); sequences.add(ours);
sequences.add(theirs); sequences.add(theirs);
MergeResult result = new MergeResult(sequences); MergeResult result = new MergeResult<S>(sequences);
EditList oursEdits = new MyersDiff(base, ours).getEdits(); EditList oursEdits = new MyersDiff<S>(cmp, base, ours).getEdits();
Iterator<Edit> baseToOurs = oursEdits.iterator(); Iterator<Edit> baseToOurs = oursEdits.iterator();
EditList theirsEdits = new MyersDiff(base, theirs).getEdits(); EditList theirsEdits = new MyersDiff<S>(cmp, base, theirs).getEdits();
Iterator<Edit> baseToTheirs = theirsEdits.iterator(); Iterator<Edit> baseToTheirs = theirsEdits.iterator();
int current = 0; // points to the next line (first line is 0) of base int current = 0; // points to the next line (first line is 0) of base
// which was not handled yet // which was not handled yet
@ -201,13 +205,13 @@ public static MergeResult merge(Sequence base, Sequence ours,
- theirsBeginB); - theirsBeginB);
int commonPrefix = 0; int commonPrefix = 0;
while (commonPrefix < conflictLen while (commonPrefix < conflictLen
&& ours.equals(oursBeginB + commonPrefix, theirs, && cmp.equals(ours, oursBeginB + commonPrefix, theirs,
theirsBeginB + commonPrefix)) theirsBeginB + commonPrefix))
commonPrefix++; commonPrefix++;
conflictLen -= commonPrefix; conflictLen -= commonPrefix;
int commonSuffix = 0; int commonSuffix = 0;
while (commonSuffix < conflictLen while (commonSuffix < conflictLen
&& ours.equals(oursEndB - commonSuffix - 1, theirs, && cmp.equals(ours, oursEndB - commonSuffix - 1, theirs,
theirsEndB - commonSuffix - 1)) theirsEndB - commonSuffix - 1))
commonSuffix++; commonSuffix++;
conflictLen -= commonSuffix; conflictLen -= commonSuffix;

View File

@ -76,14 +76,13 @@ public class MergeFormatter {
* metadata * metadata
* @throws IOException * @throws IOException
*/ */
public void formatMerge(OutputStream out, MergeResult res, public void formatMerge(OutputStream out, MergeResult<RawText> res,
List<String> seqName, String charsetName) throws IOException { List<String> seqName, String charsetName) throws IOException {
String lastConflictingName = null; // is set to non-null whenever we are String lastConflictingName = null; // is set to non-null whenever we are
// in a conflict // in a conflict
boolean threeWayMerge = (res.getSequences().size() == 3); boolean threeWayMerge = (res.getSequences().size() == 3);
for (MergeChunk chunk : res) { for (MergeChunk chunk : res) {
RawText seq = (RawText) res.getSequences().get( RawText seq = res.getSequences().get(chunk.getSequenceIndex());
chunk.getSequenceIndex());
if (lastConflictingName != null if (lastConflictingName != null
&& chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) { && chunk.getConflictState() != ConflictState.NEXT_CONFLICTING_RANGE) {
// found the end of an conflict // found the end of an conflict

View File

@ -63,9 +63,12 @@
* This class does not know anything about how to present the merge result to * This class does not know anything about how to present the merge result to
* the end-user. MergeFormatters have to be used to construct something human * the end-user. MergeFormatters have to be used to construct something human
* readable. * readable.
*
* @param <S>
* type of sequence.
*/ */
public class MergeResult implements Iterable<MergeChunk> { public class MergeResult<S extends Sequence> implements Iterable<MergeChunk> {
private final List<Sequence> sequences; private final List<S> sequences;
private final IntList chunks = new IntList(); private final IntList chunks = new IntList();
@ -79,7 +82,7 @@ public class MergeResult implements Iterable<MergeChunk> {
* followed by the merged sequences. This list should not be * followed by the merged sequences. This list should not be
* modified anymore during the lifetime of this {@link MergeResult}. * modified anymore during the lifetime of this {@link MergeResult}.
*/ */
public MergeResult(List<Sequence> sequences) { public MergeResult(List<S> sequences) {
this.sequences = sequences; this.sequences = sequences;
} }
@ -120,7 +123,7 @@ public void add(int srcIdx, int begin, int end, ConflictState conflictState) {
* @return the common predecessor at position 0 followed by the merged * @return the common predecessor at position 0 followed by the merged
* sequences. * sequences.
*/ */
public List<Sequence> getSequences() { public List<S> getSequences() {
return sequences; return sequences;
} }

View File

@ -59,6 +59,8 @@
import org.eclipse.jgit.JGitText; import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.diff.RawText; import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.diff.RawTextComparator;
import org.eclipse.jgit.diff.Sequence;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuildIterator; import org.eclipse.jgit.dircache.DirCacheBuildIterator;
import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheBuilder;
@ -118,7 +120,7 @@ public enum MergeFailureReason {
private Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<String, DirCacheEntry>(); private Map<String, DirCacheEntry> toBeCheckedOut = new HashMap<String, DirCacheEntry>();
private Map<String, MergeResult> mergeResults = new HashMap<String, MergeResult>(); private Map<String, MergeResult<? extends Sequence>> mergeResults = new HashMap<String, MergeResult<? extends Sequence>>();
private Map<String, MergeFailureReason> failingPathes = new HashMap<String, MergeFailureReason>(); private Map<String, MergeFailureReason> failingPathes = new HashMap<String, MergeFailureReason>();
@ -413,7 +415,8 @@ private boolean contentMerge(CanonicalTreeParser base,
MergeFormatter fmt = new MergeFormatter(); MergeFormatter fmt = new MergeFormatter();
// do the merge // do the merge
MergeResult result = MergeAlgorithm.merge( MergeResult<RawText> result = MergeAlgorithm.merge(
RawTextComparator.DEFAULT,
getRawText(base.getEntryObjectId(), db), getRawText(base.getEntryObjectId(), db),
getRawText(ours.getEntryObjectId(), db), getRawText(ours.getEntryObjectId(), db),
getRawText(theirs.getEntryObjectId(), db)); getRawText(theirs.getEntryObjectId(), db));
@ -524,7 +527,7 @@ public Map<String, DirCacheEntry> getToBeCheckedOut() {
/** /**
* @return the mergeResults * @return the mergeResults
*/ */
public Map<String, MergeResult> getMergeResults() { public Map<String, MergeResult<? extends Sequence>> getMergeResults() {
return mergeResults; return mergeResults;
} }