summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.test/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/BasicRuleTest.java74
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java561
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java371
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java9
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java904
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java229
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java12
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java88
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java80
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java112
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java239
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java373
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java74
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java75
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java4
18 files changed, 3205 insertions, 16 deletions
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index d493ae4e10..7b9bd64bc1 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -20,6 +20,7 @@ Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
org.eclipse.jgit.fnmatch;version="[3.6.0,3.7.0)",
org.eclipse.jgit.gitrepo;version="[3.6.0,3.7.0)",
org.eclipse.jgit.ignore;version="[3.6.0,3.7.0)",
+ org.eclipse.jgit.ignore.internal;version="[3.6.0,3.7.0)",
org.eclipse.jgit.internal;version="[3.6.0,3.7.0)",
org.eclipse.jgit.internal.storage.dfs;version="[3.6.0,3.7.0)",
org.eclipse.jgit.internal.storage.file;version="[3.6.0,3.7.0)",
@@ -47,5 +48,6 @@ Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
org.hamcrest;version="[1.1.0,2.0.0)",
org.junit;version="[4.4.0,5.0.0)",
org.junit.experimental.theories;version="[4.4.0,5.0.0)",
- org.junit.runner;version="[4.4.0,5.0.0)"
+ org.junit.runner;version="[4.4.0,5.0.0)",
+ org.junit.runners;version="[4.11.0,5.0.0)"
Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)"
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/BasicRuleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/BasicRuleTest.java
new file mode 100644
index 0000000000..2349b55c64
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/BasicRuleTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore;
+
+import static org.junit.Assert.*;
+
+import org.eclipse.jgit.ignore.FastIgnoreRule;
+import org.junit.Test;
+
+public class BasicRuleTest {
+
+ @Test
+ public void test() {
+ FastIgnoreRule rule1 = new FastIgnoreRule("/hello/[a]/");
+ FastIgnoreRule rule2 = new FastIgnoreRule("/hello/[a]/");
+ FastIgnoreRule rule3 = new FastIgnoreRule("!/hello/[a]/");
+ FastIgnoreRule rule4 = new FastIgnoreRule("/hello/[a]");
+ assertTrue(rule1.dirOnly());
+ assertTrue(rule3.dirOnly());
+ assertFalse(rule4.dirOnly());
+ assertFalse(rule1.getNegation());
+ assertTrue(rule3.getNegation());
+ assertNotEquals(rule1, null);
+ assertEquals(rule1, rule1);
+ assertEquals(rule1, rule2);
+ assertNotEquals(rule1, rule3);
+ assertNotEquals(rule1, rule4);
+ assertEquals(rule1.hashCode(), rule2.hashCode());
+ assertNotEquals(rule1.hashCode(), rule3.hashCode());
+ assertEquals(rule1.toString(), rule2.toString());
+ assertNotEquals(rule1.toString(), rule3.toString());
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
new file mode 100644
index 0000000000..dab4e351a3
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/FastIgnoreRuleTest.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore;
+
+import static org.eclipse.jgit.ignore.internal.Strings.split;
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.ignore.FastIgnoreRule;
+import org.eclipse.jgit.ignore.IgnoreRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.*;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@SuppressWarnings("deprecation")
+@RunWith(Parameterized.class)
+public class FastIgnoreRuleTest {
+
+ @Parameters(name = "JGit? {0}")
+ public static Iterable<Boolean[]> data() {
+ return Arrays.asList(new Boolean[][] { { Boolean.FALSE },
+ { Boolean.TRUE } });
+ }
+
+ @Parameter
+ public Boolean useJGitRule;
+
+ @Test
+ public void testSimpleCharClass() {
+ assertMatched("[a]", "a");
+ assertMatched("[a]", "a/");
+ assertMatched("[a]", "a/b");
+
+ assertMatched("[a]", "b/a");
+ assertMatched("[a]", "b/a/");
+ assertMatched("[a]", "b/a/b");
+
+ assertMatched("[a]", "/a/");
+ assertMatched("[a]", "/a/b");
+
+ assertMatched("[a]", "c/a/b");
+ assertMatched("[a]", "c/b/a");
+
+ assertMatched("/[a]", "a");
+ assertMatched("/[a]", "a/");
+ assertMatched("/[a]", "a/b");
+ assertMatched("/[a]", "/a");
+ assertMatched("/[a]", "/a/");
+ assertMatched("/[a]", "/a/b");
+
+ assertMatched("[a]/", "a/");
+ assertMatched("[a]/", "a/b");
+ assertMatched("[a]/", "/a/");
+ assertMatched("[a]/", "/a/b");
+
+ assertMatched("/[a]/", "a/");
+ assertMatched("/[a]/", "a/b");
+ assertMatched("/[a]/", "/a/");
+ assertMatched("/[a]/", "/a/b");
+ }
+
+ @Test
+ public void testCharClass() {
+ assertMatched("[v-z]", "x");
+ assertMatched("[v-z]", "x/");
+ assertMatched("[v-z]", "x/b");
+
+ assertMatched("[v-z]", "b/x");
+ assertMatched("[v-z]", "b/x/");
+ assertMatched("[v-z]", "b/x/b");
+
+ assertMatched("[v-z]", "/x/");
+ assertMatched("[v-z]", "/x/b");
+
+ assertMatched("[v-z]", "c/x/b");
+ assertMatched("[v-z]", "c/b/x");
+
+ assertMatched("/[v-z]", "x");
+ assertMatched("/[v-z]", "x/");
+ assertMatched("/[v-z]", "x/b");
+ assertMatched("/[v-z]", "/x");
+ assertMatched("/[v-z]", "/x/");
+ assertMatched("/[v-z]", "/x/b");
+
+ assertMatched("[v-z]/", "x/");
+ assertMatched("[v-z]/", "x/b");
+ assertMatched("[v-z]/", "/x/");
+ assertMatched("[v-z]/", "/x/b");
+
+ assertMatched("/[v-z]/", "x/");
+ assertMatched("/[v-z]/", "x/b");
+ assertMatched("/[v-z]/", "/x/");
+ assertMatched("/[v-z]/", "/x/b");
+ }
+
+ @Test
+ public void testAsteriskDot() {
+ assertMatched("*.a", ".a");
+ assertMatched("*.a", "/.a");
+ assertMatched("*.a", "a.a");
+ assertMatched("*.a", "/b.a");
+ assertMatched("*.a", "b.a");
+ assertMatched("*.a", "/a/b.a");
+ assertMatched("*.a", "/b/.a");
+ }
+
+ @Test
+ public void testAsteriskDotDoNotMatch() {
+ assertNotMatched("*.a", ".ab");
+ assertNotMatched("*.a", "/.ab");
+ assertNotMatched("*.a", "/b.ba");
+ assertNotMatched("*.a", "a.ab");
+ assertNotMatched("*.a", "/b.ab");
+ assertNotMatched("*.a", "b.ab");
+ assertNotMatched("*.a", "/a/b.ab");
+ assertNotMatched("*.a", "/b/.ab");
+ }
+
+ @Test
+ public void testDotAsteriskMatch() {
+ assertMatched("a.*", "a.");
+ assertMatched("a.*", "a./");
+ assertMatched("a.*", "a.b");
+
+ assertMatched("a.*", "b/a.b");
+ assertMatched("a.*", "b/a.b/");
+ assertMatched("a.*", "b/a.b/b");
+
+ assertMatched("a.*", "/a.b/");
+ assertMatched("a.*", "/a.b/b");
+
+ assertMatched("a.*", "c/a.b/b");
+ assertMatched("a.*", "c/b/a.b");
+
+ assertMatched("/a.*", "a.b");
+ assertMatched("/a.*", "a.b/");
+ assertMatched("/a.*", "a.b/b");
+ assertMatched("/a.*", "/a.b");
+ assertMatched("/a.*", "/a.b/");
+ assertMatched("/a.*", "/a.b/b");
+
+ assertMatched("/a.*/b", "a.b/b");
+ assertMatched("/a.*/b", "/a.b/b");
+ assertMatched("/a.*/b", "/a.bc/b");
+ assertMatched("/a.*/b", "/a./b");
+ }
+
+ @Test
+ public void testAsterisk() {
+ assertMatched("a*", "a");
+ assertMatched("a*", "a/");
+ assertMatched("a*", "ab");
+
+ assertMatched("a*", "b/ab");
+ assertMatched("a*", "b/ab/");
+ assertMatched("a*", "b/ab/b");
+
+ assertMatched("a*", "b/abc");
+ assertMatched("a*", "b/abc/");
+ assertMatched("a*", "b/abc/b");
+
+ assertMatched("a*", "/abc/");
+ assertMatched("a*", "/abc/b");
+
+ assertMatched("a*", "c/abc/b");
+ assertMatched("a*", "c/b/abc");
+
+ assertMatched("/a*", "abc");
+ assertMatched("/a*", "abc/");
+ assertMatched("/a*", "abc/b");
+ assertMatched("/a*", "/abc");
+ assertMatched("/a*", "/abc/");
+ assertMatched("/a*", "/abc/b");
+
+ assertMatched("/a*/b", "abc/b");
+ assertMatched("/a*/b", "/abc/b");
+ assertMatched("/a*/b", "/abcd/b");
+ assertMatched("/a*/b", "/a/b");
+ }
+
+ @Test
+ public void testQuestionmark() {
+ assertMatched("a?", "ab");
+ assertMatched("a?", "ab/");
+
+ assertMatched("a?", "b/ab");
+ assertMatched("a?", "b/ab/");
+ assertMatched("a?", "b/ab/b");
+
+ assertMatched("a?", "/ab/");
+ assertMatched("a?", "/ab/b");
+
+ assertMatched("a?", "c/ab/b");
+ assertMatched("a?", "c/b/ab");
+
+ assertMatched("/a?", "ab");
+ assertMatched("/a?", "ab/");
+ assertMatched("/a?", "ab/b");
+ assertMatched("/a?", "/ab");
+ assertMatched("/a?", "/ab/");
+ assertMatched("/a?", "/ab/b");
+
+ assertMatched("/a?/b", "ab/b");
+ assertMatched("/a?/b", "/ab/b");
+ }
+
+ @Test
+ public void testQuestionmarkDoNotMatch() {
+ assertNotMatched("a?", "a/");
+ assertNotMatched("a?", "abc");
+ assertNotMatched("a?", "abc/");
+
+ assertNotMatched("a?", "b/abc");
+ assertNotMatched("a?", "b/abc/");
+
+ assertNotMatched("a?", "/abc/");
+ assertNotMatched("a?", "/abc/b");
+
+ assertNotMatched("a?", "c/abc/b");
+ assertNotMatched("a?", "c/b/abc");
+
+ assertNotMatched("/a?", "abc");
+ assertNotMatched("/a?", "abc/");
+ assertNotMatched("/a?", "abc/b");
+ assertNotMatched("/a?", "/abc");
+ assertNotMatched("/a?", "/abc/");
+ assertNotMatched("/a?", "/abc/b");
+
+ assertNotMatched("/a?/b", "abc/b");
+ assertNotMatched("/a?/b", "/abc/b");
+ assertNotMatched("/a?/b", "/a/b");
+ }
+
+ @Test
+ public void testSimplePatterns() {
+ assertMatched("a", "a");
+ assertMatched("a", "a/");
+ assertMatched("a", "a/b");
+
+ assertMatched("a", "b/a");
+ assertMatched("a", "b/a/");
+ assertMatched("a", "b/a/b");
+
+ assertMatched("a", "/a/");
+ assertMatched("a", "/a/b");
+
+ assertMatched("a", "c/a/b");
+ assertMatched("a", "c/b/a");
+
+ assertMatched("/a", "a");
+ assertMatched("/a", "a/");
+ assertMatched("/a", "a/b");
+ assertMatched("/a", "/a");
+ assertMatched("/a", "/a/");
+ assertMatched("/a", "/a/b");
+
+ assertMatched("a/", "a/");
+ assertMatched("a/", "a/b");
+ assertMatched("a/", "/a/");
+ assertMatched("a/", "/a/b");
+
+ assertMatched("/a/", "a/");
+ assertMatched("/a/", "a/b");
+ assertMatched("/a/", "/a/");
+ assertMatched("/a/", "/a/b");
+
+ }
+
+ @Test
+ public void testSimplePatternsDoNotMatch() {
+ assertNotMatched("ab", "a");
+ assertNotMatched("abc", "a/");
+ assertNotMatched("abc", "a/b");
+
+ assertNotMatched("a", "ab");
+ assertNotMatched("a", "ba");
+ assertNotMatched("a", "aa");
+
+ assertNotMatched("a", "b/ab");
+ assertNotMatched("a", "b/ba");
+
+ assertNotMatched("a", "b/ba");
+ assertNotMatched("a", "b/ab");
+
+ assertNotMatched("a", "b/ba/");
+ assertNotMatched("a", "b/ba/b");
+
+ assertNotMatched("a", "/aa");
+ assertNotMatched("a", "aa/");
+ assertNotMatched("a", "/aa/");
+
+ assertNotMatched("/a", "b/a");
+ assertNotMatched("/a", "/b/a/");
+
+ assertNotMatched("a/", "a");
+ assertNotMatched("a/", "b/a");
+
+ assertNotMatched("/a/", "a");
+ assertNotMatched("/a/", "/a");
+ assertNotMatched("/a/", "b/a");
+ }
+
+ @Test
+ public void testSegments() {
+ assertMatched("/a/b", "a/b");
+ assertMatched("/a/b", "/a/b");
+ assertMatched("/a/b", "/a/b/");
+ assertMatched("/a/b", "/a/b/c");
+
+ assertMatched("a/b", "a/b");
+ assertMatched("a/b", "/a/b");
+ assertMatched("a/b", "/a/b/");
+ assertMatched("a/b", "/a/b/c");
+
+ assertMatched("a/b/", "a/b/");
+ assertMatched("a/b/", "/a/b/");
+ assertMatched("a/b/", "/a/b/c");
+ }
+
+ @Test
+ public void testSegmentsDoNotMatch() {
+ assertNotMatched("a/b", "/a/bb");
+ assertNotMatched("a/b", "/aa/b");
+ assertNotMatched("a/b", "a/bb");
+ assertNotMatched("a/b", "aa/b");
+ assertNotMatched("a/b", "c/aa/b");
+ assertNotMatched("a/b", "c/a/bb");
+ assertNotMatched("a/b/", "/a/b");
+ assertNotMatched("/a/b/", "/a/b");
+ assertNotMatched("/a/b", "c/a/b");
+ assertNotMatched("/a/b/", "c/a/b");
+ assertNotMatched("/a/b/", "c/a/b/");
+
+ // XXX why is it like this????
+ assertNotMatched("a/b", "c/a/b");
+ assertNotMatched("a/b", "c/a/b/");
+ assertNotMatched("a/b", "c/a/b/c");
+ assertNotMatched("a/b/", "c/a/b/");
+ assertNotMatched("a/b/", "c/a/b/c");
+ }
+
+ @SuppressWarnings("boxing")
+ @Test
+ public void testWildmatch() {
+ if (useJGitRule)
+ System.err
+ .println("IgnoreRule can't understand wildmatch rules, skipping testWildmatch!");
+
+ Boolean assume = useJGitRule;
+ assertMatched("**/a/b", "a/b", assume);
+ assertMatched("**/a/b", "c/a/b", assume);
+ assertMatched("**/a/b", "c/d/a/b", assume);
+ assertMatched("**/**/a/b", "c/d/a/b", assume);
+
+ assertMatched("/**/a/b", "a/b", assume);
+ assertMatched("/**/a/b", "c/a/b", assume);
+ assertMatched("/**/a/b", "c/d/a/b", assume);
+ assertMatched("/**/**/a/b", "c/d/a/b", assume);
+
+ assertMatched("a/b/**", "a/b", assume);
+ assertMatched("a/b/**", "a/b/c", assume);
+ assertMatched("a/b/**", "a/b/c/d/", assume);
+ assertMatched("a/b/**/**", "a/b/c/d", assume);
+
+ assertMatched("**/a/**/b", "c/d/a/b", assume);
+ assertMatched("**/a/**/b", "c/d/a/e/b", assume);
+ assertMatched("**/**/a/**/**/b", "c/d/a/e/b", assume);
+
+ assertMatched("/**/a/**/b", "c/d/a/b", assume);
+ assertMatched("/**/a/**/b", "c/d/a/e/b", assume);
+ assertMatched("/**/**/a/**/**/b", "c/d/a/e/b", assume);
+
+ assertMatched("a/**/b", "a/b", assume);
+ assertMatched("a/**/b", "a/c/b", assume);
+ assertMatched("a/**/b", "a/c/d/b", assume);
+ assertMatched("a/**/**/b", "a/c/d/b", assume);
+
+ assertMatched("a/**/b/**/c", "a/c/b/d/c", assume);
+ assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c", assume);
+ }
+
+ @SuppressWarnings("boxing")
+ @Test
+ public void testWildmatchDoNotMatch() {
+ if (useJGitRule)
+ System.err
+ .println("IgnoreRule can't understand wildmatch rules, skipping testWildmatchDoNotMatch!");
+
+ Boolean assume = useJGitRule;
+ assertNotMatched("**/a/b", "a/c/b", assume);
+ assertNotMatched("!/**/*.zip", "c/a/b.zip", assume);
+ assertNotMatched("!**/*.zip", "c/a/b.zip", assume);
+ assertNotMatched("a/**/b", "a/c/bb", assume);
+ }
+
+ @SuppressWarnings("unused")
+ @Test
+ public void testSimpleRules() {
+ try {
+ new FastIgnoreRule(null);
+ fail("Illegal input allowed!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ assertFalse(new FastIgnoreRule("/").isMatch("/", false));
+ assertFalse(new FastIgnoreRule("//").isMatch("//", false));
+ assertFalse(new FastIgnoreRule("#").isMatch("#", false));
+ assertFalse(new FastIgnoreRule("").isMatch("", false));
+ assertFalse(new FastIgnoreRule(" ").isMatch(" ", false));
+ }
+
+ @Test
+ public void testSplit() {
+ try {
+ split("/", '/').toArray();
+ fail("should not allow single slash");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+
+ assertArrayEquals(new String[] { "a", "b" }, split("a/b", '/')
+ .toArray());
+ assertArrayEquals(new String[] { "a", "b/" }, split("a/b/", '/')
+ .toArray());
+ assertArrayEquals(new String[] { "/a", "b" }, split("/a/b", '/')
+ .toArray());
+ assertArrayEquals(new String[] { "/a", "b/" }, split("/a/b/", '/')
+ .toArray());
+ assertArrayEquals(new String[] { "/a", "b", "c" }, split("/a/b/c", '/')
+ .toArray());
+ assertArrayEquals(new String[] { "/a", "b", "c/" },
+ split("/a/b/c/", '/').toArray());
+ }
+
+ public void assertMatched(String pattern, String path, Boolean... assume) {
+ boolean match = match(pattern, path);
+ String result = path + " is " + (match ? "ignored" : "not ignored")
+ + " via '" + pattern + "' rule";
+ if (!match)
+ System.err.println(result);
+ if (assume.length == 0 || !assume[0].booleanValue())
+ assertTrue("Expected a match for: " + pattern + " with: " + path,
+ match);
+ else
+ assumeTrue("Expected a match for: " + pattern + " with: " + path,
+ match);
+
+ if (pattern.startsWith("!"))
+ pattern = pattern.substring(1);
+ else
+ pattern = "!" + pattern;
+ match = match(pattern, path);
+ if (assume.length == 0 || !assume[0].booleanValue())
+ assertFalse("Expected no match for: " + pattern + " with: " + path,
+ match);
+ else
+ assumeFalse("Expected no match for: " + pattern + " with: " + path,
+ match);
+ }
+
+ public void assertNotMatched(String pattern, String path, Boolean... assume) {
+ boolean match = match(pattern, path);
+ String result = path + " is " + (match ? "ignored" : "not ignored")
+ + " via '" + pattern + "' rule";
+ if (match)
+ System.err.println(result);
+ if (assume.length == 0 || !assume[0].booleanValue())
+ assertFalse("Expected no match for: " + pattern + " with: " + path,
+ match);
+ else
+ assumeFalse("Expected no match for: " + pattern + " with: " + path,
+ match);
+
+ if (pattern.startsWith("!"))
+ pattern = pattern.substring(1);
+ else
+ pattern = "!" + pattern;
+ match = match(pattern, path);
+ if (assume.length == 0 || !assume[0].booleanValue())
+ assertTrue("Expected a match for: " + pattern + " with: " + path,
+ match);
+ else
+ assumeTrue("Expected a match for: " + pattern + " with: " + path,
+ match);
+ }
+
+ /**
+ * Check for a match. If target ends with "/", match will assume that the
+ * target is meant to be a directory.
+ *
+ * @param pattern
+ * Pattern as it would appear in a .gitignore file
+ * @param target
+ * Target file path relative to repository's GIT_DIR
+ * @return Result of {@link FastIgnoreRule#isMatch(String, boolean)}
+ */
+ private boolean match(String pattern, String target) {
+ boolean isDirectory = target.endsWith("/");
+ if (useJGitRule.booleanValue()) {
+ IgnoreRule r = new IgnoreRule(pattern);
+ // If speed of this test is ever an issue, we can use a presetRule
+ // field
+ // to avoid recompiling a pattern each time.
+ boolean match = r.isMatch(target, isDirectory);
+ if (r.getNegation())
+ match = !match;
+ return match;
+ }
+ FastIgnoreRule r = new FastIgnoreRule(pattern);
+ // If speed of this test is ever an issue, we can use a presetRule field
+ // to avoid recompiling a pattern each time.
+ boolean match = r.isMatch(target, isDirectory);
+ if (r.getNegation())
+ match = !match;
+ return match;
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java
new file mode 100644
index 0000000000..a51582ab3d
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherParametrizedTest.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2010, Red Hat Inc.
+ * 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.ignore;
+
+import static org.junit.Assert.*;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.ignore.FastIgnoreRule;
+import org.eclipse.jgit.ignore.IgnoreRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.*;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Tests ignore pattern matches
+ */
+@SuppressWarnings("deprecation")
+@RunWith(Parameterized.class)
+public class IgnoreMatcherParametrizedTest {
+
+ @Parameters(name = "JGit? {0}")
+ public static Iterable<Boolean[]> data() {
+ return Arrays.asList(new Boolean[][] { { Boolean.FALSE },
+ { Boolean.TRUE } });
+ }
+
+ @Parameter
+ public Boolean useJGitRule;
+
+ @Test
+ public void testBasic() {
+ String pattern = "/test.stp";
+ assertMatched(pattern, "/test.stp");
+
+ pattern = "#/test.stp";
+ assertNotMatched(pattern, "/test.stp");
+ }
+
+ @Test
+ public void testFileNameWildcards() {
+ // Test basic * and ? for any pattern + any character
+ String pattern = "*.st?";
+ assertMatched(pattern, "/test.stp");
+ assertMatched(pattern, "/anothertest.stg");
+ assertMatched(pattern, "/anothertest.st0");
+ assertNotMatched(pattern, "/anothertest.sta1");
+ // Check that asterisk does not expand to "/"
+ assertNotMatched(pattern, "/another/test.sta1");
+
+ // Same as above, with a leading slash to ensure that doesn't cause
+ // problems
+ pattern = "/*.st?";
+ assertMatched(pattern, "/test.stp");
+ assertMatched(pattern, "/anothertest.stg");
+ assertMatched(pattern, "/anothertest.st0");
+ assertNotMatched(pattern, "/anothertest.sta1");
+ // Check that asterisk does not expand to "/"
+ assertNotMatched(pattern, "/another/test.sta1");
+
+ // Test for numbers
+ pattern = "*.sta[0-5]";
+ assertMatched(pattern, "/test.sta5");
+ assertMatched(pattern, "/test.sta4");
+ assertMatched(pattern, "/test.sta3");
+ assertMatched(pattern, "/test.sta2");
+ assertMatched(pattern, "/test.sta1");
+ assertMatched(pattern, "/test.sta0");
+ assertMatched(pattern, "/anothertest.sta2");
+ assertNotMatched(pattern, "test.stag");
+ assertNotMatched(pattern, "test.sta6");
+
+ // Test for letters
+ pattern = "/[tv]est.sta[a-d]";
+ assertMatched(pattern, "/test.staa");
+ assertMatched(pattern, "/test.stab");
+ assertMatched(pattern, "/test.stac");
+ assertMatched(pattern, "/test.stad");
+ assertMatched(pattern, "/vest.stac");
+ assertNotMatched(pattern, "test.stae");
+ assertNotMatched(pattern, "test.sta9");
+
+ // Test child directory/file is matched
+ pattern = "/src/ne?";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertNotMatched(pattern, "/src/new.c");
+
+ // Test name-only fnmatcher matches
+ pattern = "ne?";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertMatched(pattern, "/neb");
+ assertNotMatched(pattern, "/src/new.c");
+ }
+
+ @Test
+ public void testTargetWithoutLeadingSlash() {
+ // Test basic * and ? for any pattern + any character
+ String pattern = "/*.st?";
+ assertMatched(pattern, "test.stp");
+ assertMatched(pattern, "anothertest.stg");
+ assertMatched(pattern, "anothertest.st0");
+ assertNotMatched(pattern, "anothertest.sta1");
+ // Check that asterisk does not expand to ""
+ assertNotMatched(pattern, "another/test.sta1");
+
+ // Same as above, with a leading slash to ensure that doesn't cause
+ // problems
+ pattern = "/*.st?";
+ assertMatched(pattern, "test.stp");
+ assertMatched(pattern, "anothertest.stg");
+ assertMatched(pattern, "anothertest.st0");
+ assertNotMatched(pattern, "anothertest.sta1");
+ // Check that asterisk does not expand to ""
+ assertNotMatched(pattern, "another/test.sta1");
+
+ // Test for numbers
+ pattern = "/*.sta[0-5]";
+ assertMatched(pattern, "test.sta5");
+ assertMatched(pattern, "test.sta4");
+ assertMatched(pattern, "test.sta3");
+ assertMatched(pattern, "test.sta2");
+ assertMatched(pattern, "test.sta1");
+ assertMatched(pattern, "test.sta0");
+ assertMatched(pattern, "anothertest.sta2");
+ assertNotMatched(pattern, "test.stag");
+ assertNotMatched(pattern, "test.sta6");
+
+ // Test for letters
+ pattern = "/[tv]est.sta[a-d]";
+ assertMatched(pattern, "test.staa");
+ assertMatched(pattern, "test.stab");
+ assertMatched(pattern, "test.stac");
+ assertMatched(pattern, "test.stad");
+ assertMatched(pattern, "vest.stac");
+ assertNotMatched(pattern, "test.stae");
+ assertNotMatched(pattern, "test.sta9");
+
+ // Test child directory/file is matched
+ pattern = "/src/ne?";
+ assertMatched(pattern, "src/new/");
+ assertMatched(pattern, "src/new");
+ assertMatched(pattern, "src/new/a.c");
+ assertMatched(pattern, "src/new/a/a.c");
+ assertNotMatched(pattern, "src/new.c");
+
+ // Test name-only fnmatcher matches
+ pattern = "ne?";
+ assertMatched(pattern, "src/new/");
+ assertMatched(pattern, "src/new");
+ assertMatched(pattern, "src/new/a.c");
+ assertMatched(pattern, "src/new/a/a.c");
+ assertMatched(pattern, "neb");
+ assertNotMatched(pattern, "src/new.c");
+ }
+
+ @Test
+ public void testParentDirectoryGitIgnores() {
+ // Contains git ignore patterns such as might be seen in a parent
+ // directory
+
+ // Test for wildcards
+ String pattern = "/*/*.c";
+ assertMatched(pattern, "/file/a.c");
+ assertMatched(pattern, "/src/a.c");
+ assertNotMatched(pattern, "/src/new/a.c");
+
+ // Test child directory/file is matched
+ pattern = "/src/new";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertNotMatched(pattern, "/src/new.c");
+
+ // Test child directory is matched, slash after name
+ pattern = "/src/new/";
+ assertMatched(pattern, "/src/new/");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/new/a/a.c");
+ assertNotMatched(pattern, "/src/new");
+ assertNotMatched(pattern, "/src/new.c");
+
+ // Test directory is matched by name only
+ pattern = "b1";
+ assertMatched(pattern, "/src/new/a/b1/a.c");
+ assertNotMatched(pattern, "/src/new/a/b2/file.c");
+ assertNotMatched(pattern, "/src/new/a/bb1/file.c");
+ assertNotMatched(pattern, "/src/new/a/file.c");
+ }
+
+ @Test
+ public void testTrailingSlash() {
+ String pattern = "/src/";
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/src/new");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/src/a.c");
+ assertNotMatched(pattern, "/src");
+ assertNotMatched(pattern, "/srcA/");
+ }
+
+ @Test
+ public void testNameOnlyMatches() {
+ /*
+ * Name-only matches do not contain any path separators
+ */
+ // Test matches for file extension
+ String pattern = "*.stp";
+ assertMatched(pattern, "/test.stp");
+ assertMatched(pattern, "/src/test.stp");
+ assertNotMatched(pattern, "/test.stp1");
+ assertNotMatched(pattern, "/test.astp");
+
+ // Test matches for name-only, applies to file name or folder name
+ pattern = "src";
+ assertMatched(pattern, "/src");
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertMatched(pattern, "/file/src");
+
+ // Test matches for name-only, applies only to folder names
+ pattern = "src/";
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertNotMatched(pattern, "/src");
+ assertNotMatched(pattern, "/file/src");
+
+ // Test matches for name-only, applies to file name or folder name
+ // With a small wildcard
+ pattern = "?rc";
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertMatched(pattern, "/file/src");
+ assertMatched(pattern, "/src/");
+
+ // Test matches for name-only, applies to file name or folder name
+ // With a small wildcard
+ pattern = "?r[a-c]";
+ assertMatched(pattern, "/src/a.c");
+ assertMatched(pattern, "/src/new/a.c");
+ assertMatched(pattern, "/new/src/a.c");
+ assertMatched(pattern, "/file/src");
+ assertMatched(pattern, "/src/");
+ assertMatched(pattern, "/srb/a.c");
+ assertMatched(pattern, "/grb/new/a.c");
+ assertMatched(pattern, "/new/crb/a.c");
+ assertMatched(pattern, "/file/3rb");
+ assertMatched(pattern, "/xrb/");
+ assertMatched(pattern, "/3ra/a.c");
+ assertMatched(pattern, "/5ra/new/a.c");
+ assertMatched(pattern, "/new/1ra/a.c");
+ assertMatched(pattern, "/file/dra");
+ assertMatched(pattern, "/era/");
+ assertNotMatched(pattern, "/crg");
+ assertNotMatched(pattern, "/cr3");
+ }
+
+ @Test
+ public void testNegation() {
+ String pattern = "!/test.stp";
+ assertMatched(pattern, "/test.stp");
+ }
+
+ /**
+ * Check for a match. If target ends with "/", match will assume that the
+ * target is meant to be a directory.
+ *
+ * @param pattern
+ * Pattern as it would appear in a .gitignore file
+ * @param target
+ * Target file path relative to repository's GIT_DIR
+ */
+ public void assertMatched(String pattern, String target) {
+ boolean value = match(pattern, target);
+ assertTrue("Expected a match for: " + pattern + " with: " + target,
+ value);
+ }
+
+ /**
+ * Check for a match. If target ends with "/", match will assume that the
+ * target is meant to be a directory.
+ *
+ * @param pattern
+ * Pattern as it would appear in a .gitignore file
+ * @param target
+ * Target file path relative to repository's GIT_DIR
+ */
+ public void assertNotMatched(String pattern, String target) {
+ boolean value = match(pattern, target);
+ assertFalse("Expected no match for: " + pattern + " with: " + target,
+ value);
+ }
+
+ /**
+ * Check for a match. If target ends with "/", match will assume that the
+ * target is meant to be a directory.
+ *
+ * @param pattern
+ * Pattern as it would appear in a .gitignore file
+ * @param target
+ * Target file path relative to repository's GIT_DIR
+ * @return Result of {@link FastIgnoreRule#isMatch(String, boolean)}
+ */
+ private boolean match(String pattern, String target) {
+ boolean isDirectory = target.endsWith("/");
+ if (useJGitRule.booleanValue()) {
+ IgnoreRule r = new IgnoreRule(pattern);
+ // If speed of this test is ever an issue, we can use a presetRule
+ // field
+ // to avoid recompiling a pattern each time.
+ return r.isMatch(target, isDirectory);
+ }
+ FastIgnoreRule r = new FastIgnoreRule(pattern);
+ // If speed of this test is ever an issue, we can use a presetRule field
+ // to avoid recompiling a pattern each time.
+ return r.isMatch(target, isDirectory);
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
index aa98696b24..0713b1ad16 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java
@@ -52,6 +52,7 @@ import org.junit.Test;
/**
* Tests ignore pattern matches
*/
+@SuppressWarnings("deprecation")
public class IgnoreMatcherTest {
@Test
@@ -383,12 +384,12 @@ public class IgnoreMatcherTest {
/**
* Check for a match. If target ends with "/", match will assume that the
* target is meant to be a directory.
+ *
* @param pattern
- * Pattern as it would appear in a .gitignore file
+ * Pattern as it would appear in a .gitignore file
* @param target
- * Target file path relative to repository's GIT_DIR
- * @return
- * Result of {@link IgnoreRule#isMatch(String, boolean)}
+ * Target file path relative to repository's GIT_DIR
+ * @return Result of IgnoreRule.isMatch(String, boolean)
*/
private static boolean match(String pattern, String target) {
IgnoreRule r = new IgnoreRule(pattern);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java
new file mode 100644
index 0000000000..41180e70c2
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreRuleSpecialCasesTest.java
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2008, Florian Koeberle <florianskarten@web.de>
+ * Copyright (C) 2008, Florian Köberle <florianskarten@web.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.ignore;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.ignore.FastIgnoreRule;
+import org.eclipse.jgit.ignore.IgnoreRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.*;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+@SuppressWarnings({ "deprecation", "boxing" })
+public class IgnoreRuleSpecialCasesTest {
+
+ @Parameters(name = "JGit? {0}")
+ public static Iterable<Boolean[]> data() {
+ return Arrays.asList(new Boolean[][] { { Boolean.FALSE },
+ { Boolean.TRUE } });
+ }
+
+ @Parameter
+ public Boolean useJGitRule;
+
+ private void assertMatch(final String pattern, final String input,
+ final boolean matchExpected, Boolean... assume) {
+ boolean assumeDir = input.endsWith("/");
+ if (useJGitRule.booleanValue()) {
+ final IgnoreRule matcher = new IgnoreRule(pattern);
+ if (assume.length == 0 || !assume[0].booleanValue())
+ assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
+ else
+ assumeTrue(matchExpected == matcher.isMatch(input, assumeDir));
+ } else {
+ FastIgnoreRule matcher = new FastIgnoreRule(pattern);
+ if (assume.length == 0 || !assume[0].booleanValue())
+ assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
+ else
+ assumeTrue(matchExpected == matcher.isMatch(input, assumeDir));
+ }
+ }
+
+ private void assertFileNameMatch(final String pattern, final String input,
+ final boolean matchExpected) {
+ boolean assumeDir = input.endsWith("/");
+ if (useJGitRule.booleanValue()) {
+ final IgnoreRule matcher = new IgnoreRule(pattern);
+ assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
+ } else {
+ FastIgnoreRule matcher = new FastIgnoreRule(pattern);
+ assertEquals(matchExpected, matcher.isMatch(input, assumeDir));
+ }
+ }
+
+ @Test
+ public void testVerySimplePatternCase0() throws Exception {
+ if (useJGitRule)
+ System.err
+ .println("IgnoreRule can't understand blank lines, skipping");
+ Boolean assume = useJGitRule;
+ assertMatch("", "", false, assume);
+ }
+
+ @Test
+ public void testVerySimplePatternCase1() throws Exception {
+ assertMatch("ab", "a", false);
+ }
+
+ @Test
+ public void testVerySimplePatternCase2() throws Exception {
+ assertMatch("ab", "ab", true);
+ }
+
+ @Test
+ public void testVerySimplePatternCase3() throws Exception {
+ assertMatch("ab", "ac", false);
+ }
+
+ @Test
+ public void testVerySimplePatternCase4() throws Exception {
+ assertMatch("ab", "abc", false);
+ }
+
+ @Test
+ public void testVerySimpleWildcardCase0() throws Exception {
+ assertMatch("?", "a", true);
+ }
+
+ @Test
+ public void testVerySimpleWildCardCase1() throws Exception {
+ assertMatch("??", "a", false);
+ }
+
+ @Test
+ public void testVerySimpleWildCardCase2() throws Exception {
+ assertMatch("??", "ab", true);
+ }
+
+ @Test
+ public void testVerySimpleWildCardCase3() throws Exception {
+ assertMatch("??", "abc", false);
+ }
+
+ @Test
+ public void testVerySimpleStarCase0() throws Exception {
+ // can't happen, but blank lines should never match
+ assertMatch("*", "", false);
+ }
+
+ @Test
+ public void testVerySimpleStarCase1() throws Exception {
+ assertMatch("*", "a", true);
+ }
+
+ @Test
+ public void testVerySimpleStarCase2() throws Exception {
+ assertMatch("*", "ab", true);
+ }
+
+ @Test
+ public void testSimpleStarCase0() throws Exception {
+ assertMatch("a*b", "a", false);
+ }
+
+ @Test
+ public void testSimpleStarCase1() throws Exception {
+ assertMatch("a*c", "ac", true);
+ }
+
+ @Test
+ public void testSimpleStarCase2() throws Exception {
+ assertMatch("a*c", "ab", false);
+ }
+
+ @Test
+ public void testSimpleStarCase3() throws Exception {
+ assertMatch("a*c", "abc", true);
+ }
+
+ @Test
+ public void testManySolutionsCase0() throws Exception {
+ assertMatch("a*a*a", "aaa", true);
+ }
+
+ @Test
+ public void testManySolutionsCase1() throws Exception {
+ assertMatch("a*a*a", "aaaa", true);
+ }
+
+ @Test
+ public void testManySolutionsCase2() throws Exception {
+ assertMatch("a*a*a", "ababa", true);
+ }
+
+ @Test
+ public void testManySolutionsCase3() throws Exception {
+ assertMatch("a*a*a", "aaaaaaaa", true);
+ }
+
+ @Test
+ public void testManySolutionsCase4() throws Exception {
+ assertMatch("a*a*a", "aaaaaaab", false);
+ }
+
+ @Test
+ public void testVerySimpleGroupCase0() throws Exception {
+ assertMatch("[ab]", "a", true);
+ }
+
+ @Test
+ public void testVerySimpleGroupCase1() throws Exception {
+ assertMatch("[ab]", "b", true);
+ }
+
+ @Test
+ public void testVerySimpleGroupCase2() throws Exception {
+ assertMatch("[ab]", "ab", false);
+ }
+
+ @Test
+ public void testVerySimpleGroupRangeCase0() throws Exception {
+ assertMatch("[b-d]", "a", false);
+ }
+
+ @Test
+ public void testVerySimpleGroupRangeCase1() throws Exception {
+ assertMatch("[b-d]", "b", true);
+ }
+
+ @Test
+ public void testVerySimpleGroupRangeCase2() throws Exception {
+ assertMatch("[b-d]", "c", true);
+ }
+
+ @Test
+ public void testVerySimpleGroupRangeCase3() throws Exception {
+ assertMatch("[b-d]", "d", true);
+ }
+
+ @Test
+ public void testVerySimpleGroupRangeCase4() throws Exception {
+ assertMatch("[b-d]", "e", false);
+ }
+
+ @Test
+ public void testVerySimpleGroupRangeCase5() throws Exception {
+ assertMatch("[b-d]", "-", false);
+ }
+
+ @Test
+ public void testTwoGroupsCase0() throws Exception {
+ assertMatch("[b-d][ab]", "bb", true);
+ }
+
+ @Test
+ public void testTwoGroupsCase1() throws Exception {
+ assertMatch("[b-d][ab]", "ca", true);
+ }
+
+ @Test
+ public void testTwoGroupsCase2() throws Exception {
+ assertMatch("[b-d][ab]", "fa", false);
+ }
+
+ @Test
+ public void testTwoGroupsCase3() throws Exception {
+ assertMatch("[b-d][ab]", "bc", false);
+ }
+
+ @Test
+ public void testTwoRangesInOneGroupCase0() throws Exception {
+ assertMatch("[b-ce-e]", "a", false);
+ }
+
+ @Test
+ public void testTwoRangesInOneGroupCase1() throws Exception {
+ assertMatch("[b-ce-e]", "b", true);
+ }
+
+ @Test
+ public void testTwoRangesInOneGroupCase2() throws Exception {
+ assertMatch("[b-ce-e]", "c", true);
+ }
+
+ @Test
+ public void testTwoRangesInOneGroupCase3() throws Exception {
+ assertMatch("[b-ce-e]", "d", false);
+ }
+
+ @Test
+ public void testTwoRangesInOneGroupCase4() throws Exception {
+ assertMatch("[b-ce-e]", "e", true);
+ }
+
+ @Test
+ public void testTwoRangesInOneGroupCase5() throws Exception {
+ assertMatch("[b-ce-e]", "f", false);
+ }
+
+ @Test
+ public void testIncompleteRangesInOneGroupCase0() throws Exception {
+ assertMatch("a[b-]", "ab", true);
+ }
+
+ @Test
+ public void testIncompleteRangesInOneGroupCase1() throws Exception {
+ assertMatch("a[b-]", "ac", false);
+ }
+
+ @Test
+ public void testIncompleteRangesInOneGroupCase2() throws Exception {
+ assertMatch("a[b-]", "a-", true);
+ }
+
+ @Test
+ public void testCombinedRangesInOneGroupCase0() throws Exception {
+ assertMatch("[a-c-e]", "b", true);
+ }
+
+ /**
+ * The c belongs to the range a-c. "-e" is no valid range so d should not
+ * match.
+ *
+ * @throws Exception
+ * for some reasons
+ */
+ @Test
+ public void testCombinedRangesInOneGroupCase1() throws Exception {
+ assertMatch("[a-c-e]", "d", false);
+ }
+
+ @Test
+ public void testCombinedRangesInOneGroupCase2() throws Exception {
+ assertMatch("[a-c-e]", "e", true);
+ }
+
+ @Test
+ public void testInversedGroupCase0() throws Exception {
+ assertMatch("[!b-c]", "a", true);
+ }
+
+ @Test
+ public void testInversedGroupCase1() throws Exception {
+ assertMatch("[!b-c]", "b", false);
+ }
+
+ @Test
+ public void testInversedGroupCase2() throws Exception {
+ assertMatch("[!b-c]", "c", false);
+ }
+
+ @Test
+ public void testInversedGroupCase3() throws Exception {
+ assertMatch("[!b-c]", "d", true);
+ }
+
+ @Test
+ public void testAlphaGroupCase0() throws Exception {
+ assertMatch("[[:alpha:]]", "d", true);
+ }
+
+ @Test
+ public void testAlphaGroupCase1() throws Exception {
+ assertMatch("[[:alpha:]]", ":", false);
+ }
+
+ @Test
+ public void testAlphaGroupCase2() throws Exception {
+ // \u00f6 = 'o' with dots on it
+ assertMatch("[[:alpha:]]", "\u00f6", true);
+ }
+
+ @Test
+ public void test2AlphaGroupsCase0() throws Exception {
+ // \u00f6 = 'o' with dots on it
+ assertMatch("[[:alpha:]][[:alpha:]]", "a\u00f6", true);
+ assertMatch("[[:alpha:]][[:alpha:]]", "a1", false);
+ }
+
+ @Test
+ public void testAlnumGroupCase0() throws Exception {
+ assertMatch("[[:alnum:]]", "a", true);
+ }
+
+ @Test
+ public void testAlnumGroupCase1() throws Exception {
+ assertMatch("[[:alnum:]]", "1", true);
+ }
+
+ @Test
+ public void testAlnumGroupCase2() throws Exception {
+ assertMatch("[[:alnum:]]", ":", false);
+ }
+
+ @Test
+ public void testBlankGroupCase0() throws Exception {
+ assertMatch("[[:blank:]]", " ", true);
+ }
+
+ @Test
+ public void testBlankGroupCase1() throws Exception {
+ assertMatch("[[:blank:]]", "\t", true);
+ }
+
+ @Test
+ public void testBlankGroupCase2() throws Exception {
+ assertMatch("[[:blank:]]", "\r", false);
+ }
+
+ @Test
+ public void testBlankGroupCase3() throws Exception {
+ assertMatch("[[:blank:]]", "\n", false);
+ }
+
+ @Test
+ public void testBlankGroupCase4() throws Exception {
+ assertMatch("[[:blank:]]", "a", false);
+ }
+
+ @Test
+ public void testCntrlGroupCase0() throws Exception {
+ assertMatch("[[:cntrl:]]", "a", false);
+ }
+
+ @Test
+ public void testCntrlGroupCase1() throws Exception {
+ assertMatch("[[:cntrl:]]", String.valueOf((char) 7), true);
+ }
+
+ @Test
+ public void testDigitGroupCase0() throws Exception {
+ assertMatch("[[:digit:]]", "0", true);
+ }
+
+ @Test
+ public void testDigitGroupCase1() throws Exception {
+ assertMatch("[[:digit:]]", "5", true);
+ }
+
+ @Test
+ public void testDigitGroupCase2() throws Exception {
+ assertMatch("[[:digit:]]", "9", true);
+ }
+
+ @Test
+ public void testDigitGroupCase3() throws Exception {
+ // \u06f9 = EXTENDED ARABIC-INDIC DIGIT NINE
+ assertMatch("[[:digit:]]", "\u06f9", true);
+ }
+
+ @Test
+ public void testDigitGroupCase4() throws Exception {
+ assertMatch("[[:digit:]]", "a", false);
+ }
+
+ @Test
+ public void testDigitGroupCase5() throws Exception {
+ assertMatch("[[:digit:]]", "]", false);
+ }
+
+ @Test
+ public void testGraphGroupCase0() throws Exception {
+ assertMatch("[[:graph:]]", "]", true);
+ }
+
+ @Test
+ public void testGraphGroupCase1() throws Exception {
+ assertMatch("[[:graph:]]", "a", true);
+ }
+
+ @Test
+ public void testGraphGroupCase2() throws Exception {
+ assertMatch("[[:graph:]]", ".", true);
+ }
+
+ @Test
+ public void testGraphGroupCase3() throws Exception {
+ assertMatch("[[:graph:]]", "0", true);
+ }
+
+ @Test
+ public void testGraphGroupCase4() throws Exception {
+ assertMatch("[[:graph:]]", " ", false);
+ }
+
+ @Test
+ public void testGraphGroupCase5() throws Exception {
+ // \u00f6 = 'o' with dots on it
+ assertMatch("[[:graph:]]", "\u00f6", true);
+ }
+
+ @Test
+ public void testLowerGroupCase0() throws Exception {
+ assertMatch("[[:lower:]]", "a", true);
+ }
+
+ @Test
+ public void testLowerGroupCase1() throws Exception {
+ assertMatch("[[:lower:]]", "h", true);
+ }
+
+ @Test
+ public void testLowerGroupCase2() throws Exception {
+ assertMatch("[[:lower:]]", "A", false);
+ }
+
+ @Test
+ public void testLowerGroupCase3() throws Exception {
+ assertMatch("[[:lower:]]", "H", false);
+ }
+
+ @Test
+ public void testLowerGroupCase4() throws Exception {
+ // \u00e4 = small 'a' with dots on it
+ assertMatch("[[:lower:]]", "\u00e4", true);
+ }
+
+ @Test
+ public void testLowerGroupCase5() throws Exception {
+ assertMatch("[[:lower:]]", ".", false);
+ }
+
+ @Test
+ public void testPrintGroupCase0() throws Exception {
+ assertMatch("[[:print:]]", "]", true);
+ }
+
+ @Test
+ public void testPrintGroupCase1() throws Exception {
+ assertMatch("[[:print:]]", "a", true);
+ }
+
+ @Test
+ public void testPrintGroupCase2() throws Exception {
+ assertMatch("[[:print:]]", ".", true);
+ }
+
+ @Test
+ public void testPrintGroupCase3() throws Exception {
+ assertMatch("[[:print:]]", "0", true);
+ }
+
+ @Test
+ public void testPrintGroupCase4() throws Exception {
+ assertMatch("[[:print:]]", " ", true);
+ }
+
+ @Test
+ public void testPrintGroupCase5() throws Exception {
+ // \u00f6 = 'o' with dots on it
+ assertMatch("[[:print:]]", "\u00f6", true);
+ }
+
+ @Test
+ public void testPunctGroupCase0() throws Exception {
+ assertMatch("[[:punct:]]", ".", true);
+ }
+
+ @Test
+ public void testPunctGroupCase1() throws Exception {
+ assertMatch("[[:punct:]]", "@", true);
+ }
+
+ @Test
+ public void testPunctGroupCase2() throws Exception {
+ assertMatch("[[:punct:]]", " ", false);
+ }
+
+ @Test
+ public void testPunctGroupCase3() throws Exception {
+ assertMatch("[[:punct:]]", "a", false);
+ }
+
+ @Test
+ public void testSpaceGroupCase0() throws Exception {
+ assertMatch("[[:space:]]", " ", true);
+ }
+
+ @Test
+ public void testSpaceGroupCase1() throws Exception {
+ assertMatch("[[:space:]]", "\t", true);
+ }
+
+ @Test
+ public void testSpaceGroupCase2() throws Exception {
+ assertMatch("[[:space:]]", "\r", true);
+ }
+
+ @Test
+ public void testSpaceGroupCase3() throws Exception {
+ assertMatch("[[:space:]]", "\n", true);
+ }
+
+ @Test
+ public void testSpaceGroupCase4() throws Exception {
+ assertMatch("[[:space:]]", "a", false);
+ }
+
+ @Test
+ public void testUpperGroupCase0() throws Exception {
+ assertMatch("[[:upper:]]", "a", false);
+ }
+
+ @Test
+ public void testUpperGroupCase1() throws Exception {
+ assertMatch("[[:upper:]]", "h", false);
+ }
+
+ @Test
+ public void testUpperGroupCase2() throws Exception {
+ assertMatch("[[:upper:]]", "A", true);
+ }
+
+ @Test
+ public void testUpperGroupCase3() throws Exception {
+ assertMatch("[[:upper:]]", "H", true);
+ }
+
+ @Test
+ public void testUpperGroupCase4() throws Exception {
+ // \u00c4 = 'A' with dots on it
+ assertMatch("[[:upper:]]", "\u00c4", true);
+ }
+
+ @Test
+ public void testUpperGroupCase5() throws Exception {
+ assertMatch("[[:upper:]]", ".", false);
+ }
+
+ @Test
+ public void testXDigitGroupCase0() throws Exception {
+ assertMatch("[[:xdigit:]]", "a", true);
+ }
+
+ @Test
+ public void testXDigitGroupCase1() throws Exception {
+ assertMatch("[[:xdigit:]]", "d", true);
+ }
+
+ @Test
+ public void testXDigitGroupCase2() throws Exception {
+ assertMatch("[[:xdigit:]]", "f", true);
+ }
+
+ @Test
+ public void testXDigitGroupCase3() throws Exception {
+ assertMatch("[[:xdigit:]]", "0", true);
+ }
+
+ @Test
+ public void testXDigitGroupCase4() throws Exception {
+ assertMatch("[[:xdigit:]]", "5", true);
+ }
+
+ @Test
+ public void testXDigitGroupCase5() throws Exception {
+ assertMatch("[[:xdigit:]]", "9", true);
+ }
+
+ @Test
+ public void testXDigitGroupCase6() throws Exception {
+ assertMatch("[[:xdigit:]]", "Û¹", false);
+ }
+
+ @Test
+ public void testXDigitGroupCase7() throws Exception {
+ assertMatch("[[:xdigit:]]", ".", false);
+ }
+
+ @Test
+ public void testWordGroupCase0() throws Exception {
+ assertMatch("[[:word:]]", "g", true);
+ }
+
+ @Test
+ public void testWordGroupCase1() throws Exception {
+ // \u00f6 = 'o' with dots on it
+ assertMatch("[[:word:]]", "\u00f6", true);
+ }
+
+ @Test
+ public void testWordGroupCase2() throws Exception {
+ assertMatch("[[:word:]]", "5", true);
+ }
+
+ @Test
+ public void testWordGroupCase3() throws Exception {
+ assertMatch("[[:word:]]", "_", true);
+ }
+
+ @Test
+ public void testWordGroupCase4() throws Exception {
+ assertMatch("[[:word:]]", " ", false);
+ }
+
+ @Test
+ public void testWordGroupCase5() throws Exception {
+ assertMatch("[[:word:]]", ".", false);
+ }
+
+ @Test
+ public void testMixedGroupCase0() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "A", true);
+ }
+
+ @Test
+ public void testMixedGroupCase1() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "C", true);
+ }
+
+ @Test
+ public void testMixedGroupCase2() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "e", true);
+ }
+
+ @Test
+ public void testMixedGroupCase3() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "3", true);
+ }
+
+ @Test
+ public void testMixedGroupCase4() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "4", true);
+ }
+
+ @Test
+ public void testMixedGroupCase5() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "5", true);
+ }
+
+ @Test
+ public void testMixedGroupCase6() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "B", false);
+ }
+
+ @Test
+ public void testMixedGroupCase7() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "2", false);
+ }
+
+ @Test
+ public void testMixedGroupCase8() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", "6", false);
+ }
+
+ @Test
+ public void testMixedGroupCase9() throws Exception {
+ assertMatch("[A[:lower:]C3-5]", ".", false);
+ }
+
+ @Test
+ public void testSpecialGroupCase0() throws Exception {
+ assertMatch("[[]", "[", true);
+ }
+
+ @Test
+ public void testSpecialGroupCase1() throws Exception {
+ assertMatch("[]]", "]", true);
+ }
+
+ @Test
+ public void testSpecialGroupCase2() throws Exception {
+ assertMatch("[]a]", "]", true);
+ }
+
+ @Test
+ public void testSpecialGroupCase3() throws Exception {
+ assertMatch("[a[]", "[", true);
+ }
+
+ @Test
+ public void testSpecialGroupCase4() throws Exception {
+ assertMatch("[a[]", "a", true);
+ }
+
+ @Test
+ public void testSpecialGroupCase5() throws Exception {
+ assertMatch("[!]]", "]", false);
+ }
+
+ @Test
+ public void testSpecialGroupCase6() throws Exception {
+ assertMatch("[!]]", "x", true);
+ }
+
+ @Test
+ public void testSpecialGroupCase7() throws Exception {
+ assertMatch("[:]]", ":]", true);
+ }
+
+ @Test
+ public void testSpecialGroupCase8() throws Exception {
+ assertMatch("[:]]", ":", false);
+ }
+
+ @Test
+ public void testSpecialGroupCase9() throws Exception {
+ if (useJGitRule)
+ System.err.println("IgnoreRule can't understand [[:], skipping");
+ Boolean assume = useJGitRule;
+ // Second bracket is threated literally, so both [ and : should match
+ assertMatch("[[:]", ":", true, assume);
+ assertMatch("[[:]", "[", true, assume);
+ }
+
+ @Test
+ public void testUnsupportedGroupCase0() throws Exception {
+ assertMatch("[[=a=]]", "a", false);
+ assertMatch("[[=a=]]", "=", false);
+ assertMatch("[=a=]", "a", true);
+ assertMatch("[=a=]", "=", true);
+ }
+
+ @Test
+ public void testUnsupportedGroupCase01() throws Exception {
+ assertMatch("[.a.]*[.a.]", "aha", true);
+ }
+
+ @Test
+ public void testUnsupportedGroupCase1() throws Exception {
+ assertMatch("[[.a.]]", "a", false);
+ assertMatch("[[.a.]]", ".", false);
+ assertMatch("[.a.]", "a", true);
+ assertMatch("[.a.]", ".", true);
+ }
+
+ @Test
+ public void testEscapedBracket1() throws Exception {
+ assertMatch("\\[", "[", true);
+ }
+
+ @Test
+ public void testEscapedBracket2() throws Exception {
+ assertMatch("\\[[a]", "[", false);
+ }
+
+ @Test
+ public void testEscapedBracket3() throws Exception {
+ assertMatch("\\[[a]", "a", false);
+ }
+
+ @Test
+ public void testEscapedBracket4() throws Exception {
+ assertMatch("\\[[a]", "[a", true);
+ }
+
+ @Test
+ public void testEscapedBracket5() throws Exception {
+ assertMatch("[a\\]]", "]", true);
+ }
+
+ @Test
+ public void testEscapedBracket6() throws Exception {
+ assertMatch("[a\\]]", "a", true);
+ }
+
+ @Test
+ public void testEscapedBackslash() throws Exception {
+ if (useJGitRule)
+ System.err
+ .println("IgnoreRule can't understand escaped backslashes, skipping");
+ Boolean assume = useJGitRule;
+ // In Git CLI a\\b matches a\b file
+ assertMatch("a\\\\b", "a\\b", true, assume);
+ }
+
+ @Test
+ public void testMultipleEscapedCharacters1() throws Exception {
+ assertMatch("\\]a?c\\*\\[d\\?\\]", "]abc*[d?]", true);
+ }
+
+ @Test
+ public void testFilePathSimpleCase() throws Exception {
+ assertFileNameMatch("a/b", "a/b", true);
+ }
+
+ @Test
+ public void testFilePathCase0() throws Exception {
+ assertFileNameMatch("a*b", "a/b", false);
+ }
+
+ @Test
+ public void testFilePathCase1() throws Exception {
+ assertFileNameMatch("a?b", "a/b", false);
+ }
+
+ @Test
+ public void testFilePathCase2() throws Exception {
+ assertFileNameMatch("a*b", "a\\b", true);
+ }
+
+ @Test
+ public void testFilePathCase3() throws Exception {
+ assertFileNameMatch("a?b", "a\\b", true);
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 9cbb9955f2..80399f6008 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -50,6 +50,7 @@ Export-Package: org.eclipse.jgit.api;version="3.6.0";
org.eclipse.jgit.lib",
org.eclipse.jgit.gitrepo.internal;version="3.6.0";x-internal:=true,
org.eclipse.jgit.ignore;version="3.6.0",
+ org.eclipse.jgit.ignore.internal;version="3.6.0";x-friends:="org.eclipse.jgit.test",
org.eclipse.jgit.internal;version="3.6.0";x-friends:="org.eclipse.jgit.test,org.eclipse.jgit.http.test",
org.eclipse.jgit.internal.storage.dfs;version="3.6.0";x-friends:="org.eclipse.jgit.test",
org.eclipse.jgit.internal.storage.file;version="3.6.0";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
new file mode 100644
index 0000000000..02863bd16a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore;
+
+import static org.eclipse.jgit.ignore.internal.Strings.stripTrailing;
+
+import org.eclipse.jgit.errors.InvalidPatternException;
+import org.eclipse.jgit.ignore.internal.IMatcher;
+import org.eclipse.jgit.ignore.internal.PathMatcher;
+
+/**
+ * "Fast" (compared with IgnoreRule) git ignore rule implementation supporting
+ * also double star <code>**<code> pattern.
+ * <p>
+ * This class is immutable and thread safe.
+ *
+ * @since 3.6
+ */
+public class FastIgnoreRule {
+
+ /**
+ * Character used as default path separator for ignore entries
+ */
+ public static final char PATH_SEPARATOR = '/';
+
+ private static final NoResultMatcher NO_MATCH = new NoResultMatcher();
+
+ private final IMatcher matcher;
+
+ private final boolean inverse;
+
+ private final boolean dirOnly;
+
+ /**
+ *
+ * @param pattern
+ * ignore pattern as described in <a href=
+ * "https://www.kernel.org/pub/software/scm/git/docs/gitignore.html"
+ * >git manual</a>. If pattern is invalid or is not a pattern
+ * (comment), this rule doesn't match anything.
+ */
+ public FastIgnoreRule(String pattern) {
+ if (pattern == null)
+ throw new IllegalArgumentException("Pattern must not be null!"); //$NON-NLS-1$
+ if (pattern.length() == 0) {
+ dirOnly = false;
+ inverse = false;
+ this.matcher = NO_MATCH;
+ return;
+ }
+ inverse = pattern.charAt(0) == '!';
+ if (inverse) {
+ pattern = pattern.substring(1);
+ if (pattern.length() == 0) {
+ dirOnly = false;
+ this.matcher = NO_MATCH;
+ return;
+ }
+ }
+ if (pattern.charAt(0) == '#') {
+ this.matcher = NO_MATCH;
+ dirOnly = false;
+ } else {
+ dirOnly = pattern.charAt(pattern.length() - 1) == PATH_SEPARATOR;
+ if (dirOnly) {
+ pattern = stripTrailing(pattern, PATH_SEPARATOR);
+ if (pattern.length() == 0) {
+ this.matcher = NO_MATCH;
+ return;
+ }
+ }
+ IMatcher m;
+ try {
+ m = PathMatcher.createPathMatcher(pattern,
+ Character.valueOf(PATH_SEPARATOR), dirOnly);
+ } catch (InvalidPatternException e) {
+ m = NO_MATCH;
+ }
+ this.matcher = m;
+ }
+ }
+
+ /**
+ * Returns true if a match was made. <br>
+ * This function does NOT return the actual ignore status of the target!
+ * Please consult {@link #getResult()} for the negation status. The actual
+ * ignore status may be true or false depending on whether this rule is an
+ * ignore rule or a negation rule.
+ *
+ * @param path
+ * Name pattern of the file, relative to the base directory of
+ * this rule
+ * @param directory
+ * Whether the target file is a directory or not
+ * @return True if a match was made. This does not necessarily mean that the
+ * target is ignored. Call {@link #getResult() getResult()} for the
+ * result.
+ */
+ public boolean isMatch(String path, boolean directory) {
+ if (path == null)
+ return false;
+ if (path.length() == 0)
+ return false;
+ boolean match = matcher.matches(path, directory);
+ return match;
+ }
+
+ /**
+ * @return True if the pattern is just a file name and not a path
+ */
+ public boolean getNameOnly() {
+ return !(matcher instanceof PathMatcher);
+ }
+
+ /**
+ *
+ * @return True if the pattern should match directories only
+ */
+ public boolean dirOnly() {
+ return dirOnly;
+ }
+
+ /**
+ * Indicates whether the rule is non-negation or negation.
+ *
+ * @return True if the pattern had a "!" in front of it
+ */
+ public boolean getNegation() {
+ return inverse;
+ }
+
+ /**
+ * Indicates whether the rule is non-negation or negation.
+ *
+ * @return True if the target is to be ignored, false otherwise.
+ */
+ public boolean getResult() {
+ return !inverse;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (inverse)
+ sb.append('!');
+ sb.append(matcher);
+ if (dirOnly)
+ sb.append(PATH_SEPARATOR);
+ return sb.toString();
+
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (inverse ? 1231 : 1237);
+ result = prime * result + (dirOnly ? 1231 : 1237);
+ result = prime * result + ((matcher == null) ? 0 : matcher.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof FastIgnoreRule))
+ return false;
+
+ FastIgnoreRule other = (FastIgnoreRule) obj;
+ if (inverse != other.inverse)
+ return false;
+ if (dirOnly != other.dirOnly)
+ return false;
+ return matcher.equals(other.matcher);
+ }
+
+ static final class NoResultMatcher implements IMatcher {
+
+ public boolean matches(String path, boolean assumeDirectory) {
+ return false;
+ }
+
+ public boolean matches(String segment, int startIncl, int endExcl,
+ boolean assumeDirectory) {
+ return false;
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
index 2cddddbe11..ff74f7cabf 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreNode.java
@@ -71,11 +71,11 @@ public class IgnoreNode {
}
/** The rules that have been parsed into this node. */
- private final List<IgnoreRule> rules;
+ private final List<FastIgnoreRule> rules;
/** Create an empty ignore node with no rules. */
public IgnoreNode() {
- rules = new ArrayList<IgnoreRule>();
+ rules = new ArrayList<FastIgnoreRule>();
}
/**
@@ -84,7 +84,7 @@ public class IgnoreNode {
* @param rules
* list of rules.
**/
- public IgnoreNode(List<IgnoreRule> rules) {
+ public IgnoreNode(List<FastIgnoreRule> rules) {
this.rules = rules;
}
@@ -103,7 +103,7 @@ public class IgnoreNode {
while ((txt = br.readLine()) != null) {
txt = txt.trim();
if (txt.length() > 0 && !txt.startsWith("#") && !txt.equals("/")) //$NON-NLS-1$ //$NON-NLS-2$
- rules.add(new IgnoreRule(txt));
+ rules.add(new FastIgnoreRule(txt));
}
}
@@ -112,7 +112,7 @@ public class IgnoreNode {
}
/** @return list of all ignore rules held by this node. */
- public List<IgnoreRule> getRules() {
+ public List<FastIgnoreRule> getRules() {
return Collections.unmodifiableList(rules);
}
@@ -133,7 +133,7 @@ public class IgnoreNode {
// Parse rules in the reverse order that they were read
for (int i = rules.size() - 1; i > -1; i--) {
- IgnoreRule rule = rules.get(i);
+ FastIgnoreRule rule = rules.get(i);
if (rule.isMatch(entryPath, isDirectory)) {
if (rule.getResult())
return MatchResult.IGNORED;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
index 42bbd9e9b8..f14d1bd0b2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/IgnoreRule.java
@@ -46,11 +46,16 @@ import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.fnmatch.FileNameMatcher;
/**
- * A single ignore rule corresponding to one line in a .gitignore or
- * ignore file. Parses the ignore pattern
+ * A single ignore rule corresponding to one line in a .gitignore or ignore
+ * file. Parses the ignore pattern
*
* Inspiration from: Ferry Huberts
+ *
+ * @deprecated this rule does not support double star pattern and is slow
+ * parsing glob expressions. Consider to use {@link FastIgnoreRule}
+ * instead. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=440732
*/
+@Deprecated
public class IgnoreRule {
private String pattern;
private boolean negation;
@@ -270,4 +275,4 @@ public class IgnoreRule {
public String toString() {
return pattern;
}
-} \ No newline at end of file
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java
new file mode 100644
index 0000000000..4e90d8c3cb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/AbstractMatcher.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore.internal;
+
+/**
+ * Base class for default methods as {@link #toString()} and such.
+ * <p>
+ * This class is immutable and thread safe.
+ *
+ * @since 3.6
+ */
+public abstract class AbstractMatcher implements IMatcher {
+
+ final boolean dirOnly;
+
+ final String pattern;
+
+ /**
+ * @param pattern
+ * string to parse
+ * @param dirOnly
+ * true if this matcher should match only directories
+ */
+ AbstractMatcher(String pattern, boolean dirOnly) {
+ this.pattern = pattern;
+ this.dirOnly = dirOnly;
+ }
+
+ @Override
+ public String toString() {
+ return pattern;
+ }
+
+ @Override
+ public int hashCode() {
+ return pattern.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof AbstractMatcher))
+ return false;
+ AbstractMatcher other = (AbstractMatcher) obj;
+ return dirOnly == other.dirOnly && pattern.equals(other.pattern);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
new file mode 100644
index 0000000000..10b5e49e1f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore.internal;
+
+/**
+ * Generic string matcher
+ *
+ * @since 3.6
+ */
+public interface IMatcher {
+
+ /**
+ * Matches entire given string
+ *
+ * @param path
+ * string which is not null, but might be empty
+ * @param assumeDirectory
+ * true to assume this path as directory (even if it doesn't end
+ * with a slash)
+ * @return true if this matcher pattern matches given string
+ */
+ boolean matches(String path, boolean assumeDirectory);
+
+ /**
+ * Matches only part of given string
+ *
+ * @param segment
+ * string which is not null, but might be empty
+ * @param startIncl
+ * start index, inclusive
+ * @param endExcl
+ * end index, exclusive
+ * @param assumeDirectory
+ * true to assume this path as directory (even if it doesn't end
+ * with a slash)
+ * @return true if this matcher pattern matches given string
+ */
+ boolean matches(String segment, int startIncl, int endExcl,
+ boolean assumeDirectory);
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
new file mode 100644
index 0000000000..6c4c8092fe
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/NameMatcher.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore.internal;
+
+import static org.eclipse.jgit.ignore.internal.Strings.getPathSeparator;
+
+/**
+ * Matcher built from patterns for file names (single path segments). This class
+ * is immutable and thread safe.
+ *
+ * @since 3.6
+ */
+public class NameMatcher extends AbstractMatcher {
+
+ final boolean beginning;
+
+ final char slash;
+
+ final String subPattern;
+
+ NameMatcher(String pattern, Character pathSeparator, boolean dirOnly) {
+ super(pattern, dirOnly);
+ slash = getPathSeparator(pathSeparator);
+ beginning = pattern.length() == 0 ? false : pattern.charAt(0) == slash;
+ if (!beginning)
+ this.subPattern = pattern;
+ else
+ this.subPattern = pattern.substring(1);
+ }
+
+ public boolean matches(String path, boolean assumeDirectory) {
+ int end = 0;
+ int firstChar = 0;
+ do {
+ firstChar = getFirstNotSlash(path, end);
+ end = getFirstSlash(path, firstChar);
+ boolean match = matches(path, firstChar, end, assumeDirectory);
+ if (match)
+ // make sure the directory matches: either if we are done with
+ // segment and there is next one, or if the directory is assumed
+ return !dirOnly ? true : (end > 0 && end != path.length())
+ || assumeDirectory;
+ } while (!beginning && end != path.length());
+ return false;
+ }
+
+ public boolean matches(String segment, int startIncl, int endExcl,
+ boolean assumeDirectory) {
+ // faster local access, same as in string.indexOf()
+ String s = subPattern;
+ if (s.length() != (endExcl - startIncl))
+ return false;
+ for (int i = 0; i < s.length(); i++) {
+ char c1 = s.charAt(i);
+ char c2 = segment.charAt(i + startIncl);
+ if (c1 != c2)
+ return false;
+ }
+ return true;
+ }
+
+ private int getFirstNotSlash(String s, int start) {
+ int slashIdx = s.indexOf(slash, start);
+ return slashIdx == start ? start + 1 : start;
+ }
+
+ private int getFirstSlash(String s, int start) {
+ int slashIdx = s.indexOf(slash, start);
+ return slashIdx == -1 ? s.length() : slashIdx;
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
new file mode 100644
index 0000000000..753971ed40
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore.internal;
+
+import static org.eclipse.jgit.ignore.internal.Strings.*;
+
+import java.util.*;
+
+import org.eclipse.jgit.errors.InvalidPatternException;
+import org.eclipse.jgit.ignore.FastIgnoreRule;
+
+/**
+ * Matcher built by patterns consists of multiple path segments.
+ * <p>
+ * This class is immutable and thread safe.
+ *
+ * @since 3.6
+ */
+public class PathMatcher extends AbstractMatcher {
+
+ private static final WildMatcher WILD = WildMatcher.INSTANCE;
+
+ private final List<IMatcher> matchers;
+
+ private final char slash;
+
+ private boolean beginning;
+
+ PathMatcher(String pattern, Character pathSeparator, boolean dirOnly)
+ throws InvalidPatternException {
+ super(pattern, dirOnly);
+ slash = getPathSeparator(pathSeparator);
+ beginning = pattern.indexOf(slash) == 0;
+ if (isSimplePathWithSegments(pattern))
+ matchers = null;
+ else
+ matchers = createMatchers(split(pattern, slash), pathSeparator,
+ dirOnly);
+ }
+
+ private boolean isSimplePathWithSegments(String path) {
+ return !isWildCard(path) && count(path, slash, true) > 0;
+ }
+
+ static private List<IMatcher> createMatchers(List<String> segments,
+ Character pathSeparator, boolean dirOnly)
+ throws InvalidPatternException {
+ List<IMatcher> matchers = new ArrayList<IMatcher>(segments.size());
+ for (int i = 0; i < segments.size(); i++) {
+ String segment = segments.get(i);
+ IMatcher matcher = createNameMatcher0(segment, pathSeparator,
+ dirOnly);
+ if (matcher == WILD && i > 0
+ && matchers.get(matchers.size() - 1) == WILD)
+ // collapse wildmatchers **/** is same as **
+ continue;
+ matchers.add(matcher);
+ }
+ return matchers;
+ }
+
+ /**
+ *
+ * @param pattern
+ * @param pathSeparator
+ * if this parameter isn't null then this character will not
+ * match at wildcards(* and ? are wildcards).
+ * @param dirOnly
+ * @return never null
+ * @throws InvalidPatternException
+ */
+ public static IMatcher createPathMatcher(String pattern,
+ Character pathSeparator, boolean dirOnly)
+ throws InvalidPatternException {
+ pattern = pattern.trim();
+ char slash = Strings.getPathSeparator(pathSeparator);
+ // ignore possible leading and trailing slash
+ int slashIdx = pattern.indexOf(slash, 1);
+ if (slashIdx > 0 && slashIdx < pattern.length() - 1)
+ return new PathMatcher(pattern, pathSeparator, dirOnly);
+ return createNameMatcher0(pattern, pathSeparator, dirOnly);
+ }
+
+ private static IMatcher createNameMatcher0(String segment,
+ Character pathSeparator, boolean dirOnly)
+ throws InvalidPatternException {
+ // check if we see /** or ** segments => double star pattern
+ if (WildMatcher.WILDMATCH.equals(segment)
+ || WildMatcher.WILDMATCH2.equals(segment))
+ return WILD;
+ if (isWildCard(segment))
+ return new WildCardMatcher(segment, pathSeparator, dirOnly);
+ return new NameMatcher(segment, pathSeparator, dirOnly);
+ }
+
+ public boolean matches(String path, boolean assumeDirectory) {
+ if (matchers == null)
+ return simpleMatch(path, assumeDirectory);
+ return iterate(path, 0, path.length(), assumeDirectory);
+ }
+
+ /*
+ * Stupid but fast string comparison: the case where we don't have to match
+ * wildcards or single segments (mean: this is multi-segment path which must
+ * be at the beginning of the another string)
+ */
+ private boolean simpleMatch(String path, boolean assumeDirectory) {
+ boolean hasSlash = path.indexOf(slash) == 0;
+ if (beginning && !hasSlash)
+ path = slash + path;
+
+ if (!beginning && hasSlash)
+ path = path.substring(1);
+
+ if (path.equals(pattern))
+ // Exact match
+ if (dirOnly && !assumeDirectory)
+ // Directory expectations not met
+ return false;
+ else
+ // Directory expectations met
+ return true;
+
+ /*
+ * Add slashes for startsWith check. This avoids matching e.g.
+ * "/src/new" to /src/newfile" but allows "/src/new" to match
+ * "/src/new/newfile", as is the git standard
+ */
+ if (path.startsWith(pattern + FastIgnoreRule.PATH_SEPARATOR))
+ return true;
+
+ return false;
+ }
+
+ public boolean matches(String segment, int startIncl, int endExcl,
+ boolean assumeDirectory) {
+ throw new UnsupportedOperationException(
+ "Path matcher works only on entire paths"); //$NON-NLS-1$
+ }
+
+ boolean iterate(final String path, final int startIncl, final int endExcl,
+ boolean assumeDirectory) {
+ int matcher = 0;
+ int right = startIncl;
+ boolean match = false;
+ int lastWildmatch = -1;
+ while (true) {
+ int left = right;
+ right = path.indexOf(slash, right);
+ if (right == -1) {
+ if (left < endExcl)
+ match = matches(matcher, path, left, endExcl,
+ assumeDirectory);
+ if (match) {
+ if (matcher == matchers.size() - 2
+ && matchers.get(matcher + 1) == WILD)
+ // ** can match *nothing*: a/b/** match also a/b
+ return true;
+ if (matcher < matchers.size() - 1
+ && matchers.get(matcher) == WILD) {
+ // ** can match *nothing*: a/**/b match also a/b
+ matcher++;
+ match = matches(matcher, path, left, endExcl,
+ assumeDirectory);
+ } else if (dirOnly)
+ return false;
+ }
+ return match && matcher + 1 == matchers.size();
+ }
+ if (right - left > 0)
+ match = matches(matcher, path, left, right, assumeDirectory);
+ else {
+ // path starts with slash???
+ right++;
+ continue;
+ }
+ if (match) {
+ if (matchers.get(matcher) == WILD) {
+ lastWildmatch = matcher;
+ // ** can match *nothing*: a/**/b match also a/b
+ right = left - 1;
+ }
+ matcher++;
+ if (matcher == matchers.size())
+ return true;
+ } else if (lastWildmatch != -1)
+ matcher = lastWildmatch + 1;
+ else
+ return false;
+ right++;
+ }
+ }
+
+ boolean matches(int matcherIdx, String path, int startIncl, int endExcl,
+ boolean assumeDirectory) {
+ IMatcher matcher = matchers.get(matcherIdx);
+ return matcher.matches(path, startIncl, endExcl, assumeDirectory);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
new file mode 100644
index 0000000000..e9b300760a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/Strings.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore.internal;
+
+import static java.lang.Character.isLetter;
+
+import java.util.*;
+import java.util.regex.Pattern;
+
+import org.eclipse.jgit.errors.InvalidPatternException;
+import org.eclipse.jgit.ignore.FastIgnoreRule;
+
+/**
+ * Various {@link String} related utility methods, written mostly to avoid
+ * generation of new String objects (e.g. via splitting Strings etc).
+ *
+ * @since 3.6
+ */
+public class Strings {
+
+ static char getPathSeparator(Character pathSeparator) {
+ return pathSeparator == null ? FastIgnoreRule.PATH_SEPARATOR
+ : pathSeparator.charValue();
+ }
+
+ /**
+ * @param pattern
+ * non null
+ * @param c
+ * character to remove
+ * @return new string with all trailing characters removed
+ */
+ public static String stripTrailing(String pattern, char c) {
+ while (pattern.length() > 0
+ && pattern.charAt(pattern.length() - 1) == c)
+ pattern = pattern.substring(0, pattern.length() - 1);
+ return pattern;
+ }
+
+ static int count(String s, char c, boolean ignoreFirstLast) {
+ int start = 0;
+ int count = 0;
+ while (true) {
+ start = s.indexOf(c, start);
+ if (start == -1)
+ break;
+ if (!ignoreFirstLast || (start != 0 && start != s.length()))
+ count++;
+ start++;
+ }
+ return count;
+ }
+
+ /**
+ * Splits given string to substrings by given separator
+ *
+ * @param pattern
+ * non null
+ * @param slash
+ * separator char
+ * @return list of substrings
+ */
+ public static List<String> split(String pattern, char slash) {
+ int count = count(pattern, slash, true);
+ if (count < 1)
+ throw new IllegalStateException(
+ "Pattern must have at least two segments: " + pattern); //$NON-NLS-1$
+ List<String> segments = new ArrayList<String>(count);
+ int right = 0;
+ while (true) {
+ int left = right;
+ right = pattern.indexOf(slash, right);
+ if (right == -1) {
+ if (left < pattern.length())
+ segments.add(pattern.substring(left));
+ break;
+ }
+ if (right - left > 0)
+ if (left == 1)
+ // leading slash should remain by the first pattern
+ segments.add(pattern.substring(left - 1, right));
+ else if (right == pattern.length() - 1)
+ // trailing slash should remain too
+ segments.add(pattern.substring(left, right + 1));
+ else
+ segments.add(pattern.substring(left, right));
+ right++;
+ }
+ return segments;
+ }
+
+ static boolean isWildCard(String pattern) {
+ return pattern.indexOf('*') != -1 || pattern.indexOf('?') != -1
+ || pattern.indexOf('[') != -1
+ // required to match escaped backslashes '\\\\'
+ || pattern.indexOf('\\') != -1 || pattern.indexOf(']') != -1;
+ }
+
+ final static List<String> POSIX_CHAR_CLASSES = Arrays.asList(
+ "alnum", "alpha", "blank", "cntrl", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ // [:alnum:] [:alpha:] [:blank:] [:cntrl:]
+ "digit", "graph", "lower", "print", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ // [:digit:] [:graph:] [:lower:] [:print:]
+ "punct", "space", "upper", "xdigit", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ // [:punct:] [:space:] [:upper:] [:xdigit:]
+ "word" //$NON-NLS-1$
+ // [:word:] XXX I don't see it in
+ // http://man7.org/linux/man-pages/man7/glob.7.html
+ // but this was in org.eclipse.jgit.fnmatch.GroupHead.java ???
+ );
+
+ private static final String DL = "\\p{javaDigit}\\p{javaLetter}"; //$NON-NLS-1$
+
+ final static List<String> JAVA_CHAR_CLASSES = Arrays
+ .asList("\\p{Alnum}", "\\p{javaLetter}", "\\p{Blank}", "\\p{Cntrl}", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ // [:alnum:] [:alpha:] [:blank:] [:cntrl:]
+ "\\p{javaDigit}", "[\\p{Graph}" + DL + "]", "\\p{Ll}", "[\\p{Print}" + DL + "]", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+ // [:digit:] [:graph:] [:lower:] [:print:]
+ "\\p{Punct}", "\\p{Space}", "\\p{Lu}", "\\p{XDigit}", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ // [:punct:] [:space:] [:upper:] [:xdigit:]
+ "[" + DL + "_]" //$NON-NLS-1$ //$NON-NLS-2$
+ // [:word:]
+ );
+
+ // Collating symbols [[.a.]] or equivalence class expressions [[=a=]] are
+ // not supported by CLI git (at least not by 1.9.1)
+ final static Pattern UNSUPPORTED = Pattern
+ .compile("\\[\\[[.=]\\w+[.=]\\]\\]"); //$NON-NLS-1$
+
+ /**
+ * Conversion from glob to Java regex following two sources: <li>
+ * http://man7.org/linux/man-pages/man7/glob.7.html <li>
+ * org.eclipse.jgit.fnmatch.FileNameMatcher.java Seems that there are
+ * various ways to define what "glob" can be.
+ *
+ * @param pattern
+ * non null pattern
+ *
+ * @return Java regex pattern corresponding to given glob pattern
+ * @throws InvalidPatternException
+ */
+ static Pattern convertGlob(String pattern) throws InvalidPatternException {
+ if (UNSUPPORTED.matcher(pattern).find())
+ throw new InvalidPatternException(
+ "Collating symbols [[.a.]] or equivalence class expressions [[=a=]] are not supported", //$NON-NLS-1$
+ pattern);
+
+ StringBuilder sb = new StringBuilder(pattern.length());
+
+ int in_brackets = 0;
+ boolean seenEscape = false;
+ boolean ignoreLastBracket = false;
+ boolean in_char_class = false;
+ // 6 is the length of the longest posix char class "xdigit"
+ char[] charClass = new char[6];
+
+ for (int i = 0; i < pattern.length(); i++) {
+ char c = pattern.charAt(i);
+ switch (c) {
+
+ case '*':
+ if (seenEscape || in_brackets > 0)
+ sb.append(c);
+ else
+ sb.append('.').append(c);
+ break;
+
+ case '.':
+ if (seenEscape)
+ sb.append(c);
+ else
+ sb.append('\\').append('.');
+ break;
+
+ case '?':
+ if (seenEscape || in_brackets > 0)
+ sb.append(c);
+ else
+ sb.append('.');
+ break;
+
+ case ':':
+ if (in_brackets > 0)
+ if (lookBehind(sb) == '['
+ && isLetter(lookAhead(pattern, i)))
+ in_char_class = true;
+ sb.append(':');
+ break;
+
+ case '-':
+ if (in_brackets > 0) {
+ if (lookAhead(pattern, i) == ']')
+ sb.append('\\').append(c);
+ else
+ sb.append(c);
+ } else
+ sb.append('-');
+ break;
+
+ case '\\':
+ if (in_brackets > 0) {
+ char lookAhead = lookAhead(pattern, i);
+ if (lookAhead == ']' || lookAhead == '[')
+ ignoreLastBracket = true;
+ }
+ sb.append(c);
+ break;
+
+ case '[':
+ if (in_brackets > 0) {
+ sb.append('\\').append('[');
+ ignoreLastBracket = true;
+ } else {
+ if (!seenEscape) {
+ in_brackets++;
+ ignoreLastBracket = false;
+ }
+ sb.append('[');
+ }
+ break;
+
+ case ']':
+ if (seenEscape) {
+ sb.append(']');
+ ignoreLastBracket = true;
+ break;
+ }
+ if (in_brackets <= 0) {
+ sb.append('\\').append(']');
+ ignoreLastBracket = true;
+ break;
+ }
+ char lookBehind = lookBehind(sb);
+ if ((lookBehind == '[' && !ignoreLastBracket)
+ || lookBehind == '^') {
+ sb.append('\\');
+ sb.append(']');
+ ignoreLastBracket = true;
+ } else {
+ ignoreLastBracket = false;
+ if (!in_char_class) {
+ in_brackets--;
+ sb.append(']');
+ } else {
+ in_char_class = false;
+ String charCl = checkPosixCharClass(charClass);
+ // delete last \[:: chars and set the pattern
+ if (charCl != null) {
+ sb.setLength(sb.length() - 4);
+ sb.append(charCl);
+ }
+ reset(charClass);
+ }
+ }
+ break;
+
+ case '!':
+ if (in_brackets > 0) {
+ if (lookBehind(sb) == '[')
+ sb.append('^');
+ else
+ sb.append(c);
+ } else
+ sb.append(c);
+ break;
+
+ default:
+ if (in_char_class)
+ setNext(charClass, c);
+ else
+ sb.append(c);
+ break;
+ } // end switch
+
+ seenEscape = c == '\\';
+
+ } // end for
+
+ if (in_brackets > 0)
+ throw new InvalidPatternException("Not closed bracket?", pattern); //$NON-NLS-1$
+ return Pattern.compile(sb.toString());
+ }
+
+ /**
+ * @param buffer
+ * @return zero of the buffer is empty, otherwise the last character from
+ * buffer
+ */
+ private static char lookBehind(StringBuilder buffer) {
+ return buffer.length() > 0 ? buffer.charAt(buffer.length() - 1) : 0;
+ }
+
+ /**
+ * @param pattern
+ * @param i
+ * current pointer in the pattern
+ * @return zero of the index is out of range, otherwise the next character
+ * from given position
+ */
+ private static char lookAhead(String pattern, int i) {
+ int idx = i + 1;
+ return idx >= pattern.length() ? 0 : pattern.charAt(idx);
+ }
+
+ private static void setNext(char[] buffer, char c) {
+ for (int i = 0; i < buffer.length; i++)
+ if (buffer[i] == 0) {
+ buffer[i] = c;
+ break;
+ }
+ }
+
+ private static void reset(char[] buffer) {
+ for (int i = 0; i < buffer.length; i++)
+ buffer[i] = 0;
+ }
+
+ private static String checkPosixCharClass(char[] buffer) {
+ for (int i = 0; i < POSIX_CHAR_CLASSES.size(); i++) {
+ String clazz = POSIX_CHAR_CLASSES.get(i);
+ boolean match = true;
+ for (int j = 0; j < clazz.length(); j++)
+ if (buffer[j] != clazz.charAt(j)) {
+ match = false;
+ break;
+ }
+ if (match)
+ return JAVA_CHAR_CLASSES.get(i);
+ }
+ return null;
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
new file mode 100644
index 0000000000..7d12b0ddce
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildCardMatcher.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore.internal;
+
+import static org.eclipse.jgit.ignore.internal.Strings.convertGlob;
+
+import java.util.regex.Pattern;
+
+import org.eclipse.jgit.errors.InvalidPatternException;
+
+/**
+ * Matcher built from path segments containing wildcards. This matcher converts
+ * glob wildcards to Java {@link Pattern}'s.
+ * <p>
+ * This class is immutable and thread safe.
+ *
+ * @since 3.6
+ */
+public class WildCardMatcher extends NameMatcher {
+
+ final Pattern p;
+
+ WildCardMatcher(String pattern, Character pathSeparator, boolean dirOnly)
+ throws InvalidPatternException {
+ super(pattern, pathSeparator, dirOnly);
+ p = convertGlob(subPattern);
+ }
+
+ @Override
+ public boolean matches(String segment, int startIncl, int endExcl,
+ boolean assumeDirectory) {
+ return p.matcher(segment.substring(startIncl, endExcl)).matches();
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
new file mode 100644
index 0000000000..d578654375
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/WildMatcher.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014, Andrey Loskutov <loskutov@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.ignore.internal;
+
+/**
+ * Wildmatch matcher for "double star" (<code>**</code>) pattern only. This
+ * matcher matches any path.
+ * <p>
+ * This class is immutable and thread safe.
+ *
+ * @since 3.6
+ */
+public final class WildMatcher extends AbstractMatcher {
+
+ static final String WILDMATCH = "**"; //$NON-NLS-1$
+
+ // double star for the beginning of pattern
+ static final String WILDMATCH2 = "/**"; //$NON-NLS-1$
+
+ static final WildMatcher INSTANCE = new WildMatcher();
+
+ private WildMatcher() {
+ super(WILDMATCH, false);
+ }
+
+ public final boolean matches(String path, boolean assumeDirectory) {
+ return true;
+ }
+
+ public final boolean matches(String segment, int startIncl, int endExcl,
+ boolean assumeDirectory) {
+ return true;
+ }
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 3d63d92542..cc5ef18074 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -70,8 +70,8 @@ import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoWorkTreeException;
+import org.eclipse.jgit.ignore.FastIgnoreRule;
import org.eclipse.jgit.ignore.IgnoreNode;
-import org.eclipse.jgit.ignore.IgnoreRule;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.CoreConfig;
@@ -1132,7 +1132,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
final Entry entry;
PerDirectoryIgnoreNode(Entry entry) {
- super(Collections.<IgnoreRule> emptyList());
+ super(Collections.<FastIgnoreRule> emptyList());
this.entry = entry;
}