]> source.dussan.org Git - jgit.git/commitdiff
Exclude file matching: fix backtracking on match failures after ** 27/103027/6
authorThomas Wolf <thomas.wolf@paranor.ch>
Sun, 13 Aug 2017 13:08:16 +0000 (15:08 +0200)
committerMatthias Sohn <matthias.sohn@sap.com>
Sun, 27 Aug 2017 14:02:41 +0000 (16:02 +0200)
** matching always tries the empty match first. If a mismatch occurs
later, the ** must be extended by exactly one segment and matching must
resume with the matcher following the ** matcher.

Bug: 520920
Change-Id: Id019ad1c773bd645ae92e398021952f8e961f45c
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesHandlerTest.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/CGitAttributesTest.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/CGitIgnoreTest.java
org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/PathMatcher.java

index 0717379e244f145b4b9e1e908dc095a1e2a1b6e5..5868482c886438c882d4f1f3379107da3ca40e78 100644 (file)
@@ -451,6 +451,50 @@ public class AttributesHandlerTest extends RepositoryTestCase {
                endWalk();
        }
 
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack() throws Exception {
+               setupRepo(null, null, "**/sub/new/ bar", null);
+               writeTrashFile("sub/new/foo.txt", "1");
+               writeTrashFile("foo/sub/new/foo.txt", "2");
+               writeTrashFile("sub/sub/new/foo.txt", "3");
+               walk = beginWalk();
+               assertIteration(F, ".gitattributes");
+               assertIteration(D, "foo");
+               assertIteration(D, "foo/sub");
+               assertIteration(D, "foo/sub/new", attrs("bar"));
+               assertIteration(F, "foo/sub/new/foo.txt");
+               assertIteration(D, "sub");
+               assertIteration(F, "sub/a.txt");
+               assertIteration(D, "sub/new", attrs("bar"));
+               assertIteration(F, "sub/new/foo.txt");
+               assertIteration(D, "sub/sub");
+               assertIteration(D, "sub/sub/new", attrs("bar"));
+               assertIteration(F, "sub/sub/new/foo.txt");
+               endWalk();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception {
+               setupRepo(null, null, "**/**/sub/new/ bar", null);
+               writeTrashFile("sub/new/foo.txt", "1");
+               writeTrashFile("foo/sub/new/foo.txt", "2");
+               writeTrashFile("sub/sub/new/foo.txt", "3");
+               walk = beginWalk();
+               assertIteration(F, ".gitattributes");
+               assertIteration(D, "foo");
+               assertIteration(D, "foo/sub");
+               assertIteration(D, "foo/sub/new", attrs("bar"));
+               assertIteration(F, "foo/sub/new/foo.txt");
+               assertIteration(D, "sub");
+               assertIteration(F, "sub/a.txt");
+               assertIteration(D, "sub/new", attrs("bar"));
+               assertIteration(F, "sub/new/foo.txt");
+               assertIteration(D, "sub/sub");
+               assertIteration(D, "sub/sub/new", attrs("bar"));
+               assertIteration(F, "sub/sub/new/foo.txt");
+               endWalk();
+       }
+
        @Test
        public void testDirectoryMatchSubComplex() throws Exception {
                setupRepo(null, null, "s[uv]b/n*/ bar", null);
index c8ff6c8752b2b958a983fae9f64cd7aa8c594e39..6188fa60c934de0cf1e836ffd2de389a796c4057 100644 (file)
@@ -319,6 +319,51 @@ public class CGitAttributesTest extends RepositoryTestCase {
                assertSameAsCGit();
        }
 
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack() throws Exception {
+               createFiles("src/new/foo.txt", "src/src/new/foo.txt");
+               writeTrashFile(".gitattributes", "**/src/new/ bar\n");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception {
+               createFiles("src/new/foo.txt", "src/src/new/foo.txt");
+               writeTrashFile(".gitattributes", "**/**/src/new/ bar\n");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack3() throws Exception {
+               createFiles("src/new/src/new/foo.txt",
+                               "foo/src/new/bar/src/new/foo.txt");
+               writeTrashFile(".gitattributes", "**/src/new/ bar\n");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack4() throws Exception {
+               createFiles("src/src/src/new/foo.txt",
+                               "foo/src/src/bar/src/new/foo.txt");
+               writeTrashFile(".gitattributes", "**/src/ bar\n");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack5() throws Exception {
+               createFiles("x/a/a/b/foo.txt", "x/y/z/b/a/b/foo.txt",
+                               "x/y/a/a/a/a/b/foo.txt", "x/y/a/a/a/a/b/a/b/foo.txt");
+               writeTrashFile(".gitattributes", "**/*/a/b bar\n");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack6() throws Exception {
+               createFiles("x/a/a/b/foo.txt", "x/y/a/b/a/b/foo.txt");
+               writeTrashFile(".gitattributes", "**/*/**/a/b bar\n");
+               assertSameAsCGit();
+       }
+
        @Test
        public void testDirectoryMatchSubComplex() throws Exception {
                createFiles("src/new/foo.txt", "foo/src/new/foo.txt", "sub/src/new");
index 3ea894f492a2afc0696eb4257dfa4b366c4948d3..c61f2ea7f6bd4dd6cc0105351757519414726d31 100644 (file)
@@ -203,4 +203,41 @@ public class CGitIgnoreTest extends RepositoryTestCase {
                writeTrashFile(".gitignore", "**/src/new/");
                assertSameAsCGit();
        }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack() throws Exception {
+               createFiles("src/new/foo.txt", "src/src/new/foo.txt");
+               writeTrashFile(".gitignore", "**/src/new/");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack2() throws Exception {
+               createFiles("src/new/foo.txt", "src/src/new/foo.txt");
+               writeTrashFile(".gitignore", "**/**/src/new/");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack3() throws Exception {
+               createFiles("x/a/a/b/foo.txt");
+               writeTrashFile(".gitignore", "**/*/a/b/");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack4() throws Exception {
+               createFiles("x/a/a/b/foo.txt", "x/y/z/b/a/b/foo.txt",
+                               "x/y/a/a/a/a/b/foo.txt", "x/y/a/a/a/a/b/a/b/foo.txt");
+               writeTrashFile(".gitignore", "**/*/a/b bar\n");
+               assertSameAsCGit();
+       }
+
+       @Test
+       public void testDirectoryMatchSubRecursiveBacktrack5() throws Exception {
+               createFiles("x/a/a/b/foo.txt", "x/y/a/b/a/b/foo.txt");
+               writeTrashFile(".gitignore", "**/*/**/a/b bar\n");
+               assertSameAsCGit();
+       }
+
 }
index 85073ecfb2d01049fab01eaaa6d5484a16ac7aa3..9b3a2aac78c41605764b439784aa9098d75082a4 100644 (file)
@@ -225,6 +225,11 @@ public class PathMatcher extends AbstractMatcher {
                int right = startIncl;
                boolean match = false;
                int lastWildmatch = -1;
+               // ** matches may get extended if a later match fails. When that
+               // happens, we must extend the ** by exactly one segment.
+               // wildmatchBacktrackPos records the end of the segment after a **
+               // match, so that we can reset correctly.
+               int wildmatchBacktrackPos = -1;
                while (true) {
                        int left = right;
                        right = path.indexOf(slash, right);
@@ -250,6 +255,9 @@ public class PathMatcher extends AbstractMatcher {
                                }
                                return match && matcher + 1 == matchers.size();
                        }
+                       if (wildmatchBacktrackPos < 0) {
+                               wildmatchBacktrackPos = right;
+                       }
                        if (right - left > 0) {
                                match = matches(matcher, path, left, right, assumeDirectory);
                        } else {
@@ -261,6 +269,7 @@ public class PathMatcher extends AbstractMatcher {
                                boolean wasWild = matchers.get(matcher) == WILD;
                                if (wasWild) {
                                        lastWildmatch = matcher;
+                                       wildmatchBacktrackPos = -1;
                                        // ** can match *nothing*: a/**/b match also a/b
                                        right = left - 1;
                                }
@@ -276,11 +285,25 @@ public class PathMatcher extends AbstractMatcher {
                                                        return !dirOnly || assumeDirectory;
                                                }
                                                // Prefix matches only if pattern ended with /**
-                                               return wasWild;
+                                               if (wasWild) {
+                                                       return true;
+                                               }
+                                               if (lastWildmatch >= 0) {
+                                                       // Consider pattern **/x and input x/x.
+                                                       // We've matched the prefix x/ so far: we
+                                                       // must try to extend the **!
+                                                       matcher = lastWildmatch + 1;
+                                                       right = wildmatchBacktrackPos;
+                                                       wildmatchBacktrackPos = -1;
+                                               } else {
+                                                       return false;
+                                               }
                                        }
                                }
                        } else if (lastWildmatch != -1) {
                                matcher = lastWildmatch + 1;
+                               right = wildmatchBacktrackPos;
+                               wildmatchBacktrackPos = -1;
                        } else {
                                return false;
                        }