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);
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");
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();
+ }
+
}
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);
}
return match && matcher + 1 == matchers.size();
}
+ if (wildmatchBacktrackPos < 0) {
+ wildmatchBacktrackPos = right;
+ }
if (right - left > 0) {
match = matches(matcher, path, left, right, assumeDirectory);
} else {
boolean wasWild = matchers.get(matcher) == WILD;
if (wasWild) {
lastWildmatch = matcher;
+ wildmatchBacktrackPos = -1;
// ** can match *nothing*: a/**/b match also a/b
right = left - 1;
}
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;
}