--- a/mercurial/scmutil.py Sat Nov 16 13:24:26 2013 -0800
+++ b/mercurial/scmutil.py Sat Nov 16 13:29:39 2013 -0800
@@ -786,23 +786,26 @@
entry.refresh()
class filecache(object):
- '''A property like decorator that tracks a file under .hg/ for updates.
+ '''A property like decorator that tracks files under .hg/ for updates.
Records stat info when called in _filecache.
- On subsequent calls, compares old stat info with new info, and recreates
- the object when needed, updating the new stat info in _filecache.
+ On subsequent calls, compares old stat info with new info, and recreates the
+ object when any of the files changes, updating the new stat info in
+ _filecache.
Mercurial either atomic renames or appends for files under .hg,
so to ensure the cache is reliable we need the filesystem to be able
to tell us if a file has been replaced. If it can't, we fallback to
recreating the object on every call (essentially the same behaviour as
- propertycache).'''
- def __init__(self, path):
- self.path = path
+ propertycache).
+
+ '''
+ def __init__(self, *paths):
+ self.paths = paths
def join(self, obj, fname):
- """Used to compute the runtime path of the cached file.
+ """Used to compute the runtime path of a cached file.
Users should subclass filecache and provide their own version of this
function to call the appropriate join function on 'obj' (an instance
@@ -827,11 +830,11 @@
if entry.changed():
entry.obj = self.func(obj)
else:
- path = self.join(obj, self.path)
+ paths = [self.join(obj, path) for path in self.paths]
# We stat -before- creating the object so our cache doesn't lie if
# a writer modified between the time we read and stat
- entry = filecachesubentry(path, True)
+ entry = filecacheentry(paths, True)
entry.obj = self.func(obj)
obj._filecache[self.name] = entry
@@ -843,7 +846,8 @@
if self.name not in obj._filecache:
# we add an entry for the missing value because X in __dict__
# implies X in _filecache
- ce = filecachesubentry(self.join(obj, self.path), False)
+ paths = [self.join(obj, path) for path in self.paths]
+ ce = filecacheentry(paths, False)
obj._filecache[self.name] = ce
else:
ce = obj._filecache[self.name]
--- a/tests/test-filecache.py Sat Nov 16 13:24:26 2013 -0800
+++ b/tests/test-filecache.py Sat Nov 16 13:29:39 2013 -0800
@@ -18,7 +18,7 @@
def sjoin(self, p):
return p
- @filecache('x')
+ @filecache('x', 'y')
def cached(self):
print 'creating'
return 'string from function'
@@ -31,12 +31,12 @@
pass
def basic(repo):
- print "* file doesn't exist"
+ print "* neither file exists"
# calls function
repo.cached
repo.invalidate()
- print "* file still doesn't exist"
+ print "* neither file still exists"
# uses cache
repo.cached
@@ -57,7 +57,7 @@
repo.cached
repo.invalidate()
- print "* nothing changed with file x"
+ print "* nothing changed with either file"
# stats file again, reuses object
repo.cached
@@ -72,6 +72,41 @@
print "* file x changed inode"
repo.cached
+ # create empty file y
+ f = open('y', 'w')
+ f.close()
+ repo.invalidate()
+ print "* empty file y created"
+ # should recreate the object
+ repo.cached
+
+ f = open('y', 'w')
+ f.write('A')
+ f.close()
+ repo.invalidate()
+ print "* file y changed size"
+ # should recreate the object
+ repo.cached
+
+ f = scmutil.opener('.')('y', 'w', atomictemp=True)
+ f.write('B')
+ f.close()
+
+ repo.invalidate()
+ print "* file y changed inode"
+ repo.cached
+
+ f = scmutil.opener('.')('x', 'w', atomictemp=True)
+ f.write('c')
+ f.close()
+ f = scmutil.opener('.')('y', 'w', atomictemp=True)
+ f.write('C')
+ f.close()
+
+ repo.invalidate()
+ print "* both files changed inode"
+ repo.cached
+
def fakeuncacheable():
def wrapcacheable(orig, *args, **kwargs):
return False
@@ -83,10 +118,11 @@
origcacheable = extensions.wrapfunction(util.cachestat, 'cacheable',
wrapcacheable)
- try:
- os.remove('x')
- except OSError:
- pass
+ for fn in ['x', 'y']:
+ try:
+ os.remove(fn)
+ except OSError:
+ pass
basic(fakerepo())
@@ -110,9 +146,10 @@
def setbeforeget(repo):
os.remove('x')
+ os.remove('y')
repo.cached = 'string set externally'
repo.invalidate()
- print "* file x doesn't exist"
+ print "* neither file exists"
print repo.cached
repo.invalidate()
f = open('x', 'w')
@@ -121,6 +158,18 @@
print "* file x created"
print repo.cached
+ repo.cached = 'string 2 set externally'
+ repo.invalidate()
+ print "* string set externally again"
+ print repo.cached
+
+ repo.invalidate()
+ f = open('y', 'w')
+ f.write('b')
+ f.close()
+ print "* file y created"
+ print repo.cached
+
print 'basic:'
print
basic(fakerepo())
--- a/tests/test-filecache.py.out Sat Nov 16 13:24:26 2013 -0800
+++ b/tests/test-filecache.py.out Sat Nov 16 13:29:39 2013 -0800
@@ -1,30 +1,46 @@
basic:
-* file doesn't exist
+* neither file exists
creating
-* file still doesn't exist
+* neither file still exists
* empty file x created
creating
* file x changed size
creating
-* nothing changed with file x
+* nothing changed with either file
* file x changed inode
creating
+* empty file y created
+creating
+* file y changed size
+creating
+* file y changed inode
+creating
+* both files changed inode
+creating
fakeuncacheable:
-* file doesn't exist
+* neither file exists
creating
-* file still doesn't exist
+* neither file still exists
creating
* empty file x created
creating
* file x changed size
creating
-* nothing changed with file x
+* nothing changed with either file
creating
* file x changed inode
creating
+* empty file y created
+creating
+* file y changed size
+creating
+* file y changed inode
+creating
+* both files changed inode
+creating
repository tip rolled back to revision -1 (undo commit)
working directory now based on revision -1
repository tip rolled back to revision -1 (undo commit)
@@ -32,8 +48,13 @@
setbeforeget:
-* file x doesn't exist
+* neither file exists
string set externally
* file x created
creating
string from function
+* string set externally again
+string 2 set externally
+* file y created
+creating
+string from function