icasefs: retry directory scan once for already invalidated cache
authorFUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Fri, 16 Dec 2011 21:09:40 +0900
changeset 15709 a1f4bd47d18e
parent 15708 309e49491253
child 15710 f63e40047372
icasefs: retry directory scan once for already invalidated cache some hg operation (e.g.: qpush) create new files after first dirstate.walk()-ing, and it invalidates _fspathcache for fspath(). then, fspath() will fail to look up specified name in _fspathcache. this causes case preservation breaking, because parts of already normcase()-ed path are used as result at that time. in this case, file creation and writing out should be done before fspath() invocation, so the second invocation of os.listdir() has not so much impact on runtime performance.
mercurial/util.py
--- a/mercurial/util.py	Thu Sep 29 17:20:04 2011 +0200
+++ b/mercurial/util.py	Fri Dec 16 21:09:40 2011 +0900
@@ -630,6 +630,13 @@
     if not os.path.lexists(os.path.join(root, name)):
         return None
 
+    def find(p, contents):
+        lenp = len(p)
+        for n in contents:
+            if lenp == len(n) and normcase(n) == p:
+                return n
+        return None
+
     seps = os.sep
     if os.altsep:
         seps = seps + os.altsep
@@ -643,18 +650,19 @@
             result.append(sep)
             continue
 
-        if dir not in _fspathcache:
-            _fspathcache[dir] = os.listdir(dir)
-        contents = _fspathcache[dir]
+        contents = _fspathcache.get(dir, None)
+        if contents is None:
+            contents = os.listdir(dir)
+            _fspathcache[dir] = contents
 
-        lenp = len(part)
-        for n in contents:
-            if lenp == len(n) and normcase(n) == part:
-                result.append(n)
-                break
-        else:
-            # Cannot happen, as the file exists!
-            result.append(part)
+        found = find(part, contents)
+        if not found:
+            # retry once for the corner case: add files after dir walking
+            contents = os.listdir(dir)
+            _fspathcache[dir] = contents
+            found = find(part, contents)
+
+        result.append(found or part)
         dir = os.path.join(dir, part)
 
     return ''.join(result)