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 |
|
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 extincts |
|
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 unstables |
|
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