hgext/obsolete.py
changeset 450 4d18739b506b
parent 449 4f23f224afb4
child 451 dc1bd3595075
equal deleted inserted replaced
449:4f23f224afb4 450:4d18739b506b
    65 from mercurial.node import bin, short, nullid
    65 from mercurial.node import bin, short, nullid
    66 
    66 
    67 # This extension hold the following code
    67 # This extension hold the following code
    68 #
    68 #
    69 # - Extension Helper code
    69 # - Extension Helper code
       
    70 # - Obsolescence cache
    70 # - ...
    71 # - ...
    71 # - Older format compat
    72 # - Older format compat
    72 
    73 
    73 
    74 
    74 
    75 
   303 
   304 
   304 #####################################################################
   305 #####################################################################
   305 ### Obsolescence Caching Logic                                    ###
   306 ### Obsolescence Caching Logic                                    ###
   306 #####################################################################
   307 #####################################################################
   307 
   308 
   308 ### compute cache functions
   309 # Obsolescence related logic can be very slow if we don't have efficient cache.
   309 
   310 #
   310 computecache = {}
   311 # This section implements a cache mechanism that did not make it into core for
       
   312 # time reason. It store meaningful set of revision related to obsolescence
       
   313 # (obsolete, unstabletble ...
       
   314 #
       
   315 # Here is:
       
   316 #
       
   317 # - Computation of meaningful set,
       
   318 # - Cache access logic,
       
   319 # - Cache invalidation logic,
       
   320 # - revset and ctx using this cache.
       
   321 #
       
   322 
       
   323 
       
   324 ### Computation of meaningful set
       
   325 #
       
   326 # Most set can be computed with "simple" revset.
       
   327 
       
   328 #: { set name -> function to compute this set } mapping
       
   329 #:   function take a single "repo" argument.
       
   330 #:
       
   331 #: Use the `cachefor` decorator to register new cache function
       
   332 cachefuncs = {}
   311 def cachefor(name):
   333 def cachefor(name):
   312     """Decorator to register a function as computing the cache for a set"""
   334     """Decorator to register a function as computing the cache for a set"""
   313     def decorator(func):
   335     def decorator(func):
   314         assert name not in computecache
   336         assert name not in cachefuncs
   315         computecache[name] = func
   337         cachefuncs[name] = func
   316         return func
   338         return func
   317     return decorator
   339     return decorator
   318 
   340 
   319 @cachefor('obsolete')
   341 @cachefor('obsolete')
   320 def _computeobsoleteset(repo):
   342 def _computeobsoleteset(repo):
   346 def _initobsstorecache(orig, obsstore, *args, **kwargs):
   368 def _initobsstorecache(orig, obsstore, *args, **kwargs):
   347     """add a caches attributes to obsstore"""
   369     """add a caches attributes to obsstore"""
   348     obsstore.caches = {}
   370     obsstore.caches = {}
   349     return orig(obsstore, *args, **kwargs)
   371     return orig(obsstore, *args, **kwargs)
   350 
   372 
       
   373 ### Cache access
       
   374 
   351 def getobscache(repo, name):
   375 def getobscache(repo, name):
       
   376     """Return the set of revision that belong to the <name> set
       
   377 
       
   378     Such access may compute the set and cache it for future use"""
   352     if not repo.obsstore:
   379     if not repo.obsstore:
   353         return ()
   380         return ()
   354     if name not in repo.obsstore.caches:
   381     if name not in repo.obsstore.caches:
   355         repo.obsstore.caches[name] = computecache[name](repo)
   382         repo.obsstore.caches[name] = cachefuncs[name](repo)
   356     return repo.obsstore.caches[name]
   383     return repo.obsstore.caches[name]
   357 
   384 
   358 
   385 ### Cache clean up
   359 ### cache clean up
   386 #
       
   387 # To be simple we need to invalidate obsolescence cache when:
       
   388 #
       
   389 # - new changeset is added:
       
   390 # - public phase is changed
       
   391 # - obsolescence marker are added
       
   392 # - strip is used a repo
       
   393 
       
   394 
   360 def clearobscaches(repo):
   395 def clearobscaches(repo):
   361     """"""
   396     """Remove all obsolescence related cache from a repo
       
   397 
       
   398     This remove all cache in obsstore is the obsstore already exist on the
       
   399     repo.
       
   400 
       
   401     (We could be smarter here)"""
   362     if 'obsstore' in repo._filecache:
   402     if 'obsstore' in repo._filecache:
   363         repo.obsstore.caches.clear()
   403         repo.obsstore.caches.clear()
   364 
   404 
   365 @eh.wrapfunction(localrepo.localrepository, 'updatebranchcache')
   405 @eh.wrapfunction(localrepo.localrepository, 'addchangegroup')  # new changeset
   366 @eh.wrapfunction(phases, 'advanceboundary')
   406 @eh.wrapfunction(phases, 'retractboundary')  # phase movement
       
   407 @eh.wrapfunction(phases, 'advanceboundary')  # phase movement
       
   408 @eh.wrapfunction(localrepo.localrepository, 'destroyed')  # strip
   367 def wrapclearcache(orig, repo, *args, **kwargs):
   409 def wrapclearcache(orig, repo, *args, **kwargs):
   368     try:
   410     try:
   369         return orig(repo, *args, **kwargs)
   411         return orig(repo, *args, **kwargs)
   370     finally:
   412     finally:
       
   413         # we are a bit wide here
       
   414         # we could restrict to:
       
   415         # advanceboundary + phase==public
       
   416         # retractboundary + phase==draft
   371         clearobscaches(repo)
   417         clearobscaches(repo)
   372 
   418 
   373 @eh.wrapfunction(obsolete.obsstore, 'add')
   419 @eh.wrapfunction(obsolete.obsstore, 'add')  # new marker
   374 def clearonadd(orig, obsstore, *args, **kwargs):
   420 def clearonadd(orig, obsstore, *args, **kwargs):
   375     try:
   421     try:
   376         return orig(obsstore, *args, **kwargs)
   422         return orig(obsstore, *args, **kwargs)
   377     finally:
   423     finally:
   378         obsstore.caches.clear()
   424         obsstore.caches.clear()
   379 
   425 
   380 ### cache user
   426 ### Use the case
       
   427 # Function in core that could benefic from the cache are overwritten by cache using version
       
   428 
       
   429 # changectx method
   381 
   430 
   382 @eh.addattr(context.changectx, 'unstable')
   431 @eh.addattr(context.changectx, 'unstable')
   383 def unstable(ctx):
   432 def unstable(ctx):
   384     """is the changeset unstable (have obsolete ancestor)"""
   433     """is the changeset unstable (have obsolete ancestor)"""
   385     if ctx.node() is None:
   434     if ctx.node() is None:
   391 def extinct(ctx):
   440 def extinct(ctx):
   392     """is the changeset extinct by other"""
   441     """is the changeset extinct by other"""
   393     if ctx.node() is None:
   442     if ctx.node() is None:
   394         return False
   443         return False
   395     return ctx.rev() in getobscache(ctx._repo, 'extinct')
   444     return ctx.rev() in getobscache(ctx._repo, 'extinct')
       
   445 
       
   446 # revset
   396 
   447 
   397 @eh.revset('obsolete')
   448 @eh.revset('obsolete')
   398 def revsetobsolete(repo, subset, x):
   449 def revsetobsolete(repo, subset, x):
   399     """``obsolete()``
   450     """``obsolete()``
   400     Changeset is obsolete.
   451     Changeset is obsolete.