diff -r 9d47474d2582 -r fa85e7205e0b hgext/obsolete.py --- 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: