--- a/hgext/obsolete.py Tue Aug 07 18:53:26 2012 +0200
+++ b/hgext/obsolete.py Tue Aug 07 21:35:39 2012 +0200
@@ -57,6 +57,7 @@
from mercurial import discovery
from mercurial import error
from mercurial import extensions
+from mercurial import localrepo
from mercurial import phases
from mercurial import revset
from mercurial import scmutil
@@ -304,12 +305,112 @@
### Obsolescence Caching Logic ###
#####################################################################
+### compute cache functions
+
+computecache = {}
+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
+ return func
+ return decorator
+
+@cachefor('obsolete')
+def _computeobsoleteset(repo):
+ """the set of obsolete revision"""
+ obs = set()
+ nm = repo.changelog.nodemap
+ for prec in repo.obsstore.precursors:
+ rev = nm.get(prec)
+ if rev is not None:
+ obs.add(rev)
+ return set(repo.revs('%ld - public()', obs))
+
+@cachefor('unstable')
+def _computeunstableset(repo):
+ """the set of non obsolete revision with obsolete parent"""
+ return set(repo.revs('(obsolete()::) - obsolete()'))
+
+@cachefor('suspended')
+def _computesuspendedset(repo):
+ """the set of obsolete parent with non obsolete descendant"""
+ return set(repo.revs('obsolete() and obsolete()::unstable()'))
+
+@cachefor('extinct')
+def _computeextinctset(repo):
+ """the set of obsolete parent without non obsolete descendant"""
+ return set(repo.revs('obsolete() - obsolete()::unstable()'))
+
+@cachefor('latecomer')
+def _computelatecomerset(repo):
+ """the set of rev trying to obsolete public revision"""
+ query = 'allsuccessors(public()) - obsolete() - public()'
+ return set(repo.revs(query))
+
+@cachefor('conflicting')
+def _computeconflictingset(repo):
+ """the set of rev trying to obsolete public revision"""
+ conflicting = set()
+ obsstore = repo.obsstore
+ newermap = {}
+ for ctx in repo.set('(not public()) - obsolete()'):
+ prec = obsstore.successors.get(ctx.node(), ())
+ toprocess = set(prec)
+ while toprocess:
+ prec = toprocess.pop()[0]
+ if prec not in newermap:
+ newermap[prec] = newerversion(repo, prec)
+ newer = [n for n in newermap[prec] if n] # filter kill
+ if len(newer) > 1:
+ conflicting.add(ctx.rev())
+ break
+ toprocess.update(obsstore.successors.get(prec, ()))
+ return conflicting
+
+@eh.wrapfunction(obsolete.obsstore, '__init__')
+def _initobsstorecache(orig, obsstore, *args, **kwargs):
+ """add a caches attributes to obsstore"""
+ obsstore.caches = {}
+ return orig(obsstore, *args, **kwargs)
+
+def getobscache(repo, name):
+ if not repo.obsstore:
+ return ()
+ if name not in repo.obsstore.caches:
+ repo.obsstore.caches[name] = computecache[name](repo)
+ return repo.obsstore.caches[name]
+
+
+### cache clean up
+def clearobscaches(repo):
+ #if 'obsstore' in vars(repo):
+ # should work great but cache invalidation act strange
+ repo.obsstore.caches.clear()
+
+@eh.wrapfunction(localrepo.localrepository, 'updatebranchcache')
+@eh.wrapfunction(phases, 'advanceboundary')
+def wrapclearcache(orig, repo, *args, **kwargs):
+ try:
+ return orig(repo, *args, **kwargs)
+ finally:
+ clearobscaches(repo)
+
+@eh.wrapfunction(obsolete.obsstore, 'add')
+def clearonadd(orig, obsstore, *args, **kwargs):
+ try:
+ return orig(obsstore, *args, **kwargs)
+ finally:
+ obsstore.caches.clear()
+
+### cache user
+
@eh.addattr(context.changectx, 'unstable')
def unstable(ctx):
"""is the changeset unstable (have obsolete ancestor)"""
if ctx.node() is None:
return False
- return ctx.rev() in ctx._repo._unstableset
+ return ctx.rev() in getobscache(ctx._repo, 'unstable')
@eh.addattr(context.changectx, 'extinct')
@@ -317,7 +418,7 @@
"""is the changeset extinct by other"""
if ctx.node() is None:
return False
- return ctx.rev() in ctx._repo._extinctset
+ return ctx.rev() in getobscache(ctx._repo, 'extinct')
@eh.revset('obsolete')
def revsetobsolete(repo, subset, x):
@@ -325,7 +426,8 @@
Changeset is obsolete.
"""
args = revset.getargs(x, 0, 0, 'obsolete takes no argument')
- return [r for r in subset if r in repo._obsoleteset and repo._phasecache.phase(repo, r) > 0]
+ obsoletes = getobscache(repo, 'obsolete')
+ return [r for r in subset if r in obsoletes]
@eh.revset('unstable')
def revsetunstable(repo, subset, x):
@@ -333,7 +435,8 @@
Unstable changesets are non-obsolete with obsolete ancestors.
"""
args = revset.getargs(x, 0, 0, 'unstable takes no arguments')
- return [r for r in subset if r in repo._unstableset]
+ unstables = getobscache(repo, 'unstable')
+ return [r for r in subset if r in unstables]
@eh.revset('extinct')
def revsetextinct(repo, subset, x):
@@ -341,104 +444,8 @@
Obsolete changesets with obsolete descendants only.
"""
args = revset.getargs(x, 0, 0, 'extinct takes no arguments')
- return [r for r in subset if r in repo._extinctset]
-
-
-@eh.wrapfunction(phases, 'advanceboundary')
-def wrapclearcache(orig, repo, *args, **kwargs):
- try:
- return orig(repo, *args, **kwargs)
- finally:
- repo._clearobsoletecache()
-
-@eh.reposetup
-def _repocachesetup(ui, repo):
- if not repo.local():
- return
-
- o_updatebranchcache = repo.updatebranchcache
- class cachedobsolescencegrepo(repo.__class__):
-
- # XXX move me on obssotre
- @util.propertycache
- def _obsoleteset(self):
- """the set of obsolete revision"""
- obs = set()
- nm = self.changelog.nodemap
- for prec in self.obsstore.precursors:
- rev = nm.get(prec)
- if rev is not None:
- obs.add(rev)
- return obs
-
- # XXX move me on obssotre
- @util.propertycache
- def _unstableset(self):
- """the set of non obsolete revision with obsolete parent"""
- return set(self.revs('(obsolete()::) - obsolete()'))
-
- # XXX move me on obssotre
- @util.propertycache
- def _suspendedset(self):
- """the set of obsolete parent with non obsolete descendant"""
- return set(self.revs('obsolete() and obsolete()::unstable()'))
-
- # XXX move me on obssotre
- @util.propertycache
- def _extinctset(self):
- """the set of obsolete parent without non obsolete descendant"""
- return set(self.revs('obsolete() - obsolete()::unstable()'))
-
- # XXX move me on obssotre
- @util.propertycache
- def _latecomerset(self):
- """the set of rev trying to obsolete public revision"""
- query = 'allsuccessors(public()) - obsolete() - public()'
- return set(self.revs(query))
-
- # XXX move me on obssotre
- @util.propertycache
- def _conflictingset(self):
- """the set of rev trying to obsolete public revision"""
- conflicting = set()
- obsstore = self.obsstore
- newermap = {}
- for ctx in self.set('(not public()) - obsolete()'):
- prec = obsstore.successors.get(ctx.node(), ())
- toprocess = set(prec)
- while toprocess:
- prec = toprocess.pop()[0]
- if prec not in newermap:
- newermap[prec] = newerversion(self, prec)
- newer = [n for n in newermap[prec] if n] # filter kill
- if len(newer) > 1:
- conflicting.add(ctx.rev())
- break
- toprocess.update(obsstore.successors.get(prec, ()))
- return conflicting
-
- def _clearobsoletecache(self):
- if '_obsoleteset' in vars(self):
- del self._obsoleteset
- self._clearunstablecache()
-
- def updatebranchcache(self):
- o_updatebranchcache()
- self._clearunstablecache()
-
- def _clearunstablecache(self):
- if '_unstableset' in vars(self):
- del self._unstableset
- if '_suspendedset' in vars(self):
- del self._suspendedset
- if '_extinctset' in vars(self):
- del self._extinctset
- if '_latecomerset' in vars(self):
- del self._latecomerset
- if '_conflictingset' in vars(self):
- del self._conflictingset
-
- repo.__class__ = cachedobsolescencegrepo
+ extincts = getobscache(repo, 'extinct')
+ return [r for r in subset if r in extincts]
#####################################################################
### Complete troubles computation logic ###
@@ -449,14 +456,14 @@
"""is the changeset latecomer (Try to succeed to public change)"""
if ctx.node() is None:
return False
- return ctx.rev() in ctx._repo._latecomerset
+ return ctx.rev() in getobscache(ctx._repo, 'latecomer')
@eh.addattr(context.changectx, 'conflicting')
def conflicting(ctx):
"""is the changeset conflicting (Try to succeed to public change)"""
if ctx.node() is None:
return False
- return ctx.rev() in ctx._repo._conflictingset
+ return ctx.rev() in getobscache(ctx._repo, 'conflicting')
#####################################################################
@@ -593,7 +600,7 @@
if nprec in nsucs:
raise util.Abort("Changeset %s cannot obsolete himself" % prec)
repo.obsstore.create(tr, nprec, nsucs, flag, metadata)
- repo._clearobsoletecache()
+ clearobscaches(repo)
tr.close()
finally:
tr.release()
@@ -618,7 +625,8 @@
Obsolete changesets with non-obsolete descendants.
"""
args = revset.getargs(x, 0, 0, 'suspended takes no arguments')
- return [r for r in subset if r in repo._suspendedset]
+ suspended = getobscache(repo, 'suspended')
+ return [r for r in subset if r in suspended]
@eh.revset('latecomer')
def revsetlatecomer(repo, subset, x):
@@ -626,7 +634,8 @@
Changesets marked as successors of public changesets.
"""
args = revset.getargs(x, 0, 0, 'latecomer takes no arguments')
- return [r for r in subset if r in repo._latecomerset]
+ lates = getobscache(repo, 'latecomer')
+ return [r for r in subset if r in lates]
@eh.revset('conflicting')
def revsetconflicting(repo, subset, x):
@@ -634,7 +643,8 @@
Changesets marked as successors of a same changeset.
"""
args = revset.getargs(x, 0, 0, 'conflicting takes no arguments')
- return [r for r in subset if r in repo._conflictingset]
+ conf = getobscache(repo, 'conflicting')
+ return [r for r in subset if r in conf]
@eh.revset('precursors')
@@ -683,11 +693,12 @@
``stable``, ``unstable``, ``suspended`` or ``extinct``.
"""
rev = ctx.rev()
- if rev in repo._extinctset:
- return 'extinct'
- if rev in repo._suspendedset:
- return 'suspended'
- if rev in repo._unstableset:
+ if ctx.obsolete():
+ if ctx.extinct():
+ return 'extinct'
+ else:
+ return 'suspended'
+ elif ctx.unstable():
return 'unstable'
return 'stable'
@@ -798,7 +809,7 @@
}
repo.obsstore.create(tr, oldnode, [new], 0, meta)
tr.close()
- repo._clearobsoletecache()
+ clearobscaches(repo)
finally:
tr.release()
finally: