dirstate: split the foldmap into separate ones for files and directories
authorSiddharth Agarwal <sid0@fb.com>
Sun, 29 Mar 2015 19:42:49 -0700
changeset 24540 25c1d3ca5ff6
parent 24539 3a8eba78803e
child 24541 e235b5dc5cf9
dirstate: split the foldmap into separate ones for files and directories Computing the set of directories in the dirstate can be pretty expensive. For 'hg status' without arguments, it turns out we actually never need to figure out the right case for directories in the foldmap. (An upcoming patch explains why.) This patch splits up the directory and file maps into separate ones, allowing for the subsequent optimization in status.
mercurial/dirstate.py
--- a/mercurial/dirstate.py	Sat Mar 28 18:53:54 2015 -0700
+++ b/mercurial/dirstate.py	Sun Mar 29 19:42:49 2015 -0700
@@ -87,15 +87,21 @@
         return self._copymap
 
     @propertycache
-    def _foldmap(self):
+    def _filefoldmap(self):
         f = {}
         normcase = util.normcase
         for name, s in self._map.iteritems():
             if s[0] != 'r':
                 f[normcase(name)] = name
+        f['.'] = '.' # prevents useless util.fspath() invocation
+        return f
+
+    @propertycache
+    def _dirfoldmap(self):
+        f = {}
+        normcase = util.normcase
         for name in self._dirs:
             f[normcase(name)] = name
-        f['.'] = '.' # prevents useless util.fspath() invocation
         return f
 
     @repocache('branch')
@@ -332,8 +338,8 @@
             self._pl = p
 
     def invalidate(self):
-        for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
-                "_ignore"):
+        for a in ("_map", "_copymap", "_filefoldmap", "_dirfoldmap", "_branch",
+                  "_pl", "_dirs", "_ignore"):
             if a in self.__dict__:
                 delattr(self, a)
         self._lastnormaltime = 0
@@ -492,24 +498,27 @@
 
     def _normalizefile(self, path, isknown, ignoremissing=False, exists=None):
         normed = util.normcase(path)
-        folded = self._foldmap.get(normed, None)
+        folded = self._filefoldmap.get(normed, None)
         if folded is None:
             if isknown:
                 folded = path
             else:
                 folded = self._discoverpath(path, normed, ignoremissing, exists,
-                                            self._foldmap)
+                                            self._filefoldmap)
         return folded
 
     def _normalize(self, path, isknown, ignoremissing=False, exists=None):
         normed = util.normcase(path)
-        folded = self._foldmap.get(normed, None)
+        folded = self._filefoldmap.get(normed,
+                                       self._dirfoldmap.get(normed, None))
         if folded is None:
             if isknown:
                 folded = path
             else:
+                # store discovered result in dirfoldmap so that future
+                # normalizefile calls don't start matching directories
                 folded = self._discoverpath(path, normed, ignoremissing, exists,
-                                            self._foldmap)
+                                            self._dirfoldmap)
         return folded
 
     def normalize(self, path, isknown=False, ignoremissing=False):