|
|
# Mercurial phases support code
|
|
|
#
|
|
|
# Copyright 2011 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
|
|
|
# Logilab SA <contact@logilab.fr>
|
|
|
# Augie Fackler <durin42@gmail.com>
|
|
|
#
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
|
|
import errno
|
|
|
from node import nullid, bin, hex, short
|
|
|
from i18n import _
|
|
|
|
|
|
allphases = range(2)
|
|
|
trackedphases = allphases[1:]
|
|
|
|
|
|
def readroots(repo):
|
|
|
"""Read phase roots from disk"""
|
|
|
roots = [set() for i in allphases]
|
|
|
roots[0].add(nullid)
|
|
|
try:
|
|
|
f = repo.sopener('phaseroots')
|
|
|
try:
|
|
|
for line in f:
|
|
|
phase, nh = line.strip().split()
|
|
|
roots[int(phase)].add(bin(nh))
|
|
|
finally:
|
|
|
f.close()
|
|
|
except IOError, inst:
|
|
|
if inst.errno != errno.ENOENT:
|
|
|
raise
|
|
|
return roots
|
|
|
|
|
|
def writeroots(repo):
|
|
|
"""Write phase roots from disk"""
|
|
|
f = repo.sopener('phaseroots', 'w', atomictemp=True)
|
|
|
try:
|
|
|
for phase, roots in enumerate(repo._phaseroots):
|
|
|
for h in roots:
|
|
|
f.write('%i %s\n' % (phase, hex(h)))
|
|
|
repo._dirtyphases = False
|
|
|
finally:
|
|
|
f.close()
|
|
|
|
|
|
def filterunknown(repo, phaseroots=None):
|
|
|
"""remove unknown nodes from the phase boundary
|
|
|
|
|
|
no data is lost as unknown node only old data for their descentants
|
|
|
"""
|
|
|
if phaseroots is None:
|
|
|
phaseroots = repo._phaseroots
|
|
|
for phase, nodes in enumerate(phaseroots):
|
|
|
missing = [node for node in nodes if node not in repo]
|
|
|
if missing:
|
|
|
for mnode in missing:
|
|
|
msg = _('Removing unknown node %(n)s from %(p)i-phase boundary')
|
|
|
repo.ui.debug(msg, {'n': short(mnode), 'p': phase})
|
|
|
nodes.symmetric_difference_update(missing)
|
|
|
repo._dirtyphases = True
|
|
|
|
|
|
def advanceboundary(repo, targetphase, nodes):
|
|
|
"""Add nodes to a phase changing other nodes phases if necessary.
|
|
|
|
|
|
This function move boundary *forward* this means that all nodes are set
|
|
|
in the target phase or kept in a *lower* phase.
|
|
|
|
|
|
Simplify boundary to contains phase roots only."""
|
|
|
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 = repo._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
|
|
|
if '_phaserev' in vars(repo):
|
|
|
del repo._phaserev
|
|
|
repo._dirtyphases = True
|
|
|
|
|
|
def retractboundary(repo, targetphase, nodes):
|
|
|
"""Set nodes back to a phase changing other nodes phases if necessary.
|
|
|
|
|
|
This function move boundary *backward* this means that all nodes are set
|
|
|
in the target phase or kept in a *higher* phase.
|
|
|
|
|
|
Simplify boundary to contains phase roots only."""
|
|
|
currentroots = repo._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)
|
|
|
if '_phaserev' in vars(repo):
|
|
|
del repo._phaserev
|
|
|
repo._dirtyphases = True
|
|
|
|
|
|
|