Merge branch 'stable-3.6'

* stable-3.6:
  Prepare 3.6.2-SNAPSHOT builds
  JGit v3.6.1.201501031845-r
  Trim author/committer name and email in commit header
  Rename detection should canonicalize line endings
  PathMatcher should respect "assumeDirectory" flag

Change-Id: Idd48c6d94cf1ab09abc07f70d50890b1b78e1833
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
Matthias Sohn 2015-01-04 02:18:27 +01:00
commit 79dacba922
6 changed files with 203 additions and 57 deletions

View File

@ -83,7 +83,7 @@ public void testIndexingLargeObject() throws IOException,
+ "B\n" // + "B\n" //
+ "B\n").getBytes("UTF-8"); + "B\n").getBytes("UTF-8");
SimilarityIndex si = new SimilarityIndex(); SimilarityIndex si = new SimilarityIndex();
si.hash(new ByteArrayInputStream(in), in.length); si.hash(new ByteArrayInputStream(in), in.length, false);
assertEquals(2, si.size()); assertEquals(2, si.size());
} }
@ -103,6 +103,48 @@ public void testCommonScore_SameFiles() throws TableFullException {
assertEquals(100, dst.score(src, 100)); assertEquals(100, dst.score(src, 100));
} }
@Test
public void testCommonScore_SameFiles_CR_canonicalization()
throws TableFullException {
String text = "" //
+ "A\r\n" //
+ "B\r\n" //
+ "D\r\n" //
+ "B\r\n";
SimilarityIndex src = hash(text);
SimilarityIndex dst = hash(text.replace("\r", ""));
assertEquals(8, src.common(dst));
assertEquals(8, dst.common(src));
assertEquals(100, src.score(dst, 100));
assertEquals(100, dst.score(src, 100));
}
@Test
public void testCommonScoreLargeObject_SameFiles_CR_canonicalization()
throws TableFullException, IOException {
String text = "" //
+ "A\r\n" //
+ "B\r\n" //
+ "D\r\n" //
+ "B\r\n";
SimilarityIndex src = new SimilarityIndex();
byte[] bytes1 = text.getBytes("UTF-8");
src.hash(new ByteArrayInputStream(bytes1), bytes1.length, true);
src.sort();
SimilarityIndex dst = new SimilarityIndex();
byte[] bytes2 = text.replace("\r", "").getBytes("UTF-8");
dst.hash(new ByteArrayInputStream(bytes2), bytes2.length, true);
dst.sort();
assertEquals(8, src.common(dst));
assertEquals(8, dst.common(src));
assertEquals(100, src.score(dst, 100));
assertEquals(100, dst.score(src, 100));
}
@Test @Test
public void testCommonScore_EmptyFiles() throws TableFullException { public void testCommonScore_EmptyFiles() throws TableFullException {
SimilarityIndex src = hash(""); SimilarityIndex src = hash("");
@ -132,24 +174,8 @@ public void testCommonScore_SimiliarBy75() throws TableFullException {
} }
private static SimilarityIndex hash(String text) throws TableFullException { private static SimilarityIndex hash(String text) throws TableFullException {
SimilarityIndex src = new SimilarityIndex() { SimilarityIndex src = new SimilarityIndex();
@Override
void hash(byte[] raw, int ptr, final int end)
throws TableFullException {
while (ptr < end) {
int hash = raw[ptr] & 0xff;
int start = ptr;
do {
int c = raw[ptr++] & 0xff;
if (c == '\n')
break;
} while (ptr < end && ptr - start < 64);
add(hash, ptr - start);
}
}
};
byte[] raw = Constants.encode(text); byte[] raw = Constants.encode(text);
src.setFileSize(raw.length);
src.hash(raw, 0, raw.length); src.hash(raw, 0, raw.length);
src.sort(); src.sort();
return src; return src;

View File

@ -44,9 +44,12 @@
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import java.util.Arrays; import java.util.Arrays;
import org.eclipse.jgit.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
@ -236,16 +239,62 @@ public void testParentDirectoryGitIgnores() {
} }
@Test @Test
public void testTrailingSlash() { public void testDirModeAndNoRegex() {
String pattern = "/src/"; String pattern = "/src/";
assertMatched(pattern, "/src/"); assertMatched(pattern, "/src/");
assertMatched(pattern, "/src/new"); assertMatched(pattern, "/src/new");
assertMatched(pattern, "/src/new/a.c"); assertMatched(pattern, "/src/new/a.c");
assertMatched(pattern, "/src/a.c"); assertMatched(pattern, "/src/a.c");
// no match as a "file" pattern, because rule is for directories only
assertNotMatched(pattern, "/src"); assertNotMatched(pattern, "/src");
assertNotMatched(pattern, "/srcA/"); assertNotMatched(pattern, "/srcA/");
} }
@Test
public void testDirModeAndRegex1() {
// IgnoreRule was buggy for some cases below, therefore using "Assume"
Boolean assume = useOldRule;
String pattern = "a/*/src/";
assertMatched(pattern, "a/b/src/");
assertMatched(pattern, "a/b/src/new");
assertMatched(pattern, "a/b/src/new/a.c");
assertMatched(pattern, "a/b/src/a.c");
// no match as a "file" pattern, because rule is for directories only
assertNotMatched(pattern, "a/b/src", assume);
assertNotMatched(pattern, "a/b/srcA/");
}
@Test
public void testDirModeAndRegex2() {
// IgnoreRule was buggy for some cases below, therefore using "Assume"
Boolean assume = useOldRule;
String pattern = "a/[a-b]/src/";
assertMatched(pattern, "a/b/src/");
assertMatched(pattern, "a/b/src/new");
assertMatched(pattern, "a/b/src/new/a.c");
assertMatched(pattern, "a/b/src/a.c");
// no match as a "file" pattern, because rule is for directories only
assertNotMatched(pattern, "a/b/src", assume);
assertNotMatched(pattern, "a/b/srcA/");
}
@Test
public void testDirModeAndRegex3() {
// IgnoreRule was buggy for some cases below, therefore using "Assume"
Boolean assume = useOldRule;
String pattern = "**/src/";
assertMatched(pattern, "a/b/src/", assume);
assertMatched(pattern, "a/b/src/new", assume);
assertMatched(pattern, "a/b/src/new/a.c", assume);
assertMatched(pattern, "a/b/src/a.c", assume);
// no match as a "file" pattern, because rule is for directories only
assertNotMatched(pattern, "a/b/src", assume);
assertNotMatched(pattern, "a/b/srcA/", assume);
}
@Test @Test
public void testNameOnlyMatches() { public void testNameOnlyMatches() {
/* /*
@ -321,11 +370,16 @@ public void testNegation() {
* Pattern as it would appear in a .gitignore file * Pattern as it would appear in a .gitignore file
* @param target * @param target
* Target file path relative to repository's GIT_DIR * Target file path relative to repository's GIT_DIR
* @param assume
*/ */
public void assertMatched(String pattern, String target) { public void assertMatched(String pattern, String target, Boolean... assume) {
boolean value = match(pattern, target); boolean value = match(pattern, target);
if (assume.length == 0 || !assume[0].booleanValue())
assertTrue("Expected a match for: " + pattern + " with: " + target, assertTrue("Expected a match for: " + pattern + " with: " + target,
value); value);
else
assumeTrue("Expected a match for: " + pattern + " with: " + target,
value);
} }
/** /**
@ -336,11 +390,17 @@ public void assertMatched(String pattern, String target) {
* Pattern as it would appear in a .gitignore file * Pattern as it would appear in a .gitignore file
* @param target * @param target
* Target file path relative to repository's GIT_DIR * Target file path relative to repository's GIT_DIR
* @param assume
*/ */
public void assertNotMatched(String pattern, String target) { public void assertNotMatched(String pattern, String target,
Boolean... assume) {
boolean value = match(pattern, target); boolean value = match(pattern, target);
assertFalse("Expected no match for: " + pattern + " with: " + target, if (assume.length == 0 || !assume[0].booleanValue())
value); assertFalse("Expected no match for: " + pattern + " with: "
+ target, value);
else
assumeFalse("Expected no match for: " + pattern + " with: "
+ target, value);
} }
/** /**
@ -355,16 +415,43 @@ public void assertNotMatched(String pattern, String target) {
*/ */
private boolean match(String pattern, String target) { private boolean match(String pattern, String target) {
boolean isDirectory = target.endsWith("/"); boolean isDirectory = target.endsWith("/");
boolean match;
if (useOldRule.booleanValue()) { if (useOldRule.booleanValue()) {
IgnoreRule r = new IgnoreRule(pattern); IgnoreRule r = new IgnoreRule(pattern);
// If speed of this test is ever an issue, we can use a presetRule match = r.isMatch(target, isDirectory);
// field } else {
// to avoid recompiling a pattern each time. FastIgnoreRule r = new FastIgnoreRule(pattern);
return r.isMatch(target, isDirectory); match = r.isMatch(target, isDirectory);
}
if (isDirectory) {
boolean noTrailingSlash = matchAsDir(pattern,
target.substring(0, target.length() - 1));
if (match != noTrailingSlash) {
String message = "Difference in result for directory pattern: "
+ pattern + " with: " + target
+ " if target is given without trailing slash";
Assert.assertEquals(message, match, noTrailingSlash);
}
}
return match;
}
/**
*
* @param target
* must not ends with a slash!
* @param pattern
* same as {@link #match(String, String)}
* @return same as {@link #match(String, String)}
*/
private boolean matchAsDir(String pattern, String target) {
assertFalse(target.endsWith("/"));
if (useOldRule.booleanValue()) {
IgnoreRule r = new IgnoreRule(pattern);
return r.isMatch(target, true);
} }
FastIgnoreRule r = new FastIgnoreRule(pattern); FastIgnoreRule r = new FastIgnoreRule(pattern);
// If speed of this test is ever an issue, we can use a presetRule field return r.isMatch(target, true);
// to avoid recompiling a pattern each time.
return r.isMatch(target, isDirectory);
} }
} }

View File

@ -44,6 +44,7 @@
package org.eclipse.jgit.lib; package org.eclipse.jgit.lib;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Date; import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
@ -83,4 +84,15 @@ public void nullForNameShouldThrowIllegalArgumentException() {
public void nullForEmailShouldThrowIllegalArgumentException() { public void nullForEmailShouldThrowIllegalArgumentException() {
new PersonIdent("A U Thor", null); new PersonIdent("A U Thor", null);
} }
@Test
public void testToExternalStringTrimsNameAndEmail() throws Exception {
PersonIdent personIdent = new PersonIdent(" A U Thor ",
" author@example.com ");
String externalString = personIdent.toExternalString();
assertTrue(externalString.startsWith("A U Thor <author@example.com>"));
}
} }

View File

@ -79,8 +79,11 @@ class SimilarityIndex {
/** Maximum value of the count field, also mask to extract the count. */ /** Maximum value of the count field, also mask to extract the count. */
private static final long MAX_COUNT = (1L << KEY_SHIFT) - 1; private static final long MAX_COUNT = (1L << KEY_SHIFT) - 1;
/** Total size of the file we hashed into the structure. */ /**
private long fileSize; * Total amount of bytes hashed into the structure, including \n. This is
* usually the size of the file minus number of CRLF encounters.
*/
private long hashedCnt;
/** Number of non-zero entries in {@link #idHash}. */ /** Number of non-zero entries in {@link #idHash}. */
private int idSize; private int idSize;
@ -108,48 +111,59 @@ class SimilarityIndex {
idGrowAt = growAt(idHashBits); idGrowAt = growAt(idHashBits);
} }
long getFileSize() {
return fileSize;
}
void setFileSize(long size) {
fileSize = size;
}
void hash(ObjectLoader obj) throws MissingObjectException, IOException, void hash(ObjectLoader obj) throws MissingObjectException, IOException,
TableFullException { TableFullException {
if (obj.isLarge()) { if (obj.isLarge()) {
ObjectStream in = obj.openStream(); hashLargeObject(obj);
try {
setFileSize(in.getSize());
hash(in, fileSize);
} finally {
in.close();
}
} else { } else {
byte[] raw = obj.getCachedBytes(); byte[] raw = obj.getCachedBytes();
setFileSize(raw.length);
hash(raw, 0, raw.length); hash(raw, 0, raw.length);
} }
} }
private void hashLargeObject(ObjectLoader obj) throws IOException,
TableFullException {
ObjectStream in1 = obj.openStream();
boolean text;
try {
text = !RawText.isBinary(in1);
} finally {
in1.close();
}
ObjectStream in2 = obj.openStream();
try {
hash(in2, in2.getSize(), text);
} finally {
in2.close();
}
}
void hash(byte[] raw, int ptr, final int end) throws TableFullException { void hash(byte[] raw, int ptr, final int end) throws TableFullException {
final boolean text = !RawText.isBinary(raw);
hashedCnt = 0;
while (ptr < end) { while (ptr < end) {
int hash = 5381; int hash = 5381;
int blockHashedCnt = 0;
int start = ptr; int start = ptr;
// Hash one line, or one block, whichever occurs first. // Hash one line, or one block, whichever occurs first.
do { do {
int c = raw[ptr++] & 0xff; int c = raw[ptr++] & 0xff;
// Ignore CR in CRLF sequence if text
if (text && c == '\r' && ptr < end && raw[ptr] == '\n')
continue;
blockHashedCnt++;
if (c == '\n') if (c == '\n')
break; break;
hash = (hash << 5) + hash + c; hash = (hash << 5) + hash + c;
} while (ptr < end && ptr - start < 64); } while (ptr < end && ptr - start < 64);
add(hash, ptr - start); hashedCnt += blockHashedCnt;
add(hash, blockHashedCnt);
} }
} }
void hash(InputStream in, long remaining) throws IOException, void hash(InputStream in, long remaining, boolean text) throws IOException,
TableFullException { TableFullException {
byte[] buf = new byte[4096]; byte[] buf = new byte[4096];
int ptr = 0; int ptr = 0;
@ -157,6 +171,7 @@ void hash(InputStream in, long remaining) throws IOException,
while (0 < remaining) { while (0 < remaining) {
int hash = 5381; int hash = 5381;
int blockHashedCnt = 0;
// Hash one line, or one block, whichever occurs first. // Hash one line, or one block, whichever occurs first.
int n = 0; int n = 0;
@ -170,11 +185,16 @@ void hash(InputStream in, long remaining) throws IOException,
n++; n++;
int c = buf[ptr++] & 0xff; int c = buf[ptr++] & 0xff;
// Ignore CR in CRLF sequence if text
if (text && c == '\r' && ptr < cnt && buf[ptr] == '\n')
continue;
blockHashedCnt++;
if (c == '\n') if (c == '\n')
break; break;
hash = (hash << 5) + hash + c; hash = (hash << 5) + hash + c;
} while (n < 64 && n < remaining); } while (n < 64 && n < remaining);
add(hash, n); hashedCnt += blockHashedCnt;
add(hash, blockHashedCnt);
remaining -= n; remaining -= n;
} }
} }
@ -193,7 +213,7 @@ void sort() {
} }
int score(SimilarityIndex dst, int maxScore) { int score(SimilarityIndex dst, int maxScore) {
long max = Math.max(fileSize, dst.fileSize); long max = Math.max(hashedCnt, dst.hashedCnt);
if (max == 0) if (max == 0)
return maxScore; return maxScore;
return (int) ((common(dst) * maxScore) / max); return (int) ((common(dst) * maxScore) / max);

View File

@ -217,7 +217,8 @@ boolean iterate(final String path, final int startIncl, final int endExcl,
matcher++; matcher++;
match = matches(matcher, path, left, endExcl, match = matches(matcher, path, left, endExcl,
assumeDirectory); assumeDirectory);
} else if (dirOnly) } else if (dirOnly && !assumeDirectory)
// Directory expectations not met
return false; return false;
} }
return match && matcher + 1 == matchers.size(); return match && matcher + 1 == matchers.size();

View File

@ -254,9 +254,9 @@ && getEmailAddress().equals(p.getEmailAddress())
*/ */
public String toExternalString() { public String toExternalString() {
final StringBuilder r = new StringBuilder(); final StringBuilder r = new StringBuilder();
r.append(getName()); r.append(getName().trim());
r.append(" <"); //$NON-NLS-1$ r.append(" <"); //$NON-NLS-1$
r.append(getEmailAddress()); r.append(getEmailAddress().trim());
r.append("> "); //$NON-NLS-1$ r.append("> "); //$NON-NLS-1$
r.append(when / 1000); r.append(when / 1000);
r.append(' '); r.append(' ');