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.
--- 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)