##// 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 import copies
11 import copies
12 import match as matchmod
12 import match as matchmod
13 import os, errno, stat
13 import os, errno, stat
14 import obsolete as obsmod
14
15
15 propertycache = util.propertycache
16 propertycache = util.propertycache
16
17
@@ -232,38 +233,15 b' class changectx(object):'
232
233
233 def obsolete(self):
234 def obsolete(self):
234 """True if the changeset is obsolete"""
235 """True if the changeset is obsolete"""
235 return (self.node() in self._repo.obsstore.precursors
236 return self.rev() in obsmod.getobscache(self._repo, 'obsolete')
236 and self.phase() > phases.public)
237
237
238 def extinct(self):
238 def extinct(self):
239 """True if the changeset is extinct"""
239 """True if the changeset is extinct"""
240 # We should just compute a cache and check against it.
240 return self.rev() in obsmod.getobscache(self._repo, 'extinct')
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
252
241
253 def unstable(self):
242 def unstable(self):
254 """True if the changeset is not obsolete but it's ancestor are"""
243 """True if the changeset is not obsolete but it's ancestor are"""
255 # We should just compute /(obsolete()::) - obsolete()/
244 return self.rev() in obsmod.getobscache(self._repo, 'unstable')
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
267
245
268 def _fileinfo(self, path):
246 def _fileinfo(self, path):
269 if '_manifest' in self.__dict__:
247 if '_manifest' in self.__dict__:
@@ -1042,6 +1042,7 b' class localrepository(object):'
1042
1042
1043 self._branchcache = None # in UTF-8
1043 self._branchcache = None # in UTF-8
1044 self._branchcachetip = None
1044 self._branchcachetip = None
1045 obsolete.clearobscaches(self)
1045
1046
1046 def invalidatedirstate(self):
1047 def invalidatedirstate(self):
1047 '''Invalidates the dirstate, causing the next call to dirstate
1048 '''Invalidates the dirstate, causing the next call to dirstate
@@ -2404,6 +2405,7 b' class localrepository(object):'
2404 self.ui.status(_("added %d changesets"
2405 self.ui.status(_("added %d changesets"
2405 " with %d changes to %d files%s\n")
2406 " with %d changes to %d files%s\n")
2406 % (changesets, revisions, files, htext))
2407 % (changesets, revisions, files, htext))
2408 obsolete.clearobscaches(self)
2407
2409
2408 if changesets > 0:
2410 if changesets > 0:
2409 p = lambda: cl.writepending() and self.root or ""
2411 p = lambda: cl.writepending() and self.root or ""
@@ -161,6 +161,8 b' class obsstore(object):'
161 """
161 """
162
162
163 def __init__(self, sopener):
163 def __init__(self, sopener):
164 # caches for various obsolescence related cache
165 self.caches = {}
164 self._all = []
166 self._all = []
165 # new markers to serialize
167 # new markers to serialize
166 self.precursors = {}
168 self.precursors = {}
@@ -220,6 +222,8 b' class obsstore(object):'
220 # call 'filecacheentry.refresh()' here
222 # call 'filecacheentry.refresh()' here
221 f.close()
223 f.close()
222 self._load(new)
224 self._load(new)
225 # new marker *may* have changed several set. invalidate the cache.
226 self.caches.clear()
223 return len(new)
227 return len(new)
224
228
225 def mergemarkers(self, transation, data):
229 def mergemarkers(self, transation, data):
@@ -327,3 +331,67 b' def anysuccessors(obsstore, node):'
327 if suc not in seen:
331 if suc not in seen:
328 seen.add(suc)
332 seen.add(suc)
329 remaining.add(suc)
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 from node import nullid, nullrev, bin, hex, short
104 from node import nullid, nullrev, bin, hex, short
105 from i18n import _
105 from i18n import _
106 import util
106 import util
107 import obsolete
107
108
108 allphases = public, draft, secret = range(3)
109 allphases = public, draft, secret = range(3)
109 trackedphases = allphases[1:]
110 trackedphases = allphases[1:]
@@ -244,6 +245,7 b' class phasecache(object):'
244 # declare deleted root in the target phase
245 # declare deleted root in the target phase
245 if targetphase != 0:
246 if targetphase != 0:
246 self.retractboundary(repo, targetphase, delroots)
247 self.retractboundary(repo, targetphase, delroots)
248 obsolete.clearobscaches(repo)
247
249
248 def retractboundary(self, repo, targetphase, nodes):
250 def retractboundary(self, repo, targetphase, nodes):
249 # Be careful to preserve shallow-copied values: do not update
251 # Be careful to preserve shallow-copied values: do not update
@@ -260,6 +262,7 b' class phasecache(object):'
260 ctxs = repo.set('roots(%ln::)', currentroots)
262 ctxs = repo.set('roots(%ln::)', currentroots)
261 currentroots.intersection_update(ctx.node() for ctx in ctxs)
263 currentroots.intersection_update(ctx.node() for ctx in ctxs)
262 self._updateroots(targetphase, currentroots)
264 self._updateroots(targetphase, currentroots)
265 obsolete.clearobscaches(repo)
263
266
264 def advanceboundary(repo, targetphase, nodes):
267 def advanceboundary(repo, targetphase, nodes):
265 """Add nodes to a phase changing other nodes phases if necessary.
268 """Add nodes to a phase changing other nodes phases if necessary.
@@ -12,6 +12,7 b' import bookmarks as bookmarksmod'
12 import match as matchmod
12 import match as matchmod
13 from i18n import _
13 from i18n import _
14 import encoding
14 import encoding
15 import obsolete as obsmod
15
16
16 def _revancestors(repo, revs, followfirst):
17 def _revancestors(repo, revs, followfirst):
17 """Like revlog.ancestors(), but supports followfirst."""
18 """Like revlog.ancestors(), but supports followfirst."""
@@ -621,8 +622,8 b' def extinct(repo, subset, x):'
621 """
622 """
622 # i18n: "extinct" is a keyword
623 # i18n: "extinct" is a keyword
623 getargs(x, 0, 0, _("extinct takes no arguments"))
624 getargs(x, 0, 0, _("extinct takes no arguments"))
624 extinctset = set(repo.revs('(obsolete()::) - (::(not obsolete()))'))
625 extincts = obsmod.getobscache(repo, 'extinct')
625 return [r for r in subset if r in extinctset]
626 return [r for r in subset if r in extincts]
626
627
627 def extra(repo, subset, x):
628 def extra(repo, subset, x):
628 """``extra(label, [value])``
629 """``extra(label, [value])``
@@ -959,7 +960,8 b' def obsolete(repo, subset, x):'
959 Mutable changeset with a newer version."""
960 Mutable changeset with a newer version."""
960 # i18n: "obsolete" is a keyword
961 # i18n: "obsolete" is a keyword
961 getargs(x, 0, 0, _("obsolete takes no arguments"))
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 def origin(repo, subset, x):
966 def origin(repo, subset, x):
965 """``origin([set])``
967 """``origin([set])``
@@ -1437,8 +1439,8 b' def unstable(repo, subset, x):'
1437 """
1439 """
1438 # i18n: "unstable" is a keyword
1440 # i18n: "unstable" is a keyword
1439 getargs(x, 0, 0, _("unstable takes no arguments"))
1441 getargs(x, 0, 0, _("unstable takes no arguments"))
1440 unstableset = set(repo.revs('(obsolete()::) - obsolete()'))
1442 unstables = obsmod.getobscache(repo, 'unstable')
1441 return [r for r in subset if r in unstableset]
1443 return [r for r in subset if r in unstables]
1442
1444
1443
1445
1444 def user(repo, subset, x):
1446 def user(repo, subset, x):
General Comments 0
You need to be logged in to leave comments. Login now