##// END OF EJS Templates
obsolete: introduce caches for all meaningful sets...
Pierre-Yves David -
r17469:fb72eec7 default
parent child Browse files
Show More
@@ -11,6 +11,7 b' import ancestor, mdiff, error, util, scm'
11 11 import copies
12 12 import match as matchmod
13 13 import os, errno, stat
14 import obsolete as obsmod
14 15
15 16 propertycache = util.propertycache
16 17
@@ -232,38 +233,15 b' class changectx(object):'
232 233
233 234 def obsolete(self):
234 235 """True if the changeset is obsolete"""
235 return (self.node() in self._repo.obsstore.precursors
236 and self.phase() > phases.public)
236 return self.rev() in obsmod.getobscache(self._repo, 'obsolete')
237 237
238 238 def extinct(self):
239 239 """True if the changeset is extinct"""
240 # We should just compute a cache and check against it.
241 # See revset implementation for details.
242 #
243 # But this naive implementation does not require cache
244 if self.phase() <= phases.public:
245 return False
246 if not self.obsolete():
247 return False
248 for desc in self.descendants():
249 if not desc.obsolete():
250 return False
251 return True
240 return self.rev() in obsmod.getobscache(self._repo, 'extinct')
252 241
253 242 def unstable(self):
254 243 """True if the changeset is not obsolete but it's ancestor are"""
255 # We should just compute /(obsolete()::) - obsolete()/
256 # and keep it in a cache.
257 #
258 # But this naive implementation does not require cache
259 if self.phase() <= phases.public:
260 return False
261 if self.obsolete():
262 return False
263 for anc in self.ancestors():
264 if anc.obsolete():
265 return True
266 return False
244 return self.rev() in obsmod.getobscache(self._repo, 'unstable')
267 245
268 246 def _fileinfo(self, path):
269 247 if '_manifest' in self.__dict__:
@@ -1042,6 +1042,7 b' class localrepository(object):'
1042 1042
1043 1043 self._branchcache = None # in UTF-8
1044 1044 self._branchcachetip = None
1045 obsolete.clearobscaches(self)
1045 1046
1046 1047 def invalidatedirstate(self):
1047 1048 '''Invalidates the dirstate, causing the next call to dirstate
@@ -2404,6 +2405,7 b' class localrepository(object):'
2404 2405 self.ui.status(_("added %d changesets"
2405 2406 " with %d changes to %d files%s\n")
2406 2407 % (changesets, revisions, files, htext))
2408 obsolete.clearobscaches(self)
2407 2409
2408 2410 if changesets > 0:
2409 2411 p = lambda: cl.writepending() and self.root or ""
@@ -161,6 +161,8 b' class obsstore(object):'
161 161 """
162 162
163 163 def __init__(self, sopener):
164 # caches for various obsolescence related cache
165 self.caches = {}
164 166 self._all = []
165 167 # new markers to serialize
166 168 self.precursors = {}
@@ -220,6 +222,8 b' class obsstore(object):'
220 222 # call 'filecacheentry.refresh()' here
221 223 f.close()
222 224 self._load(new)
225 # new marker *may* have changed several set. invalidate the cache.
226 self.caches.clear()
223 227 return len(new)
224 228
225 229 def mergemarkers(self, transation, data):
@@ -327,3 +331,67 b' def anysuccessors(obsstore, node):'
327 331 if suc not in seen:
328 332 seen.add(suc)
329 333 remaining.add(suc)
334
335 # mapping of 'set-name' -> <function to computer this set>
336 cachefuncs = {}
337 def cachefor(name):
338 """Decorator to register a function as computing the cache for a set"""
339 def decorator(func):
340 assert name not in cachefuncs
341 cachefuncs[name] = func
342 return func
343 return decorator
344
345 def getobscache(repo, name):
346 """Return the set of revision that belong to the <name> set
347
348 Such access may compute the set and cache it for future use"""
349 if not repo.obsstore:
350 return ()
351 if name not in repo.obsstore.caches:
352 repo.obsstore.caches[name] = cachefuncs[name](repo)
353 return repo.obsstore.caches[name]
354
355 # To be simple we need to invalidate obsolescence cache when:
356 #
357 # - new changeset is added:
358 # - public phase is changed
359 # - obsolescence marker are added
360 # - strip is used a repo
361 def clearobscaches(repo):
362 """Remove all obsolescence related cache from a repo
363
364 This remove all cache in obsstore is the obsstore already exist on the
365 repo.
366
367 (We could be smarter here given the exact event that trigger the cache
368 clearing)"""
369 # only clear cache is there is obsstore data in this repo
370 if 'obsstore' in repo._filecache:
371 repo.obsstore.caches.clear()
372
373 @cachefor('obsolete')
374 def _computeobsoleteset(repo):
375 """the set of obsolete revisions"""
376 obs = set()
377 nm = repo.changelog.nodemap
378 for prec in repo.obsstore.precursors:
379 rev = nm.get(prec)
380 if rev is not None:
381 obs.add(rev)
382 return set(repo.revs('%ld - public()', obs))
383
384 @cachefor('unstable')
385 def _computeunstableset(repo):
386 """the set of non obsolete revisions with obsolete parents"""
387 return set(repo.revs('(obsolete()::) - obsolete()'))
388
389 @cachefor('suspended')
390 def _computesuspendedset(repo):
391 """the set of obsolete parents with non obsolete descendants"""
392 return set(repo.revs('obsolete() and obsolete()::unstable()'))
393
394 @cachefor('extinct')
395 def _computeextinctset(repo):
396 """the set of obsolete parents without non obsolete descendants"""
397 return set(repo.revs('obsolete() - obsolete()::unstable()'))
@@ -104,6 +104,7 b' import errno'
104 104 from node import nullid, nullrev, bin, hex, short
105 105 from i18n import _
106 106 import util
107 import obsolete
107 108
108 109 allphases = public, draft, secret = range(3)
109 110 trackedphases = allphases[1:]
@@ -244,6 +245,7 b' class phasecache(object):'
244 245 # declare deleted root in the target phase
245 246 if targetphase != 0:
246 247 self.retractboundary(repo, targetphase, delroots)
248 obsolete.clearobscaches(repo)
247 249
248 250 def retractboundary(self, repo, targetphase, nodes):
249 251 # Be careful to preserve shallow-copied values: do not update
@@ -260,6 +262,7 b' class phasecache(object):'
260 262 ctxs = repo.set('roots(%ln::)', currentroots)
261 263 currentroots.intersection_update(ctx.node() for ctx in ctxs)
262 264 self._updateroots(targetphase, currentroots)
265 obsolete.clearobscaches(repo)
263 266
264 267 def advanceboundary(repo, targetphase, nodes):
265 268 """Add nodes to a phase changing other nodes phases if necessary.
@@ -12,6 +12,7 b' import bookmarks as bookmarksmod'
12 12 import match as matchmod
13 13 from i18n import _
14 14 import encoding
15 import obsolete as obsmod
15 16
16 17 def _revancestors(repo, revs, followfirst):
17 18 """Like revlog.ancestors(), but supports followfirst."""
@@ -621,8 +622,8 b' def extinct(repo, subset, x):'
621 622 """
622 623 # i18n: "extinct" is a keyword
623 624 getargs(x, 0, 0, _("extinct takes no arguments"))
624 extinctset = set(repo.revs('(obsolete()::) - (::(not obsolete()))'))
625 return [r for r in subset if r in extinctset]
625 extincts = obsmod.getobscache(repo, 'extinct')
626 return [r for r in subset if r in extincts]
626 627
627 628 def extra(repo, subset, x):
628 629 """``extra(label, [value])``
@@ -959,7 +960,8 b' def obsolete(repo, subset, x):'
959 960 Mutable changeset with a newer version."""
960 961 # i18n: "obsolete" is a keyword
961 962 getargs(x, 0, 0, _("obsolete takes no arguments"))
962 return [r for r in subset if repo[r].obsolete()]
963 obsoletes = obsmod.getobscache(repo, 'obsolete')
964 return [r for r in subset if r in obsoletes]
963 965
964 966 def origin(repo, subset, x):
965 967 """``origin([set])``
@@ -1437,8 +1439,8 b' def unstable(repo, subset, x):'
1437 1439 """
1438 1440 # i18n: "unstable" is a keyword
1439 1441 getargs(x, 0, 0, _("unstable takes no arguments"))
1440 unstableset = set(repo.revs('(obsolete()::) - obsolete()'))
1441 return [r for r in subset if r in unstableset]
1442 unstables = obsmod.getobscache(repo, 'unstable')
1443 return [r for r in subset if r in unstables]
1442 1444
1443 1445
1444 1446 def user(repo, subset, x):
General Comments 0
You need to be logged in to leave comments. Login now