changeset 51569:b32c3146ec34 stable

match: fix the "visitdir" method on "rootfilesin" matchers This fixes just the Python side, the fix for the rust side will follow shortly.
author Arseniy Alekseyev <aalekseyev@janestreet.com>
date Fri, 12 Apr 2024 15:39:21 +0100
parents 2a89d2f6336f
children b39057b713b1
files mercurial/match.py tests/test-match.py tests/test-status.t
diffstat 3 files changed, 23 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/match.py	Fri Apr 12 14:21:14 2024 +0100
+++ b/mercurial/match.py	Fri Apr 12 15:39:21 2024 +0100
@@ -638,7 +638,10 @@
         super(patternmatcher, self).__init__(badfn)
         kindpats.sort()
 
+        roots, dirs, parents = _rootsdirsandparents(kindpats)
         self._files = _explicitfiles(kindpats)
+        self._dirs_explicit = set(dirs)
+        self._dirs = parents
         self._prefix = _prefix(kindpats)
         self._pats, self._matchfn = _buildmatch(kindpats, b'$', root)
 
@@ -647,14 +650,14 @@
             return True
         return self._matchfn(fn)
 
-    @propertycache
-    def _dirs(self):
-        return set(pathutil.dirs(self._fileset))
-
     def visitdir(self, dir):
         if self._prefix and dir in self._fileset:
             return b'all'
-        return dir in self._dirs or path_or_parents_in_set(dir, self._fileset)
+        return (
+            dir in self._dirs
+            or path_or_parents_in_set(dir, self._fileset)
+            or path_or_parents_in_set(dir, self._dirs_explicit)
+        )
 
     def visitchildrenset(self, dir):
         ret = self.visitdir(dir)
@@ -1461,7 +1464,7 @@
         allgroups = []
         regexps = []
         exact = set()
-        for (kind, pattern, _source) in kindpats:
+        for kind, pattern, _source in kindpats:
             if kind == b'filepath':
                 exact.add(pattern)
                 continue
--- a/tests/test-match.py	Fri Apr 12 14:21:14 2024 +0100
+++ b/tests/test-match.py	Fri Apr 12 15:39:21 2024 +0100
@@ -94,12 +94,14 @@
             patterns=[b'rootfilesin:dir/subdir'],
         )
         assert isinstance(m, matchmod.patternmatcher)
-        self.assertFalse(m.visitdir(b'dir/subdir/x'))
+        # OPT: we shouldn't visit [x] as a directory,
+        # but we should still visit it as a file.
+        # Unfortunately, `visitdir` is used for both.
+        self.assertTrue(m.visitdir(b'dir/subdir/x'))
         self.assertFalse(m.visitdir(b'folder'))
-        # FIXME: These should probably be True.
-        self.assertFalse(m.visitdir(b''))
-        self.assertFalse(m.visitdir(b'dir'))
-        self.assertFalse(m.visitdir(b'dir/subdir'))
+        self.assertTrue(m.visitdir(b''))
+        self.assertTrue(m.visitdir(b'dir'))
+        self.assertTrue(m.visitdir(b'dir/subdir'))
 
     def testVisitchildrensetRootfilesin(self):
         m = matchmod.match(
@@ -108,13 +110,13 @@
             patterns=[b'rootfilesin:dir/subdir'],
         )
         assert isinstance(m, matchmod.patternmatcher)
-        self.assertEqual(m.visitchildrenset(b'dir/subdir/x'), set())
+        self.assertEqual(m.visitchildrenset(b'dir/subdir/x'), b'this')
         self.assertEqual(m.visitchildrenset(b'folder'), set())
-        # FIXME: These should probably be {'dir'}, {'subdir'} and 'this',
-        # respectively, or at least 'this' for all three.
-        self.assertEqual(m.visitchildrenset(b''), set())
-        self.assertEqual(m.visitchildrenset(b'dir'), set())
-        self.assertEqual(m.visitchildrenset(b'dir/subdir'), set())
+        # OPT: These should probably be {'dir'}, {'subdir'} and 'this',
+        # respectively
+        self.assertEqual(m.visitchildrenset(b''), b'this')
+        self.assertEqual(m.visitchildrenset(b'dir'), b'this')
+        self.assertEqual(m.visitchildrenset(b'dir/subdir'), b'this')
 
     def testVisitdirGlob(self):
         m = matchmod.match(
--- a/tests/test-status.t	Fri Apr 12 14:21:14 2024 +0100
+++ b/tests/test-status.t	Fri Apr 12 15:39:21 2024 +0100
@@ -863,6 +863,7 @@
   M subdir/modified (no-rhg !)
   R subdir/removed (no-rhg !)
   ! subdir/deleted (no-rhg !)
+  ? subdir/unknown (no-rhg !)
 
 Note: `hg status some-name` creates a patternmatcher which is not supported
 yet by the Rust implementation of status, but includematcher is supported.