--- a/mercurial/phases.py Sat May 12 00:24:07 2012 +0200
+++ b/mercurial/phases.py Sat May 12 00:24:07 2012 +0200
@@ -157,10 +157,26 @@
return roots, dirty
class phasecache(object):
- def __init__(self, repo, phasedefaults):
- self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
- self.opener = repo.sopener
- self._phaserevs = None
+ def __init__(self, repo, phasedefaults, _load=True):
+ if _load:
+ # Cheap trick to allow shallow-copy without copy module
+ self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
+ self.opener = repo.sopener
+ self._phaserevs = None
+
+ def copy(self):
+ # Shallow copy meant to ensure isolation in
+ # advance/retractboundary(), nothing more.
+ ph = phasecache(None, None, _load=False)
+ ph.phaseroots = self.phaseroots[:]
+ ph.dirty = self.dirty
+ ph.opener = self.opener
+ ph._phaserevs = self._phaserevs
+ return ph
+
+ def replace(self, phcache):
+ for a in 'phaseroots dirty opener _phaserevs'.split():
+ setattr(self, a, getattr(phcache, a))
def getphaserevs(self, repo, rebuild=False):
if rebuild or self._phaserevs is None:
@@ -175,9 +191,6 @@
self._phaserevs = revs
return self._phaserevs
- def invalidatephaserevs(self):
- self._phaserevs = None
-
def phase(self, repo, rev):
# We need a repo argument here to be able to build _phaserev
# if necessary. The repository instance is not stored in
@@ -202,6 +215,47 @@
f.close()
self.dirty = False
+ def _updateroots(self, phase, newroots):
+ self.phaseroots[phase] = newroots
+ self._phaserevs = None
+ self.dirty = True
+
+ def advanceboundary(self, repo, targetphase, nodes):
+ # Be careful to preserve shallow-copied values: do not update
+ # phaseroots values, replace them.
+
+ delroots = [] # set of root deleted by this path
+ for phase in xrange(targetphase + 1, len(allphases)):
+ # filter nodes that are not in a compatible phase already
+ nodes = [n for n in nodes
+ if self.phase(repo, repo[n].rev()) >= phase]
+ if not nodes:
+ break # no roots to move anymore
+ olds = self.phaseroots[phase]
+ roots = set(ctx.node() for ctx in repo.set(
+ 'roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
+ if olds != roots:
+ self._updateroots(phase, roots)
+ # some roots may need to be declared for lower phases
+ delroots.extend(olds - roots)
+ # declare deleted root in the target phase
+ if targetphase != 0:
+ self.retractboundary(repo, targetphase, delroots)
+
+ def retractboundary(self, repo, targetphase, nodes):
+ # Be careful to preserve shallow-copied values: do not update
+ # phaseroots values, replace them.
+
+ currentroots = self.phaseroots[targetphase]
+ newroots = [n for n in nodes
+ if self.phase(repo, repo[n].rev()) < targetphase]
+ if newroots:
+ currentroots = currentroots.copy()
+ currentroots.update(newroots)
+ ctxs = repo.set('roots(%ln::)', currentroots)
+ currentroots.intersection_update(ctx.node() for ctx in ctxs)
+ self._updateroots(targetphase, currentroots)
+
def advanceboundary(repo, targetphase, nodes):
"""Add nodes to a phase changing other nodes phases if necessary.
@@ -209,31 +263,9 @@
in the target phase or kept in a *lower* phase.
Simplify boundary to contains phase roots only."""
- phcache = repo._phasecache
-
- delroots = [] # set of root deleted by this path
- for phase in xrange(targetphase + 1, len(allphases)):
- # filter nodes that are not in a compatible phase already
- # XXX rev phase cache might have been invalidated by a previous loop
- # XXX we need to be smarter here
- nodes = [n for n in nodes if repo[n].phase() >= phase]
- if not nodes:
- break # no roots to move anymore
- roots = phcache.phaseroots[phase]
- olds = roots.copy()
- ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
- roots.clear()
- roots.update(ctx.node() for ctx in ctxs)
- if olds != roots:
- # invalidate cache (we probably could be smarter here
- phcache.invalidatephaserevs()
- phcache.dirty = True
- # some roots may need to be declared for lower phases
- delroots.extend(olds - roots)
- # declare deleted root in the target phase
- if targetphase != 0:
- retractboundary(repo, targetphase, delroots)
-
+ phcache = repo._phasecache.copy()
+ phcache.advanceboundary(repo, targetphase, nodes)
+ repo._phasecache.replace(phcache)
def retractboundary(repo, targetphase, nodes):
"""Set nodes back to a phase changing other nodes phases if necessary.
@@ -242,17 +274,9 @@
in the target phase or kept in a *higher* phase.
Simplify boundary to contains phase roots only."""
- phcache = repo._phasecache
-
- currentroots = phcache.phaseroots[targetphase]
- newroots = [n for n in nodes if repo[n].phase() < targetphase]
- if newroots:
- currentroots.update(newroots)
- ctxs = repo.set('roots(%ln::)', currentroots)
- currentroots.intersection_update(ctx.node() for ctx in ctxs)
- phcache.invalidatephaserevs()
- phcache.dirty = True
-
+ phcache = repo._phasecache.copy()
+ phcache.retractboundary(repo, targetphase, nodes)
+ repo._phasecache.replace(phcache)
def listphases(repo):
"""List phases root for serialisation over pushkey"""