--- a/hgext/obsolete.py Wed Aug 08 11:10:43 2012 +0200
+++ b/hgext/obsolete.py Wed Aug 08 11:54:27 2012 +0200
@@ -67,6 +67,7 @@
# This extension hold the following code
#
# - Extension Helper code
+# - Obsolescence cache
# - ...
# - Older format compat
@@ -305,14 +306,35 @@
### Obsolescence Caching Logic ###
#####################################################################
-### compute cache functions
+# Obsolescence related logic can be very slow if we don't have efficient cache.
+#
+# This section implements a cache mechanism that did not make it into core for
+# time reason. It store meaningful set of revision related to obsolescence
+# (obsolete, unstabletble ...
+#
+# Here is:
+#
+# - Computation of meaningful set,
+# - Cache access logic,
+# - Cache invalidation logic,
+# - revset and ctx using this cache.
+#
-computecache = {}
+
+### Computation of meaningful set
+#
+# Most set can be computed with "simple" revset.
+
+#: { set name -> function to compute this set } mapping
+#: function take a single "repo" argument.
+#:
+#: Use the `cachefor` decorator to register new cache function
+cachefuncs = {}
def cachefor(name):
"""Decorator to register a function as computing the cache for a set"""
def decorator(func):
- assert name not in computecache
- computecache[name] = func
+ assert name not in cachefuncs
+ cachefuncs[name] = func
return func
return decorator
@@ -348,36 +370,63 @@
obsstore.caches = {}
return orig(obsstore, *args, **kwargs)
+### Cache access
+
def getobscache(repo, name):
+ """Return the set of revision that belong to the <name> set
+
+ Such access may compute the set and cache it for future use"""
if not repo.obsstore:
return ()
if name not in repo.obsstore.caches:
- repo.obsstore.caches[name] = computecache[name](repo)
+ repo.obsstore.caches[name] = cachefuncs[name](repo)
return repo.obsstore.caches[name]
+### Cache clean up
+#
+# To be simple we need to invalidate obsolescence cache when:
+#
+# - new changeset is added:
+# - public phase is changed
+# - obsolescence marker are added
+# - strip is used a repo
+
-### cache clean up
def clearobscaches(repo):
- """"""
+ """Remove all obsolescence related cache from a repo
+
+ This remove all cache in obsstore is the obsstore already exist on the
+ repo.
+
+ (We could be smarter here)"""
if 'obsstore' in repo._filecache:
repo.obsstore.caches.clear()
-@eh.wrapfunction(localrepo.localrepository, 'updatebranchcache')
-@eh.wrapfunction(phases, 'advanceboundary')
+@eh.wrapfunction(localrepo.localrepository, 'addchangegroup') # new changeset
+@eh.wrapfunction(phases, 'retractboundary') # phase movement
+@eh.wrapfunction(phases, 'advanceboundary') # phase movement
+@eh.wrapfunction(localrepo.localrepository, 'destroyed') # strip
def wrapclearcache(orig, repo, *args, **kwargs):
try:
return orig(repo, *args, **kwargs)
finally:
+ # we are a bit wide here
+ # we could restrict to:
+ # advanceboundary + phase==public
+ # retractboundary + phase==draft
clearobscaches(repo)
-@eh.wrapfunction(obsolete.obsstore, 'add')
+@eh.wrapfunction(obsolete.obsstore, 'add') # new marker
def clearonadd(orig, obsstore, *args, **kwargs):
try:
return orig(obsstore, *args, **kwargs)
finally:
obsstore.caches.clear()
-### cache user
+### Use the case
+# Function in core that could benefic from the cache are overwritten by cache using version
+
+# changectx method
@eh.addattr(context.changectx, 'unstable')
def unstable(ctx):
@@ -394,6 +443,8 @@
return False
return ctx.rev() in getobscache(ctx._repo, 'extinct')
+# revset
+
@eh.revset('obsolete')
def revsetobsolete(repo, subset, x):
"""``obsolete()``