Patchwork D7931: rust-status: use bare hg status fastpath from Python

login
register
mail settings
Submitter phabricator
Date Jan. 17, 2020, 4:54 p.m.
Message ID <differential-rev-PHID-DREV-4xlgt3px7oypflmh2v4o-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/44495/
State New
Headers show

Comments

phabricator - Jan. 17, 2020, 4:54 p.m.
Alphare created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  This change also adds a test case for subrepos.
  Repeating the benchmark information from the `hg-core` commit:
  
  On the Netbeans repository:
  C: 840ms
  Rust+C: 556ms
  
  Mozilla Central with the one pattern that causes a fallback removed:
  C: 2.315s
  Rust+C: 1.700s

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D7931

AFFECTED FILES
  mercurial/dirstate.py
  mercurial/match.py
  tests/test-subrepo-deep-nested-change.t

CHANGE DETAILS




To: Alphare, #hg-reviewers
Cc: mercurial-devel
phabricator - Jan. 23, 2020, 6:48 p.m.
This revision now requires changes to proceed.
marmoute added a comment.
marmoute requested changes to this revision.


  hooo, I am a bot

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7931/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7931

To: Alphare, #hg-reviewers, marmoute
Cc: marmoute, mercurial-devel

Patch

diff --git a/tests/test-subrepo-deep-nested-change.t b/tests/test-subrepo-deep-nested-change.t
--- a/tests/test-subrepo-deep-nested-change.t
+++ b/tests/test-subrepo-deep-nested-change.t
@@ -355,6 +355,11 @@ 
   R sub1/sub2/folder/test.txt
   ! sub1/.hgsub
   ? sub1/x.hgsub
+  $ hg status -R sub1
+  warning: subrepo spec file 'sub1/.hgsub' not found
+  R .hgsubstate
+  ! .hgsub
+  ? x.hgsub
   $ mv sub1/x.hgsub sub1/.hgsub
   $ hg update -Cq
   $ touch sub1/foo
diff --git a/mercurial/match.py b/mercurial/match.py
--- a/mercurial/match.py
+++ b/mercurial/match.py
@@ -45,6 +45,7 @@ 
 
 propertycache = util.propertycache
 
+rustmod = policy.importrust('dirstate')
 
 def _rematcher(regex):
     '''compile the regexp with the best available regexp engine and return a
@@ -666,7 +667,10 @@ 
 class includematcher(basematcher):
     def __init__(self, root, kindpats, badfn=None):
         super(includematcher, self).__init__(badfn)
-
+        if rustmod is not None:
+            # We need to pass the patterns to Rust because they can contain
+            # patterns from the user interface
+            self._kindpats = kindpats
         self._pats, self.matchfn = _buildmatch(kindpats, b'(?:/|$)', root)
         self._prefix = _prefix(kindpats)
         roots, dirs, parents = _rootsdirsandparents(kindpats)
diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -27,6 +27,7 @@ 
     policy,
     pycompat,
     scmutil,
+    sparse,
     txnutil,
     util,
 )
@@ -1083,7 +1084,7 @@ 
                     results[next(iv)] = st
         return results
 
-    def _rust_status(self, matcher, list_clean):
+    def _rust_status(self, matcher, list_clean, list_ignored, list_unknown):
         # Force Rayon (Rust parallelism library) to respect the number of
         # workers. This is a temporary workaround until Rust code knows
         # how to read the config file.
@@ -1101,16 +1102,45 @@ 
             added,
             removed,
             deleted,
+            clean,
+            ignored,
             unknown,
-            clean,
+            warnings,
+            bad,
         ) = rustmod.status(
             self._map._rustmap,
             matcher,
             self._rootdir,
-            bool(list_clean),
+            self._ignorefiles(),
+            self._checkexec,
             self._lastnormaltime,
-            self._checkexec,
+            bool(list_clean),
+            bool(list_ignored),
+            bool(list_unknown),
         )
+        if self._ui.warn:
+            for item in warnings:
+                if isinstance(item, tuple):
+                    file_path, syntax = item
+                    msg = _(b"%s: ignoring invalid syntax '%s'\n") % (
+                        file_path,
+                        syntax,
+                    )
+                    self._ui.warn(msg)
+                else:
+                    msg = _(b"skipping unreadable pattern file '%s': %s\n")
+                    self._ui.warn(
+                        msg
+                        % (
+                            pathutil.canonpath(
+                                self._rootdir, self._rootdir, item
+                            ),
+                            b"No such file or directory",
+                        )
+                    )
+
+        for (fn, message) in bad:
+            matcher.bad(fn, encoding.strtolocal(message))
 
         status = scmutil.status(
             modified=modified,
@@ -1118,7 +1148,7 @@ 
             removed=removed,
             deleted=deleted,
             unknown=unknown,
-            ignored=[],
+            ignored=ignored,
             clean=clean,
         )
         return (lookup, status)
@@ -1148,26 +1178,32 @@ 
 
         use_rust = True
 
-        allowed_matchers = (matchmod.alwaysmatcher, matchmod.exactmatcher)
+        allowed_matchers = (
+            matchmod.alwaysmatcher,
+            matchmod.exactmatcher,
+            matchmod.includematcher,
+        )
 
         if rustmod is None:
             use_rust = False
         elif subrepos:
             use_rust = False
-        elif bool(listunknown):
-            # Pathauditor does not exist yet in Rust, unknown files
-            # can't be trusted.
+        elif sparse.enabled:
             use_rust = False
-        elif self._ignorefiles() and listignored:
-            # Rust has no ignore mechanism yet, so don't use Rust for
-            # commands that need ignore.
+        elif match.traversedir is not None:
             use_rust = False
         elif not isinstance(match, allowed_matchers):
             # Matchers have yet to be implemented
             use_rust = False
 
         if use_rust:
-            return self._rust_status(match, listclean)
+            try:
+                return self._rust_status(
+                    match, listclean, listignored, listunknown
+                )
+            except rustmod.FallbackError as e:
+                msg = b"Rust status fallback: %s"
+                self._ui.debug(msg % encoding.strtolocal(e))
 
         def noop(f):
             pass
@@ -1249,13 +1285,10 @@ 
                 aadd(fn)
             elif state == b'r':
                 radd(fn)
-
-        return (
-            lookup,
-            scmutil.status(
-                modified, added, removed, deleted, unknown, ignored, clean
-            ),
+        status = scmutil.status(
+            modified, added, removed, deleted, unknown, ignored, clean
         )
+        return (lookup, status)
 
     def matches(self, match):
         '''