diff options
16 files changed, 1171 insertions, 47 deletions
diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index a4ff8a4b1d..a8f6ab73b0 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -62,7 +62,7 @@ <tycho-version>0.16.0</tycho-version> <jetty-version>7.6.0.v20120127</jetty-version> <args4j-version>2.0.12</args4j-version> - <jsch-version>0.1.44-1</jsch-version> + <jsch-version>0.1.46</jsch-version> <eclipse-site>http://download.eclipse.org/releases/indigo</eclipse-site> <orbit-site>http://download.eclipse.org/tools/orbit/downloads/drops/R20120526062928/repository/</orbit-site> </properties> diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java index cb7cad8340..4c9c54f214 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffEntryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011, Dariusz Luksza <dariusz@luksza.org> + * Copyright (C) 2011, 2013 Dariusz Luksza <dariusz@luksza.org> * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -47,6 +47,7 @@ import static org.eclipse.jgit.util.FileUtils.delete; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -65,6 +66,8 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.treewalk.EmptyTreeIterator; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilterGroup; +import org.eclipse.jgit.treewalk.filter.TreeFilter; import org.eclipse.jgit.util.FileUtils; import org.junit.Test; @@ -265,6 +268,71 @@ public class DiffEntryTest extends RepositoryTestCase { assertThat(entry.getNewPath(), is("b.txt")); } + @Test + public void shouldMarkEntriesWhenGivenMarkTreeFilter() throws Exception { + // given + Git git = new Git(db); + RevCommit c1 = git.commit().setMessage("initial commit").call(); + FileUtils.mkdir(new File(db.getWorkTree(), "b")); + writeTrashFile("a.txt", "a"); + writeTrashFile("b/1.txt", "b1"); + writeTrashFile("b/2.txt", "b2"); + writeTrashFile("c.txt", "c"); + git.add().addFilepattern("a.txt").addFilepattern("b") + .addFilepattern("c.txt").call(); + RevCommit c2 = git.commit().setMessage("second commit").call(); + TreeFilter filterA = PathFilterGroup.createFromStrings("a.txt"); + TreeFilter filterB = PathFilterGroup.createFromStrings("b"); + TreeFilter filterB2 = PathFilterGroup.createFromStrings("b/2.txt"); + + // when + TreeWalk walk = new TreeWalk(db); + walk.addTree(c1.getTree()); + walk.addTree(c2.getTree()); + List<DiffEntry> result = DiffEntry.scan(walk, true, new TreeFilter[] { + filterA, filterB, filterB2 }); + + // then + assertThat(result, notNullValue()); + assertEquals(5, result.size()); + + DiffEntry entryA = result.get(0); + DiffEntry entryB = result.get(1); + DiffEntry entryB1 = result.get(2); + DiffEntry entryB2 = result.get(3); + DiffEntry entryC = result.get(4); + + assertThat(entryA.getNewPath(), is("a.txt")); + assertTrue(entryA.isMarked(0)); + assertFalse(entryA.isMarked(1)); + assertFalse(entryA.isMarked(2)); + assertEquals(1, entryA.getTreeFilterMarks()); + + assertThat(entryB.getNewPath(), is("b")); + assertFalse(entryB.isMarked(0)); + assertTrue(entryB.isMarked(1)); + assertTrue(entryB.isMarked(2)); + assertEquals(6, entryB.getTreeFilterMarks()); + + assertThat(entryB1.getNewPath(), is("b/1.txt")); + assertFalse(entryB1.isMarked(0)); + assertTrue(entryB1.isMarked(1)); + assertFalse(entryB1.isMarked(2)); + assertEquals(2, entryB1.getTreeFilterMarks()); + + assertThat(entryB2.getNewPath(), is("b/2.txt")); + assertFalse(entryB2.isMarked(0)); + assertTrue(entryB2.isMarked(1)); + assertTrue(entryB2.isMarked(2)); + assertEquals(6, entryB2.getTreeFilterMarks()); + + assertThat(entryC.getNewPath(), is("c.txt")); + assertFalse(entryC.isMarked(0)); + assertFalse(entryC.isMarked(1)); + assertFalse(entryC.isMarked(2)); + assertEquals(0, entryC.getTreeFilterMarks()); + } + @Test(expected = IllegalArgumentException.class) public void shouldThrowIAEWhenTreeWalkHasLessThanTwoTrees() throws Exception { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/GCTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/GCTest.java index 8b6c003888..820a8ae07e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/GCTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/GCTest.java @@ -304,6 +304,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { public void nonReferencedExpiredObject_pruned() throws Exception { RevBlob a = tr.blob("a"); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); assertFalse(repo.hasObject(a)); } @@ -313,6 +314,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { RevBlob a = tr.blob("a"); RevTree t = tr.tree(tr.file("a", a)); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); assertFalse(repo.hasObject(t)); assertFalse(repo.hasObject(a)); @@ -336,6 +338,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { RevBlob a = tr.blob("a"); tr.lightweightTag("t", a); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); assertTrue(repo.hasObject(a)); } @@ -347,6 +350,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { tr.lightweightTag("t", t); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); assertTrue(repo.hasObject(t)); assertTrue(repo.hasObject(a)); @@ -357,6 +361,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { RevCommit tip = commitChain(10); tr.branch("b").update(tip); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); do { assertTrue(repo.hasObject(tip)); @@ -376,6 +381,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { update.setForceUpdate(true); update.delete(); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); assertTrue(gc.getStatistics().numberOfLooseObjects == 0); } @@ -404,6 +410,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { update.delete(); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); assertTrue(repo.hasObject(b2Tip)); } @@ -532,6 +539,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { assertEquals(0, stats.numberOfPackedObjects); gc.setExpireAgeMillis(0); + fsTick(); gc.gc(); stats = gc.getStatistics(); assertEquals(0, stats.numberOfLooseObjects); @@ -581,6 +589,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { assertEquals(8, stats.numberOfLooseObjects); assertEquals(0, stats.numberOfPackedObjects); gc.setExpireAgeMillis(0); + fsTick(); gc.gc(); stats = gc.getStatistics(); assertEquals(0, stats.numberOfLooseObjects); @@ -640,6 +649,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { assertEquals(9, stats.numberOfLooseObjects); assertEquals(0, stats.numberOfPackedObjects); gc.setExpireAgeMillis(0); + fsTick(); gc.gc(); stats = gc.getStatistics(); assertEquals(0, stats.numberOfLooseObjects); @@ -657,6 +667,7 @@ public class GCTest extends LocalDiskRepositoryTestCase { stats = gc.getStatistics(); assertEquals(8, stats.numberOfLooseObjects); gc.setExpireAgeMillis(0); + fsTick(); gc.prune(Collections.<ObjectId> emptySet()); stats = gc.getStatistics(); assertEquals(8, stats.numberOfLooseObjects); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java index bf22569dfb..a416c742f8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java @@ -253,14 +253,16 @@ public class FileTreeIteratorTest extends RepositoryTestCase { // Hopefully fsTick will make sure our entry gets smudged fsTick(f); writeTrashFile("file", "content"); + long lastModified = f.lastModified(); git.add().addFilepattern("file").call(); writeTrashFile("file", "conten2"); + f.setLastModified(lastModified); DirCacheEntry dce = db.readDirCache().getEntry("file"); FileTreeIterator fti = new FileTreeIterator(trash, db.getFS(), db .getConfig().get(WorkingTreeOptions.KEY)); while (!fti.getEntryPathString().equals("file")) fti.next(1); - // If the fsTick trick does not work we could skip the compareMetaData + // If the rounding trick does not work we could skip the compareMetaData // test and hope that we are usually testing the intended code path. assertEquals(MetadataDiff.SMUDGED, fti.compareMetadata(dce)); assertTrue(fti.isModified(dce, false)); diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java new file mode 100644 index 0000000000..5267e81a15 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2013, Robin Rosenberg + * 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.treewalk.filter; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheEditor; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.errors.StopWalkException; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.junit.Before; +import org.junit.Test; + +public class PathFilterGroupTest { + + private TreeFilter filter; + + @Before + public void setup() { + // @formatter:off + String[] paths = new String[] { + "/a", // never match + "/a/b", // never match + "a", + "b/c", + "c/d/e", + "c/d/f", + "d/e/f/g" + }; + // @formatter:on + filter = PathFilterGroup.createFromStrings(paths); + } + + @Test + public void testExact() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + assertTrue(filter.include(fakeWalk("a"))); + assertTrue(filter.include(fakeWalk("b/c"))); + assertTrue(filter.include(fakeWalk("c/d/e"))); + assertTrue(filter.include(fakeWalk("c/d/f"))); + assertTrue(filter.include(fakeWalk("d/e/f/g"))); + } + + @Test + public void testNoMatchButClose() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + assertFalse(filter.include(fakeWalk("a+"))); + assertFalse(filter.include(fakeWalk("b+/c"))); + assertFalse(filter.include(fakeWalk("c+/d/e"))); + assertFalse(filter.include(fakeWalk("c+/d/f"))); + assertFalse(filter.include(fakeWalk("c/d.a"))); + assertFalse(filter.include(fakeWalk("d+/e/f/g"))); + } + + @Test + public void testJustCommonPrefixIsNotMatch() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + assertFalse(filter.include(fakeWalk("b/a"))); + assertFalse(filter.include(fakeWalk("b/d"))); + assertFalse(filter.include(fakeWalk("c/d/a"))); + assertFalse(filter.include(fakeWalk("d/e/e"))); + } + + @Test + public void testKeyIsPrefixOfFilter() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + assertTrue(filter.include(fakeWalk("b"))); + assertTrue(filter.include(fakeWalk("c/d"))); + assertTrue(filter.include(fakeWalk("c/d"))); + assertTrue(filter.include(fakeWalk("c"))); + assertTrue(filter.include(fakeWalk("d/e/f"))); + assertTrue(filter.include(fakeWalk("d/e"))); + assertTrue(filter.include(fakeWalk("d"))); + } + + @Test + public void testFilterIsPrefixOfKey() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + assertTrue(filter.include(fakeWalk("a/b"))); + assertTrue(filter.include(fakeWalk("b/c/d"))); + assertTrue(filter.include(fakeWalk("c/d/e/f"))); + assertTrue(filter.include(fakeWalk("c/d/f/g"))); + assertTrue(filter.include(fakeWalk("d/e/f/g/h"))); + } + + @Test + public void testStopWalk() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + // Obvious + filter.include(fakeWalk("d/e/f/f")); + + // Obvious + try { + filter.include(fakeWalk("de")); + fail("StopWalkException expected"); + } catch (StopWalkException e) { + // good + } + + // less obvious due to git sorting order + filter.include(fakeWalk("d.")); + + // less obvious due to git sorting order + try { + filter.include(fakeWalk("d0")); + fail("StopWalkException expected"); + } catch (StopWalkException e) { + // good + } + + // non-ascii + try { + filter.include(fakeWalk("\u00C0")); + fail("StopWalkException expected"); + } catch (StopWalkException e) { + // good + } + } + + TreeWalk fakeWalk(final String path) throws IOException { + DirCache dc = DirCache.newInCore(); + DirCacheEditor dce = dc.editor(); + dce.add(new DirCacheEditor.PathEdit(path) { + + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.REGULAR_FILE); + } + }); + dce.finish(); + + TreeWalk ret = new TreeWalk((ObjectReader) null); + ret.reset(); + ret.setRecursive(true); + ret.addTree(new DirCacheIterator(dc)); + ret.next(); + return ret; + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest2.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest2.java new file mode 100644 index 0000000000..6b9d934e4c --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest2.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2013, Robin Rosenberg + * 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.treewalk.filter; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.lessThan; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheEditor; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheIterator; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.junit.Test; + +/** + * Performance test suite for PathFilterGroup + */ +public class PathFilterGroupTest2 { + + private DirCache dc; + + private String[] paths = new String[100000]; + + private TreeFilter pf; + + private String data; + + private long tInit; + + public void setup(int pathsize, int filtersize, String[] filters) { + long t1 = System.nanoTime(); + String[] seed = { "abc", "def", "ghi", "jkl", "mno", "pqr", "stu", + "vwx", "xyz", "\u00e5\u00e4\u00f6" }; + dc = DirCache.newInCore(); + DirCacheEditor ed = dc.editor(); + int pi = 0; + B: for (String a : seed) { + for (String b : seed) { + for (String c : seed) { + for (String d : seed) { + for (String e : seed) { + if (pi >= pathsize) + break B; + String p1 = a + "/" + b + "/" + c + "/" + d + "/" + + e; + paths[pi] = p1; + ed.add(new DirCacheEditor.PathEdit(p1) { + + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.REGULAR_FILE); + } + }); + ++pi; + } + } + } + } + } + ed.finish(); + long t2 = System.nanoTime(); + if (filters != null) + pf = PathFilterGroup.createFromStrings(filters); + else { + // System.out.println(dc.getEntryCount()); + String[] filterPaths = new String[filtersize]; + System.arraycopy(paths, pathsize - filtersize, filterPaths, 0, + filtersize); + pf = PathFilterGroup.createFromStrings(filterPaths); + } + long t3 = System.nanoTime(); + data = "PX\t" + (t2 - t1) / 1E9 + "\t" + (t3 - t2) / 1E9 + "\t"; + tInit = t2-t1; + } + + public void test(int pathSize, int filterSize) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { + setup(pathSize, filterSize, null); + data += pathSize + "\t" + filterSize + "\t"; + long t1 = System.nanoTime(); + TreeWalk tw = new TreeWalk((ObjectReader) null); + tw.reset(); + tw.addTree(new DirCacheIterator(dc)); + tw.setFilter(pf); + tw.setRecursive(true); + int n = 0; + while (tw.next()) + n++; + long t2 = System.nanoTime(); + data += (t2 - t1) / 1E9; + System.out.println(data); + assertEquals(filterSize, n); + } + + @SuppressWarnings("boxing") + public void test(int pathSize, int expectedWalkCount, String... filters) + throws MissingObjectException, IncorrectObjectTypeException, + IOException { + setup(pathSize, -1, filters); + data += pathSize + "\t" + filters.length + "\t"; + long t1 = System.nanoTime(); + TreeWalk tw = new TreeWalk((ObjectReader) null); + tw.reset(); + tw.addTree(new DirCacheIterator(dc)); + tw.setFilter(pf); + tw.setRecursive(true); + int n = 0; + while (tw.next()) + n++; + long t2 = System.nanoTime(); + data += (t2 - t1) / 1E9; + System.out.println(data); + assertEquals(expectedWalkCount, n); + + // Walk time should be (much) faster then setup time + assertThat(t2 - t1, lessThan(tInit / 2)); + } + + @Test + public void test1_1() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(1, 1); + } + + @Test + public void test2_2() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(2, 2); + } + + @Test + public void test10_10() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(10, 10); + } + + @Test + public void test100_100() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100, 100); + } + + @Test + public void test1000_1000() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(1000, 1000); + } + + @Test + public void test10000_10000() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(10000, 10000); + } + + @Test + public void test100000_100000() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 100000); + } + + @Test + public void test100000_10000() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 10000); + } + + @Test + public void test100000_1000() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 1000); + } + + @Test + public void test100000_100() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 100); + } + + @Test + public void test100000_10() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 10); + } + + @Test + public void test100000_1() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 1); + } + + @Test + public void test10000_1F() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 10000, "abc"); + } + + @Test + public void test10000_L2() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 20000, "abc", "def"); + } + + @Test + public void test10000_L10() throws MissingObjectException, + IncorrectObjectTypeException, IOException { + test(100000, 10000, "\u00e5\u00e4\u00f6"); + } +} diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 48cb487543..6673a8cd4e 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -477,6 +477,7 @@ transportProtoLocal=Local Git Repository transportProtoSFTP=SFTP transportProtoSSH=SSH treeEntryAlreadyExists=Tree entry "{0}" already exists. +treeFilterMarkerTooManyFilters=Too many markTreeFilters passed, maximum number is {0} (passed {1}) treeIteratorDoesNotSupportRemove=TreeIterator does not support remove() treeWalkMustHaveExactlyTwoTrees=TreeWalk should have exactly two trees. truncatedHunkLinesMissingForAncestor=Truncated hunk, at least {0} lines missing for ancestor {1} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java index cc8d285d95..a3d4e09d70 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffEntry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010, Google Inc. + * Copyright (C) 2008-2013, Google Inc. * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -55,6 +55,8 @@ import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.MutableObjectId; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.TreeFilter; +import org.eclipse.jgit.treewalk.filter.TreeFilterMarker; /** A value class representing a change to a file */ public class DiffEntry { @@ -123,7 +125,7 @@ public class DiffEntry { * when {@code includeTrees} parameter is {@code true} it can't * be recursive. * @param includeTrees - * include tree object's. + * include tree objects. * @return headers describing the changed files. * @throws IOException * the repository cannot be accessed. @@ -134,6 +136,36 @@ public class DiffEntry { */ public static List<DiffEntry> scan(TreeWalk walk, boolean includeTrees) throws IOException { + return scan(walk, includeTrees, null); + } + + /** + * Convert the TreeWalk into DiffEntry headers, depending on + * {@code includeTrees} it will add tree objects into result or not. + * + * @param walk + * the TreeWalk to walk through. Must have exactly two trees and + * when {@code includeTrees} parameter is {@code true} it can't + * be recursive. + * @param includeTrees + * include tree objects. + * @param markTreeFilters + * array of tree filters which will be tested for each entry. If + * an entry matches, the entry will later return true when + * queried through {{@link #isMarked(int)} (with the index from + * this passed array). + * @return headers describing the changed files. + * @throws IOException + * the repository cannot be accessed. + * @throws IllegalArgumentException + * when {@code includeTrees} is true and given TreeWalk is + * recursive. Or when given TreeWalk doesn't have exactly two + * trees + * @since 2.3 + */ + public static List<DiffEntry> scan(TreeWalk walk, boolean includeTrees, + TreeFilter[] markTreeFilters) + throws IOException { if (walk.getTreeCount() != 2) throw new IllegalArgumentException( JGitText.get().treeWalkMustHaveExactlyTwoTrees); @@ -141,6 +173,12 @@ public class DiffEntry { throw new IllegalArgumentException( JGitText.get().cannotBeRecursiveWhenTreesAreIncluded); + TreeFilterMarker treeFilterMarker; + if (markTreeFilters != null && markTreeFilters.length > 0) + treeFilterMarker = new TreeFilterMarker(markTreeFilters); + else + treeFilterMarker = null; + List<DiffEntry> r = new ArrayList<DiffEntry>(); MutableObjectId idBuf = new MutableObjectId(); while (walk.next()) { @@ -156,6 +194,9 @@ public class DiffEntry { entry.newMode = walk.getFileMode(1); entry.newPath = entry.oldPath = walk.getPathString(); + if (treeFilterMarker != null) + entry.treeFilterMarks = treeFilterMarker.getMarks(walk); + if (entry.oldMode == FileMode.MISSING) { entry.oldPath = DiffEntry.DEV_NULL; entry.changeType = ChangeType.ADD; @@ -295,6 +336,12 @@ public class DiffEntry { protected AbbreviatedObjectId newId; /** + * Bitset for marked flags of tree filters passed to + * {@link #scan(TreeWalk, boolean, TreeFilter...)} + */ + private int treeFilterMarks = 0; + + /** * Get the old name associated with this file. * <p> * The meaning of the old name can differ depending on the semantic meaning @@ -397,6 +444,48 @@ public class DiffEntry { } /** + * Whether the mark tree filter with the specified index matched during scan + * or not, see {@link #scan(TreeWalk, boolean, TreeFilter...)}. Example: + * <p> + * + * <pre> + * TreeFilter filterA = ...; + * TreeFilter filterB = ...; + * List<DiffEntry> entries = DiffEntry.scan(walk, false, filterA, filterB); + * DiffEntry entry = entries.get(0); + * boolean filterAMatched = entry.isMarked(0); + * boolean filterBMatched = entry.isMarked(1); + * </pre> + * <p> + * Note that 0 corresponds to filterA because it was the first filter that + * was passed to scan. + * <p> + * To query more than one flag at once, see {@link #getTreeFilterMarks()}. + * + * @param index + * the index of the tree filter to check for (must be between 0 + * and {@link Integer#SIZE}). + * + * @return true, if the tree filter matched; false if not + * @since 2.3 + */ + public boolean isMarked(int index) { + return (treeFilterMarks & (1L << index)) != 0; + } + + /** + * Get the raw tree filter marks, as set during + * {@link #scan(TreeWalk, boolean, TreeFilter...)}. See + * {@link #isMarked(int)} to query each mark individually. + * + * @return the bitset of tree filter marks + * @since 2.3 + */ + public int getTreeFilterMarks() { + return treeFilterMarks; + } + + /** * Get the object id. * * @param side diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 2876843926..d402f139ff 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010, Sasa Zivkov <sasa.zivkov@sap.com> + * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com> * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -538,6 +538,7 @@ public class JGitText extends TranslationBundle { /***/ public String transportProtoSFTP; /***/ public String transportProtoSSH; /***/ public String treeEntryAlreadyExists; + /***/ public String treeFilterMarkerTooManyFilters; /***/ public String treeIteratorDoesNotSupportRemove; /***/ public String treeWalkMustHaveExactlyTwoTrees; /***/ public String truncatedHunkLinesMissingForAncestor; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsGarbageCollector.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsGarbageCollector.java index 21f01ad6b4..76fb521a62 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsGarbageCollector.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/dfs/DfsGarbageCollector.java @@ -61,6 +61,7 @@ import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectIdOwnerMap; import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevWalk; @@ -332,10 +333,11 @@ public class DfsGarbageCollector { out.close(); } - final List<ObjectId> packedObjs = pw.getObjectList(); + final ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> packedObjs = pw + .getObjectSet(); newPackObj.add(new PackWriter.ObjectIdSet() { public boolean contains(AnyObjectId objectId) { - return 0 <= Collections.binarySearch(packedObjs, objectId); + return packedObjs.contains(objectId); } }); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java index b199d4feef..1cf1781289 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/storage/pack/PackWriter.java @@ -519,8 +519,7 @@ public class PackWriter { } /** - * Returns the object ids in the pack file that was created by this writer, - * sorted by name. + * Returns the object ids in the pack file that was created by this writer. * * This method can only be invoked after * {@link #writePack(ProgressMonitor, ProgressMonitor, OutputStream)} has @@ -530,13 +529,23 @@ public class PackWriter { * @throws IOException * a cached pack cannot supply its object ids. */ - public List<ObjectId> getObjectList() throws IOException { + public ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> getObjectSet() + throws IOException { if (!cachedPacks.isEmpty()) throw new IOException( JGitText.get().cachedPacksPreventsListingObjects); - return Collections.unmodifiableList( - (List<? extends ObjectId>) sortByName()); + ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> objs = new ObjectIdOwnerMap< + ObjectIdOwnerMap.Entry>(); + for (BlockList<ObjectToPack> objList : objectsLists) { + if (objList != null) { + for (ObjectToPack otp : objList) + objs.add(new ObjectIdOwnerMap.Entry(otp) { + // A new entry that copies the ObjectId + }); + } + } + return objs; } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java new file mode 100644 index 0000000000..0df24af24f --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/ByteArraySet.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2009, Google Inc. + * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> + * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> + * Copyright (C) 2013, Robin Rosenberg + * 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.treewalk.filter; + +import org.eclipse.jgit.util.RawParseUtils; + +/** + * Specialized set for byte arrays, interpreted as strings for use in + * {@link PathFilterGroup.Group}. Most methods assume the hash is already know + * and therefore requires the caller to supply it beforehand. The implementation + * is a loose derivative of ObjectIdSubclassMap. + */ +class ByteArraySet { + + private int size; + + private int grow; + + private int mask; + + private byte[][] table; + + /** + * Create an empty set. + * + * @param capacity + */ + ByteArraySet(int capacity) { + initTable(1 << Integer.highestOneBit((capacity * 2) - 1)); + } + + private byte[] get(final byte[] toFind, int length, int hash) { + final int msk = mask; + int i = hash & msk; + final byte[][] tbl = table; + byte[] obj; + + while ((obj = tbl[i]) != null) { + if (equals(obj, toFind, length)) + return obj; + i = (i + 1) & msk; + } + return null; + } + + private static boolean equals(byte[] a, byte[] b, int length) { + if (a.length < length || b.length < length) + return false; + for (int i = 0; i < length; ++i) { + if (a[i] != b[i]) + return false; + } + return true; + } + + /** + * Returns true if this set contains the specified array. + * + * @param toFind + * array to find. + * @param length + * The number of bytes in toFind that are used + * @param hash + * pre-computed hash of toFind + * @return true if the mapping exists for this byte array; false otherwise. + */ + boolean contains(final byte[] toFind, int length, int hash) { + return get(toFind, length, hash) != null; + } + + /** + * Store a byte array for future lookup. + * <p> + * Stores {@code newValue}, but only if it does not already exist in the + * set. Callers can tell if the value is new by checking the return value + * with reference equality: + * + * <pre> + * byte[] obj = ...; + * boolean wasNew = map.addIfAbsent(array, length, hash) == array; + * </pre> + * + * @param newValue + * the array to store. + * @param length + * The number of bytes in newValue that are used + * @param hash + * pre-computed hash of toFind + * @return {@code newValue} if stored, or the prior value already stored and + * that would have been returned had the caller used + * {@code get(newValue)} first. + */ + byte[] addIfAbsent(final byte[] newValue, int length, int hash) { + final int msk = mask; + int i = hash & msk; + final byte[][] tbl = table; + byte[] obj; + + while ((obj = tbl[i]) != null) { + if (equals(obj, newValue, length)) + return obj; + i = (i + 1) & msk; + } + + byte[] valueToInsert = copyIfNotSameSize(newValue, length); + if (++size == grow) { + grow(); + insert(valueToInsert, hash); + } else + tbl[i] = valueToInsert; + return valueToInsert; + } + + private static byte[] copyIfNotSameSize(byte[] newValue, int length) { + if (newValue.length == length) + return newValue; + byte[] ret = new byte[length]; + System.arraycopy(newValue, 0, ret, 0, length); + return ret; + } + + /** + * @return number of arrays in the set + */ + int size() { + return size; + } + + /** @return true if {@link #size()} is 0. */ + boolean isEmpty() { + return size == 0; + } + + private void insert(final byte[] newValue, int hash) { + final int msk = mask; + int j = hash & msk; + final byte[][] tbl = table; + while (tbl[j] != null) + j = (j + 1) & msk; + tbl[j] = newValue; + } + + private Hasher hasher = new Hasher(null, 0); + + private void grow() { + final byte[][] oldTable = table; + final int oldSize = table.length; + + initTable(oldSize << 1); + for (int i = 0; i < oldSize; i++) { + final byte[] obj = oldTable[i]; + if (obj != null) { + hasher.init(obj, obj.length); + insert(obj, hasher.hash()); + } + } + } + + private void initTable(int sz) { + if (sz < 2) + sz = 2; + grow = sz >> 1; + mask = sz - 1; + table = new byte[sz][]; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (byte[] b : table) { + if (b == null) + continue; + if (sb.length() > 1) + sb.append(" , "); //$NON-NLS-1$ + sb.append('"'); + sb.append(RawParseUtils.decode(b)); + sb.append('"'); + sb.append('('); + sb.append(chainlength(b)); + sb.append(')'); + } + sb.append(']'); + return sb.toString(); + } + + private int chainlength(byte[] b) { + Hasher h = new Hasher(b, b.length); + int hash = h.hash(); + final int msk = mask; + int i = hash & msk; + final byte[][] tbl = table; + byte[] obj; + + int n = 0; + while ((obj = tbl[i]) != null) { + if (equals(obj, b, b.length)) + return n; + i = (i + 1) & msk; + ++n; + } + return -1; + } + + static class Hasher { + private int hash; + + private int pos; + + private byte[] data; + + private int length; + + Hasher(byte[] data, int length) { + init(data, length); + } + + void init(byte[] d, int l) { + this.data = d; + this.length = l; + pos = 0; + hash = 0; + } + + int hash() { + while (pos < length) + hash = hash * 31 + data[pos++]; + return hash; + } + + int nextHash() { + for (;;) { + hash = hash * 31 + data[pos]; + ++pos; + if (pos == length || data[pos] == '/') + return hash; + } + } + + int getHash() { + return hash; + } + + boolean hasNext() { + return pos < length; + } + + public int length() { + return pos; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < pos; ++i) + sb.append((char) data[i]); + sb.append(" | "); //$NON-NLS-1$ + for (int i = pos; i < length; ++i) + sb.append((char) data[i]); + return sb.toString(); + } + } + + byte[][] toArray() { + byte[][] ret = new byte[size][]; + int i = 0; + for (byte[] entry : table) { + if (entry != null) + ret[i++] = entry; + } + return ret; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java index 51761a8126..66d9f87a77 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java @@ -44,13 +44,13 @@ package org.eclipse.jgit.treewalk.filter; -import java.util.Arrays; import java.util.Collection; -import java.util.Comparator; import org.eclipse.jgit.errors.StopWalkException; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.ByteArraySet.Hasher; +import org.eclipse.jgit.util.RawParseUtils; /** * Includes tree entries only if they match one or more configured paths. @@ -83,7 +83,8 @@ public class PathFilterGroup { */ public static TreeFilter createFromStrings(final Collection<String> paths) { if (paths.isEmpty()) - throw new IllegalArgumentException(JGitText.get().atLeastOnePathIsRequired); + throw new IllegalArgumentException( + JGitText.get().atLeastOnePathIsRequired); final PathFilter[] p = new PathFilter[paths.size()]; int i = 0; for (final String s : paths) @@ -131,7 +132,8 @@ public class PathFilterGroup { */ public static TreeFilter create(final Collection<PathFilter> paths) { if (paths.isEmpty()) - throw new IllegalArgumentException(JGitText.get().atLeastOnePathIsRequired); + throw new IllegalArgumentException( + JGitText.get().atLeastOnePathIsRequired); final PathFilter[] p = new PathFilter[paths.size()]; paths.toArray(p); return create(p); @@ -177,41 +179,74 @@ public class PathFilterGroup { } static class Group extends TreeFilter { - private static final Comparator<PathFilter> PATH_SORT = new Comparator<PathFilter>() { - public int compare(final PathFilter o1, final PathFilter o2) { - return o1.pathStr.compareTo(o2.pathStr); - } - }; - private final PathFilter[] paths; + private ByteArraySet fullpaths; + + private ByteArraySet prefixes; + + private byte[] max; + + private Group(final PathFilter[] pathFilters) { + fullpaths = new ByteArraySet(pathFilters.length); + prefixes = new ByteArraySet(pathFilters.length / 5); + // 5 is an empirically derived ratio of #paths/#prefixes from: + // egit/jgit: 8 + // git: 5 + // linux kernel: 13 + // eclipse.platform.ui: 7 + max = pathFilters[0].pathRaw; + Hasher hasher = new Hasher(null, 0); + for (PathFilter pf : pathFilters) { + hasher.init(pf.pathRaw, pf.pathRaw.length); + while (hasher.hasNext()) { + int hash = hasher.nextHash(); + if (hasher.hasNext()) + prefixes.addIfAbsent(pf.pathRaw, hasher.length(), hash); + } + fullpaths.addIfAbsent(pf.pathRaw, pf.pathRaw.length, + hasher.getHash()); + if (compare(max, pf.pathRaw) < 0) + max = pf.pathRaw; + } + } - private Group(final PathFilter[] p) { - paths = p; - Arrays.sort(paths, PATH_SORT); + private static int compare(byte[] a, byte[] b) { + int i = 0; + while (i < a.length && i < b.length) { + int ba = a[i] & 0xFF; + int bb = b[i] & 0xFF; + int cmp = ba - bb; + if (cmp != 0) + return cmp; + ++i; + } + return a.length - b.length; } @Override public boolean include(final TreeWalk walker) { - final int n = paths.length; - for (int i = 0;;) { - final byte[] r = paths[i].pathRaw; - final int cmp = walker.isPathPrefix(r, r.length); - if (cmp == 0) + + byte[] rp = walker.getRawPath(); + Hasher hasher = new Hasher(rp, walker.getPathLength()); + while (hasher.hasNext()) { + int hash = hasher.nextHash(); + if (fullpaths.contains(rp, hasher.length(), hash)) return true; - if (++i < n) - continue; - if (cmp > 0) - throw StopWalkException.INSTANCE; - return false; + if (!hasher.hasNext()) + if (prefixes.contains(rp, hasher.length(), hash)) + return true; } + + final int cmp = walker.isPathPrefix(max, max.length); + if (cmp > 0) + throw StopWalkException.INSTANCE; + + return false; } @Override public boolean shouldBeRecursive() { - for (final PathFilter p : paths) - if (p.shouldBeRecursive()) - return true; - return false; + return !prefixes.isEmpty(); } @Override @@ -222,13 +257,17 @@ public class PathFilterGroup { public String toString() { final StringBuilder r = new StringBuilder(); r.append("FAST("); //$NON-NLS-1$ - for (int i = 0; i < paths.length; i++) { - if (i > 0) + boolean first = true; + for (byte[] p : fullpaths.toArray()) { + if (!first) { r.append(" OR "); //$NON-NLS-1$ - r.append(paths[i].toString()); + } + r.append(RawParseUtils.decode(p)); + first = false; } r.append(")"); //$NON-NLS-1$ return r.toString(); } } + } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java new file mode 100644 index 0000000000..59515dca52 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/TreeFilterMarker.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013, Robin Stocker <robin@nibor.org> + * 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.treewalk.filter; + +import java.io.IOException; +import java.text.MessageFormat; + +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.errors.StopWalkException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.treewalk.TreeWalk; + +/** + * For testing an array of {@link TreeFilter} during a {@link TreeWalk} for each + * entry and returning the result as a bitmask. + * + * @since 2.3 + */ +public class TreeFilterMarker { + + private final TreeFilter[] filters; + + /** + * Construct a TreeFilterMarker. Note that it is stateful and can only be + * used for one walk loop. + * + * @param markTreeFilters + * the filters to use for marking, must not have more elements + * than {@link Integer#SIZE}. + * @throws IllegalArgumentException + * if more tree filters are passed than possible + */ + public TreeFilterMarker(TreeFilter[] markTreeFilters) { + if (markTreeFilters.length > Integer.SIZE) { + throw new IllegalArgumentException(MessageFormat.format( + JGitText.get().treeFilterMarkerTooManyFilters, + Integer.valueOf(Integer.SIZE), + Integer.valueOf(markTreeFilters.length))); + } + filters = new TreeFilter[markTreeFilters.length]; + System.arraycopy(markTreeFilters, 0, filters, 0, markTreeFilters.length); + } + + /** + * Test the filters against the walk. Returns a bitmask where each bit + * represents the result of a call to {@link TreeFilter#include(TreeWalk)}, + * ordered by the index for which the tree filters were passed in the + * constructor. + * + * @param walk + * the walk from which to test the current entry + * @return the marks bitmask + * @throws MissingObjectException + * as thrown by {@link TreeFilter#include(TreeWalk)} + * @throws IncorrectObjectTypeException + * as thrown by {@link TreeFilter#include(TreeWalk)} + * @throws IOException + * as thrown by {@link TreeFilter#include(TreeWalk)} + */ + public int getMarks(TreeWalk walk) throws MissingObjectException, + IncorrectObjectTypeException, IOException { + int marks = 0; + for (int index = 0; index < filters.length; index++) { + TreeFilter filter = filters[index]; + if (filter != null) { + try { + boolean marked = filter.include(walk); + if (marked) + marks |= (1L << index); + } catch (StopWalkException e) { + // Don't check tree filter anymore, it will no longer + // match + filters[index] = null; + } + } + } + return marks; + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java index ce62628e85..73f9161ca3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java @@ -90,7 +90,7 @@ class FS_Win32 extends FS { String path = SystemReader.getInstance().getenv("PATH"); //$NON-NLS-1$ File gitExe = searchPath(path, "git.exe", "git.cmd"); //$NON-NLS-1$ //$NON-NLS-2$ if (gitExe != null) - return gitExe.getParentFile().getParentFile(); + return resolveGrandparentFile(gitExe); // This isn't likely to work, if bash is in $PATH, git should // also be in $PATH. But its worth trying. @@ -102,7 +102,16 @@ class FS_Win32 extends FS { // The path may be in cygwin/msys notation so resolve it right away gitExe = resolve(null, w); if (gitExe != null) - return gitExe.getParentFile().getParentFile(); + return resolveGrandparentFile(gitExe); + } + return null; + } + + private static File resolveGrandparentFile(File grandchild) { + if (grandchild != null) { + File parent = grandchild.getParentFile(); + if (parent != null) + return parent.getParentFile(); } return null; } @@ -115,7 +124,8 @@ class FS_Win32 extends FS { String homeDrive = SystemReader.getInstance().getenv("HOMEDRIVE"); //$NON-NLS-1$ if (homeDrive != null) { String homePath = SystemReader.getInstance().getenv("HOMEPATH"); //$NON-NLS-1$ - return new File(homeDrive, homePath); + if (homePath != null) + return new File(homeDrive, homePath); } String homeShare = SystemReader.getInstance().getenv("HOMESHARE"); //$NON-NLS-1$ @@ -73,6 +73,12 @@ <name>Christian Halstrick</name> </developer> <developer> + <name>Colby Ranger</name> + </developer> + <developer> + <name>Dave Borowitz</name> + </developer> + <developer> <name>Gunnar Wagenknecht</name> </developer> <developer> @@ -170,7 +176,7 @@ <bundle-manifest>${project.build.directory}/META-INF/MANIFEST.MF</bundle-manifest> <jgit-last-release-version>2.1.0.201209190230-r</jgit-last-release-version> - <jsch-version>0.1.44-1</jsch-version> + <jsch-version>0.1.46</jsch-version> <junit-version>4.5</junit-version> <args4j-version>2.0.12</args4j-version> <commons-compress-version>1.3</commons-compress-version> |