D1345: dirstate: add explicit methods for querying directories

mbthomas (Mark Thomas) phabricator at mercurial-scm.org
Wed Nov 8 12:31:36 EST 2017


mbthomas created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The set-like object returned by dirstate.dirs may be difficult for other
  implementations of the dirstate to provide, and is unnecessary as it is
  only ever used for __contains__.  Instead, provide an explicit method for
  testing for a directory.

REPOSITORY
  rHG Mercurial

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

AFFECTED FILES
  contrib/perf.py
  hgext/largefiles/reposetup.py
  mercurial/dirstate.py

CHANGE DETAILS

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -157,8 +157,8 @@
     def _pl(self):
         return self._map.parents()
 
-    def dirs(self):
-        return self._map.dirs
+    def hasdir(self, d):
+        return self._map.hastrackeddir(d)
 
     @rootcache('.hgignore')
     def _ignore(self):
@@ -390,11 +390,11 @@
         oldstate = self[f]
         if state == 'a' or oldstate == 'r':
             scmutil.checkfilename(f)
-            if f in self._map.dirs:
+            if self._map.hastrackeddir(f):
                 raise error.Abort(_('directory %r already in dirstate') % f)
             # shadows
             for d in util.finddirs(f):
-                if d in self._map.dirs:
+                if self._map.hastrackeddir(d):
                     break
                 entry = self._map.get(d)
                 if entry is not None and entry[0] != 'r':
@@ -770,7 +770,6 @@
         results = dict.fromkeys(subrepos)
         results['.hg'] = None
 
-        alldirs = None
         for ff in files:
             # constructing the foldmap is expensive, so don't do it for the
             # common case where files is ['.']
@@ -801,9 +800,7 @@
                 if nf in dmap: # does it exactly match a missing file?
                     results[nf] = None
                 else: # does it match a missing directory?
-                    if alldirs is None:
-                        alldirs = util.dirs(dmap._map)
-                    if nf in alldirs:
+                    if self._map.hasdir(nf):
                         if matchedir:
                             matchedir(nf)
                         notfoundadd(nf)
@@ -1199,7 +1196,8 @@
         self._map.clear()
         self.copymap.clear()
         self.setparents(nullid, nullid)
-        util.clearcachedproperty(self, "dirs")
+        util.clearcachedproperty(self, "_dirs")
+        util.clearcachedproperty(self, "_alldirs")
         util.clearcachedproperty(self, "filefoldmap")
         util.clearcachedproperty(self, "dirfoldmap")
         util.clearcachedproperty(self, "nonnormalset")
@@ -1232,8 +1230,10 @@
 
     def addfile(self, f, oldstate, state, mode, size, mtime):
         """Add a tracked file to the dirstate."""
-        if oldstate in "?r" and "dirs" in self.__dict__:
-            self.dirs.addpath(f)
+        if oldstate in "?r" and "_dirs" in self.__dict__:
+            self._dirs.addpath(f)
+        if oldstate == "?" and "_alldirs" in self.__dict__:
+            self._alldirs.addpath(f)
         self._map[f] = dirstatetuple(state, mode, size, mtime)
         if state != 'n' or mtime == -1:
             self.nonnormalset.add(f)
@@ -1248,8 +1248,10 @@
         the file's previous state.  In the future, we should refactor this
         to be more explicit about what that state is.
         """
-        if oldstate not in "?r" and "dirs" in self.__dict__:
-            self.dirs.delpath(f)
+        if oldstate not in "?r" and "_dirs" in self.__dict__:
+            self._dirs.delpath(f)
+        if oldstate == "?" and "_alldirs" in self.__dict__:
+            self._alldirs.addpath(f)
         if "filefoldmap" in self.__dict__:
             normed = util.normcase(f)
             self.filefoldmap.pop(normed, None)
@@ -1263,8 +1265,10 @@
         """
         exists = f in self._map
         if exists:
-            if oldstate != "r" and "dirs" in self.__dict__:
-                self.dirs.delpath(f)
+            if oldstate != "r" and "_dirs" in self.__dict__:
+                self._dirs.delpath(f)
+            if "_alldirs" in self.__dict__:
+                self._alldirs.delpath(f)
             del self._map[f]
         if "filefoldmap" in self.__dict__:
             normed = util.normcase(f)
@@ -1314,13 +1318,28 @@
         f['.'] = '.' # prevents useless util.fspath() invocation
         return f
 
+    def hastrackeddir(self, d):
+        """
+        Returns True if the dirstate contains a tracked (not removed) file
+        in this directory.
+        """
+        return d in self._dirs
+
+    def hasdir(self, d):
+        """
+        Returns True if the dirstate contains a file (tracked or removed)
+        in this directory.
+        """
+        return d in self._alldirs
+
     @propertycache
-    def dirs(self):
-        """Returns a set-like object containing all the directories in the
-        current dirstate.
-        """
+    def _dirs(self):
         return util.dirs(self._map, 'r')
 
+    @propertycache
+    def _alldirs(self):
+        return util.dirs(self._map)
+
     def _opendirstatefile(self):
         fp, mode = txnutil.trypending(self._root, self._opener, self._filename)
         if self._pendingmode is not None and self._pendingmode != mode:
@@ -1438,6 +1457,6 @@
     def dirfoldmap(self):
         f = {}
         normcase = util.normcase
-        for name in self.dirs:
+        for name in self._dirs:
             f[normcase(name)] = name
         return f
diff --git a/hgext/largefiles/reposetup.py b/hgext/largefiles/reposetup.py
--- a/hgext/largefiles/reposetup.py
+++ b/hgext/largefiles/reposetup.py
@@ -138,7 +138,7 @@
                         sf = lfutil.standin(f)
                         if sf in dirstate:
                             newfiles.append(sf)
-                        elif sf in dirstate.dirs():
+                        elif dirstate.hasdir(sf):
                             # Directory entries could be regular or
                             # standin, check both
                             newfiles.extend((f, sf))
@@ -156,7 +156,7 @@
                     def sfindirstate(f):
                         sf = lfutil.standin(f)
                         dirstate = self.dirstate
-                        return sf in dirstate or sf in dirstate.dirs()
+                        return sf in dirstate or dirstate.hasdir(sf)
 
                     match._files = [f for f in match._files
                                     if sfindirstate(f)]
diff --git a/contrib/perf.py b/contrib/perf.py
--- a/contrib/perf.py
+++ b/contrib/perf.py
@@ -525,8 +525,8 @@
     dirstate = repo.dirstate
     'a' in dirstate
     def d():
-        dirstate.dirs()
-        del dirstate._map.dirs
+        dirstate.hasdir('a')
+        del dirstate._map._dirs
     timer(d)
     fm.end()
 
@@ -545,8 +545,8 @@
     timer, fm = gettimer(ui, opts)
     "a" in repo.dirstate
     def d():
-        "a" in repo.dirstate._map.dirs
-        del repo.dirstate._map.dirs
+        repo.dirstate.hasdir("a")
+        del repo.dirstate._map._dirs
     timer(d)
     fm.end()
 
@@ -569,7 +569,7 @@
     def d():
         dirstate._map.dirfoldmap.get('a')
         del dirstate._map.dirfoldmap
-        del dirstate._map.dirs
+        del dirstate._map._dirs
     timer(d)
     fm.end()
 



To: mbthomas, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list