mercurial/phases.py
changeset 16658 6b3d31d04a69
parent 16657 b6081c2c4647
child 16659 58edd786e96f
equal deleted inserted replaced
16657:b6081c2c4647 16658:6b3d31d04a69
   155     if _filterunknown(repo.ui, repo.changelog, roots):
   155     if _filterunknown(repo.ui, repo.changelog, roots):
   156         dirty = True
   156         dirty = True
   157     return roots, dirty
   157     return roots, dirty
   158 
   158 
   159 class phasecache(object):
   159 class phasecache(object):
   160     def __init__(self, repo, phasedefaults):
   160     def __init__(self, repo, phasedefaults, _load=True):
   161         self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
   161         if _load:
   162         self.opener = repo.sopener
   162             # Cheap trick to allow shallow-copy without copy module
   163         self._phaserevs = None
   163             self.phaseroots, self.dirty = _readroots(repo, phasedefaults)
       
   164             self.opener = repo.sopener
       
   165             self._phaserevs = None
       
   166 
       
   167     def copy(self):
       
   168         # Shallow copy meant to ensure isolation in
       
   169         # advance/retractboundary(), nothing more.
       
   170         ph = phasecache(None, None, _load=False)
       
   171         ph.phaseroots = self.phaseroots[:]
       
   172         ph.dirty = self.dirty
       
   173         ph.opener = self.opener
       
   174         ph._phaserevs = self._phaserevs
       
   175         return ph
       
   176 
       
   177     def replace(self, phcache):
       
   178         for a in 'phaseroots dirty opener _phaserevs'.split():
       
   179             setattr(self, a, getattr(phcache, a))
   164 
   180 
   165     def getphaserevs(self, repo, rebuild=False):
   181     def getphaserevs(self, repo, rebuild=False):
   166         if rebuild or self._phaserevs is None:
   182         if rebuild or self._phaserevs is None:
   167             revs = [public] * len(repo.changelog)
   183             revs = [public] * len(repo.changelog)
   168             for phase in trackedphases:
   184             for phase in trackedphases:
   173                     for rev in repo.changelog.descendants(*roots):
   189                     for rev in repo.changelog.descendants(*roots):
   174                         revs[rev] = phase
   190                         revs[rev] = phase
   175             self._phaserevs = revs
   191             self._phaserevs = revs
   176         return self._phaserevs
   192         return self._phaserevs
   177 
   193 
   178     def invalidatephaserevs(self):
       
   179         self._phaserevs = None
       
   180 
       
   181     def phase(self, repo, rev):
   194     def phase(self, repo, rev):
   182         # We need a repo argument here to be able to build _phaserev
   195         # We need a repo argument here to be able to build _phaserev
   183         # if necessary. The repository instance is not stored in
   196         # if necessary. The repository instance is not stored in
   184         # phasecache to avoid reference cycles. The changelog instance
   197         # phasecache to avoid reference cycles. The changelog instance
   185         # is not stored because it is a filecache() property and can
   198         # is not stored because it is a filecache() property and can
   200                     f.write('%i %s\n' % (phase, hex(h)))
   213                     f.write('%i %s\n' % (phase, hex(h)))
   201         finally:
   214         finally:
   202             f.close()
   215             f.close()
   203         self.dirty = False
   216         self.dirty = False
   204 
   217 
       
   218     def _updateroots(self, phase, newroots):
       
   219         self.phaseroots[phase] = newroots
       
   220         self._phaserevs = None
       
   221         self.dirty = True
       
   222 
       
   223     def advanceboundary(self, repo, targetphase, nodes):
       
   224         # Be careful to preserve shallow-copied values: do not update
       
   225         # phaseroots values, replace them.
       
   226 
       
   227         delroots = [] # set of root deleted by this path
       
   228         for phase in xrange(targetphase + 1, len(allphases)):
       
   229             # filter nodes that are not in a compatible phase already
       
   230             nodes = [n for n in nodes
       
   231                      if self.phase(repo, repo[n].rev()) >= phase]
       
   232             if not nodes:
       
   233                 break # no roots to move anymore
       
   234             olds = self.phaseroots[phase]
       
   235             roots = set(ctx.node() for ctx in repo.set(
       
   236                     'roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
       
   237             if olds != roots:
       
   238                 self._updateroots(phase, roots)
       
   239                 # some roots may need to be declared for lower phases
       
   240                 delroots.extend(olds - roots)
       
   241             # declare deleted root in the target phase
       
   242             if targetphase != 0:
       
   243                 self.retractboundary(repo, targetphase, delroots)
       
   244 
       
   245     def retractboundary(self, repo, targetphase, nodes):
       
   246         # Be careful to preserve shallow-copied values: do not update
       
   247         # phaseroots values, replace them.
       
   248 
       
   249         currentroots = self.phaseroots[targetphase]
       
   250         newroots = [n for n in nodes
       
   251                     if self.phase(repo, repo[n].rev()) < targetphase]
       
   252         if newroots:
       
   253             currentroots = currentroots.copy()
       
   254             currentroots.update(newroots)
       
   255             ctxs = repo.set('roots(%ln::)', currentroots)
       
   256             currentroots.intersection_update(ctx.node() for ctx in ctxs)
       
   257             self._updateroots(targetphase, currentroots)
       
   258 
   205 def advanceboundary(repo, targetphase, nodes):
   259 def advanceboundary(repo, targetphase, nodes):
   206     """Add nodes to a phase changing other nodes phases if necessary.
   260     """Add nodes to a phase changing other nodes phases if necessary.
   207 
   261 
   208     This function move boundary *forward* this means that all nodes are set
   262     This function move boundary *forward* this means that all nodes are set
   209     in the target phase or kept in a *lower* phase.
   263     in the target phase or kept in a *lower* phase.
   210 
   264 
   211     Simplify boundary to contains phase roots only."""
   265     Simplify boundary to contains phase roots only."""
   212     phcache = repo._phasecache
   266     phcache = repo._phasecache.copy()
   213 
   267     phcache.advanceboundary(repo, targetphase, nodes)
   214     delroots = [] # set of root deleted by this path
   268     repo._phasecache.replace(phcache)
   215     for phase in xrange(targetphase + 1, len(allphases)):
       
   216         # filter nodes that are not in a compatible phase already
       
   217         # XXX rev phase cache might have been invalidated by a previous loop
       
   218         # XXX we need to be smarter here
       
   219         nodes = [n for n in nodes if repo[n].phase() >= phase]
       
   220         if not nodes:
       
   221             break # no roots to move anymore
       
   222         roots = phcache.phaseroots[phase]
       
   223         olds = roots.copy()
       
   224         ctxs = list(repo.set('roots((%ln::) - (%ln::%ln))', olds, olds, nodes))
       
   225         roots.clear()
       
   226         roots.update(ctx.node() for ctx in ctxs)
       
   227         if olds != roots:
       
   228             # invalidate cache (we probably could be smarter here
       
   229             phcache.invalidatephaserevs()
       
   230             phcache.dirty = True
       
   231             # some roots may need to be declared for lower phases
       
   232             delroots.extend(olds - roots)
       
   233         # declare deleted root in the target phase
       
   234         if targetphase != 0:
       
   235             retractboundary(repo, targetphase, delroots)
       
   236 
       
   237 
   269 
   238 def retractboundary(repo, targetphase, nodes):
   270 def retractboundary(repo, targetphase, nodes):
   239     """Set nodes back to a phase changing other nodes phases if necessary.
   271     """Set nodes back to a phase changing other nodes phases if necessary.
   240 
   272 
   241     This function move boundary *backward* this means that all nodes are set
   273     This function move boundary *backward* this means that all nodes are set
   242     in the target phase or kept in a *higher* phase.
   274     in the target phase or kept in a *higher* phase.
   243 
   275 
   244     Simplify boundary to contains phase roots only."""
   276     Simplify boundary to contains phase roots only."""
   245     phcache = repo._phasecache
   277     phcache = repo._phasecache.copy()
   246 
   278     phcache.retractboundary(repo, targetphase, nodes)
   247     currentroots = phcache.phaseroots[targetphase]
   279     repo._phasecache.replace(phcache)
   248     newroots = [n for n in nodes if repo[n].phase() < targetphase]
       
   249     if newroots:
       
   250         currentroots.update(newroots)
       
   251         ctxs = repo.set('roots(%ln::)', currentroots)
       
   252         currentroots.intersection_update(ctx.node() for ctx in ctxs)
       
   253         phcache.invalidatephaserevs()
       
   254         phcache.dirty = True
       
   255 
       
   256 
   280 
   257 def listphases(repo):
   281 def listphases(repo):
   258     """List phases root for serialisation over pushkey"""
   282     """List phases root for serialisation over pushkey"""
   259     keys = {}
   283     keys = {}
   260     value = '%i' % draft
   284     value = '%i' % draft