Browse Source

Fix another test case mentioned by Segev in #526

Jelmer Vernooij 7 years ago
parent
commit
e2a556309a
2 changed files with 61 additions and 37 deletions
  1. 44 35
      dulwich/ignore.py
  2. 17 2
      dulwich/tests/test_ignore.py

+ 44 - 35
dulwich/ignore.py

@@ -27,6 +27,41 @@ import re
 import sys
 
 
+def _translate_segment(segment):
+    if segment == b"*":
+        return b'[^/]+'
+    res = b""
+    i, n = 0, len(segment)
+    while i < n:
+        c = segment[i:i+1]
+        i = i+1
+        if c == b'*':
+            res += b'[^/]*'
+        elif c == b'?':
+            res += b'.'
+        elif c == b'[':
+            j = i
+            if j < n and segment[j:j+1] == b'!':
+                j = j+1
+            if j < n and segment[j:j+1] == b']':
+                j = j+1
+            while j < n and segment[j:j+1] != b']':
+                j = j+1
+            if j >= n:
+                res += b'\\['
+            else:
+                stuff = segment[i:j].replace(b'\\', b'\\\\')
+                i = j+1
+                if stuff.startswith(b'!'):
+                    stuff = b'^' + stuff[1:]
+                elif stuff.startswith(b'^'):
+                    stuff = b'\\' + stuff
+                res += b'[' + stuff + b']'
+        else:
+            res += re.escape(c)
+    return res
+
+
 def translate(pat):
     """Translate a shell PATTERN to a regular expression.
 
@@ -40,52 +75,26 @@ def translate(pat):
 
     if b'/' not in pat[:-1]:
         # If there's no slash, this is a filename-based match
-        res = res + b'(.*/)?'
+        res += b'(.*/)?'
 
     if pat.startswith(b'**/'):
         # Leading **/
         pat = pat[2:]
-        res = res + b'(.*/)?'
+        res += b'(.*/)?'
 
     if pat.startswith(b'/'):
         pat = pat[1:]
 
-    i, n = 0, len(pat)
-
-    while i < n:
-        if pat[i:i+3] == b'/**':
-            res = res + b'(/.*)?'
-            i = i+3
+    for i, segment in enumerate(pat.split(b'/')):
+        if segment == b'**':
+            res += b'(/.*)?'
             continue
-        c = pat[i:i+1]
-        i = i+1
-        if c == b'*':
-            res = res + b'[^/]*'
-        elif c == b'?':
-            res = res + b'.'
-        elif c == b'[':
-            j = i
-            if j < n and pat[j:j+1] == b'!':
-                j = j+1
-            if j < n and pat[j:j+1] == b']':
-                j = j+1
-            while j < n and pat[j:j+1] != b']':
-                j = j+1
-            if j >= n:
-                res = res + b'\\['
-            else:
-                stuff = pat[i:j].replace(b'\\', b'\\\\')
-                i = j+1
-                if stuff.startswith(b'!'):
-                    stuff = b'^' + stuff[1:]
-                elif stuff.startswith(b'^'):
-                    stuff = b'\\' + stuff
-                res = res + b'[' + stuff + b']'
         else:
-            res = res + re.escape(c)
+            res += ((re.escape(b'/') if i > 0 else b'') +
+                    _translate_segment(segment))
 
-    if not res.endswith(b'/'):
-        res = res + b'/?'
+    if not pat.endswith(b'/'):
+        res += b'/?'
 
     return res + b'\Z'
 

+ 17 - 2
dulwich/tests/test_ignore.py

@@ -56,7 +56,7 @@ POSITIVE_MATCH_TESTS = [
     (b"foo/bla/bar", b"foo/**/bar"),
     (b"foo/bar/", b"bar/"),
     (b"foo/bar/", b"bar"),
-    (b"foo/bar/", b"foo/bar/*"),
+    (b"foo/bar/something", b"foo/bar/*"),
 ]
 
 NEGATIVE_MATCH_TESTS = [
@@ -64,6 +64,7 @@ NEGATIVE_MATCH_TESTS = [
     (b"foo/foo.c", b"/foo.c"),
     (b"foo/foo.c", b"/*.c"),
     (b"foo/bar/", b"/bar/"),
+    (b"foo/bar/", b"foo/bar/*"),
 ]
 
 
@@ -79,7 +80,7 @@ TRANSLATE_TESTS = [
     (b"foo/**/blie.c", b'(?ms)foo(/.*)?\\/blie\\.c/?\\Z'),
     (b"**/bla.c", b'(?ms)(.*/)?bla\\.c/?\\Z'),
     (b"foo/**/bar", b'(?ms)foo(/.*)?\\/bar/?\\Z'),
-    (b"foo/bar/*", b'(?ms)foo\\/bar\\/[^/]*/?\\Z'),
+    (b"foo/bar/*", b'(?ms)foo\\/bar\\/[^/]+/?\\Z'),
 ]
 
 
@@ -243,3 +244,17 @@ class IgnoreFilterManagerTests(TestCase):
         m = IgnoreFilterManager.from_repo(repo)
         self.assertTrue(m.is_ignored(os.path.join('dir', 'blie')))
         self.assertTrue(m.is_ignored(os.path.join('DIR', 'blie')))
+
+    def test_ignored_contents(self):
+        tmp_dir = tempfile.mkdtemp()
+        self.addCleanup(shutil.rmtree, tmp_dir)
+        repo = Repo.init(tmp_dir)
+        with open(os.path.join(repo.path, '.gitignore'), 'wb') as f:
+            f.write(b'a/*\n')
+            f.write(b'!a/*.txt\n')
+        m = IgnoreFilterManager.from_repo(repo)
+        os.mkdir(os.path.join(repo.path, 'a'))
+        self.assertIs(None, m.is_ignored('a'))
+        self.assertIs(None, m.is_ignored('a/'))
+        self.assertFalse(m.is_ignored('a/b.txt'))
+        self.assertTrue(m.is_ignored('a/c.dat'))