Patchwork [05,of,17] match: handle includes using new intersectionmatcher

login
register
mail settings
Submitter via Mercurial-devel
Date May 25, 2017, 6:24 p.m.
Message ID <4ca8c5116b65020cce25.1495736686@martinvonz.svl.corp.google.com>
Download mbox | patch
Permalink /patch/20907/
State Accepted
Headers show

Comments

via Mercurial-devel - May 25, 2017, 6:24 p.m.
# HG changeset patch
# User Martin von Zweigbergk <martinvonz@google.com>
# Date 1494655925 25200
#      Fri May 12 23:12:05 2017 -0700
# Node ID 4ca8c5116b65020cce2580a342ebca68b9dea8cc
# Parent  f21b39be3c4f4c541a6e0b0d2ae483d5de243ba5
match: handle includes using new intersectionmatcher
via Mercurial-devel - May 25, 2017, 9:25 p.m.
On Thu, May 25, 2017 at 11:24 AM, Martin von Zweigbergk
<martinvonz@google.com> wrote:
> # HG changeset patch
> # User Martin von Zweigbergk <martinvonz@google.com>
> # Date 1494655925 25200
> #      Fri May 12 23:12:05 2017 -0700
> # Node ID 4ca8c5116b65020cce2580a342ebca68b9dea8cc
> # Parent  f21b39be3c4f4c541a6e0b0d2ae483d5de243ba5
> match: handle includes using new intersectionmatcher

I'm sorry, a last-minute change to uipath() handling caused a bug in
this patch so test-addremove.t now fails. Working on a fix. The first
4 patches are still good to go from my point of view.
via Mercurial-devel - May 25, 2017, 9:50 p.m.
On Thu, May 25, 2017 at 2:25 PM, Martin von Zweigbergk
<martinvonz@google.com> wrote:
> On Thu, May 25, 2017 at 11:24 AM, Martin von Zweigbergk
> <martinvonz@google.com> wrote:
>> # HG changeset patch
>> # User Martin von Zweigbergk <martinvonz@google.com>
>> # Date 1494655925 25200
>> #      Fri May 12 23:12:05 2017 -0700
>> # Node ID 4ca8c5116b65020cce2580a342ebca68b9dea8cc
>> # Parent  f21b39be3c4f4c541a6e0b0d2ae483d5de243ba5
>> match: handle includes using new intersectionmatcher
>
> I'm sorry, a last-minute change to uipath() handling caused a bug in
> this patch so test-addremove.t now fails. Working on a fix. The first
> 4 patches are still good to go from my point of view.

The good news is that it looks like the solution addresses Yuya's
comment about the unused uipath() in basematcher and it also
simplifies a later patch. I'll still let someone queue (or reject) the
first 4 before I send a v2.

Patch

diff --git a/mercurial/match.py b/mercurial/match.py
--- a/mercurial/match.py
+++ b/mercurial/match.py
@@ -142,9 +142,14 @@ 
                 kindpats.append((kind, pats, source))
             return kindpats
 
