Patchwork [3,of,4] dirstate: split the foldmap into separate ones for files and directories

login
register
mail settings
Submitter Siddharth Agarwal
Date March 31, 2015, 4:52 p.m.
Message ID <cf5e06980f6cd6591497.1427820778@devbig136.prn2.facebook.com>
Download mbox | patch
Permalink /patch/8385/
State Accepted
Commit 25c1d3ca5ff666229ecff794f317c79943c8571b
Headers show

Comments

Siddharth Agarwal - March 31, 2015, 4:52 p.m.
# HG changeset patch
# User Siddharth Agarwal <sid0@fb.com>
# Date 1427683369 25200
#      Sun Mar 29 19:42:49 2015 -0700
# Node ID cf5e06980f6cd6591497b82cb6dbfb5f52f99f00
# Parent  21df72baa78f0ba8c6f7d98f4159c48b4dffddf3
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.

Patch

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -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):