-    m = matcher(root, cwd, normalize, patterns, include=include,
+    m = matcher(root, cwd, normalize, patterns, include=None,
                 default=default, exact=exact, auditor=auditor, ctx=ctx,
                 listsubrepos=listsubrepos, warn=warn, badfn=badfn)
+    if include:
+        im = matcher(root, cwd, normalize, [], include=include, default=default,
+                     exact=False, auditor=auditor, ctx=ctx,
+                     listsubrepos=listsubrepos, warn=warn, badfn=None)
+        m = intersectmatchers(m, im)
     if exclude:
         em = matcher(root, cwd, normalize, [], include=exclude, default=default,
                      exact=False, auditor=auditor, ctx=ctx,
@@ -459,6 +464,76 @@ 
     def __repr__(self):
         return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
 
+def intersectmatchers(m1, m2):
+    '''Composes two matchers by matching if both of them match.
+
+    The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
+    traversedir) are ignored.
+    '''
+    if m1 is None or m2 is None:
+        return m1 or m2
+    if m1.always():
+        m = copy.copy(m2)
+        # TODO: Consider encapsulating these things in a class so there's only
+        # one thing to copy from m1.
+        m.bad = m1.bad
+        m.explicitdir = m1.explicitdir
+        m.traversedir = m1.traversedir
+        m.abs = m1.abs
+        m.rel = m1.rel
+        m.uipath = m1.uipath
+        return m
+    if m2.always():
+        return m1
+    return intersectionmatcher(m1, m2)
+
+class intersectionmatcher(basematcher):
+    def __init__(self, m1, m2):
+        super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
+        self._m1 = m1
+        self._m2 = m2
+        self.bad = m1.bad
+        self.explicitdir = m1.explicitdir
+        self.traversedir = m1.traversedir
+
+    @propertycache
+    def _files(self):
+        if self.isexact():
+            m1, m2 = self._m1, self._m2
+            if not m1.isexact():
+                m1, m2 = m2, m1
+            return [f for f in m1.files() if m2(f)]
+        # It neither m1 nor m2 is an exact matcher, we can't easily intersect
+        # the set of files, because their files() are not always files. For
+        # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
+        # "path:dir2", we don't want to remove "dir2" from the set.
+        return self._m1.files() + self._m2.files()
+
+    def uipath(self, f):
+        return self._m1.uipath(f)
+
+    def matchfn(self, f):
+        return self._m1(f) and self._m2(f)
+
+    def visitdir(self, dir):
+        visit1 = self._m1.visitdir(dir)
+        if visit1 == 'all':
+            return self._m2.visitdir(dir)
+        # bool() because visit1=True + visit2='all' should not be 'all'
+        return bool(visit1 and self._m2.visitdir(dir))
+
+    def always(self):
+        return self._m1.always() and self._m2.always()
+
+    def isexact(self):
+        return self._m1.isexact() or self._m2.isexact()
+
+    def anypats(self):
+        return self._m1.anypats() or self._m2.anypats()
+
+    def __repr__(self):
+        return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
+
 class subdirmatcher(basematcher):
     """Adapt a matcher to work on a subdirectory only.
 
diff --git a/tests/test-walk.t b/tests/test-walk.t
--- a/tests/test-walk.t
+++ b/tests/test-walk.t
@@ -96,7 +96,7 @@ 
   f  fenugreek      ../fenugreek
   f  mammals/skunk  skunk
   $ hg debugwalk -I 'relglob:*k' .
-  matcher: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes='(?:(?:|.*/)[^/]*k(?:/|$))'>
+  matcher: <intersectionmatcher m1=<matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes=None>, m2=<matcher files=[], patterns=None, includes='(?:(?:|.*/)[^/]*k(?:/|$))'>>
   f  mammals/skunk  skunk
   $ hg debugwalk -I 're:.*k$'
   matcher: <matcher files=[], patterns=None, includes='(?:.*k$)'>
@@ -276,17 +276,17 @@ 
   f  fenugreek      fenugreek
   f  mammals/skunk  mammals/skunk
   $ hg debugwalk -Ibeans mammals
-  matcher: <matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes='(?:beans(?:/|$))'>
+  matcher: <intersectionmatcher m1=<matcher files=['mammals'], patterns='(?:mammals(?:/|$))', includes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans(?:/|$))'>>
   $ hg debugwalk -Inon-existent
   matcher: <matcher files=[], patterns=None, includes='(?:non\\-existent(?:/|$))'>
   $ hg debugwalk -Inon-existent -Ibeans/black
   matcher: <matcher files=[], patterns=None, includes='(?:non\\-existent(?:/|$)|beans\\/black(?:/|$))'>
   f  beans/black  beans/black
   $ hg debugwalk -Ibeans beans/black
-  matcher: <matcher files=['beans/black'], patterns='(?:beans\\/black(?:/|$))', includes='(?:beans(?:/|$))'>
+  matcher: <intersectionmatcher m1=<matcher files=['beans/black'], patterns='(?:beans\\/black(?:/|$))', includes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans(?:/|$))'>>
   f  beans/black  beans/black  exact
   $ hg debugwalk -Ibeans/black beans
-  matcher: <matcher files=['beans'], patterns='(?:beans(?:/|$))', includes='(?:beans\\/black(?:/|$))'>
+  matcher: <intersectionmatcher m1=<matcher files=['beans'], patterns='(?:beans(?:/|$))', includes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))'>>
   f  beans/black  beans/black
   $ hg debugwalk -Xbeans/black beans
   matcher: <differencematcher m1=<matcher files=['beans'], patterns='(?:beans(?:/|$))', includes=None>, m2=<matcher files=[], patterns=None, includes='(?:beans\\/black(?:/|$))'>>