diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1242,7 +1242,8 @@ **pycompat.strkwargs(args)) if hook.hashook(repo.ui, 'pretxnclose-phase'): cl = repo.unfiltered().changelog - for rev, (old, new) in tr.changes['phases'].items(): + phasechanges = phases.phasechanges(tr) + for rev, (old, new) in phasechanges: args = tr.hookargs.copy() node = hex(cl.node(rev)) args.update(phases.preparehookargs(node, old, new)) @@ -1277,7 +1278,7 @@ checkambigfiles=_cachedfiles) tr.changes['revs'] = xrange(0, 0) tr.changes['obsmarkers'] = set() - tr.changes['phases'] = {} + phases.initphasechange(tr) tr.changes['bookmarks'] = {} tr.hookargs['txnid'] = txnid @@ -1306,7 +1307,7 @@ if hook.hashook(repo.ui, 'txnclose-phase'): cl = repo.unfiltered().changelog - phasemv = sorted(tr.changes['phases'].items()) + phasemv = sorted(phases.phasechanges(tr)) for rev, (old, new) in phasemv: args = tr.hookargs.copy() node = hex(cl.node(rev)) diff --git a/mercurial/phases.py b/mercurial/phases.py --- a/mercurial/phases.py +++ b/mercurial/phases.py @@ -186,17 +186,43 @@ headsbyphase[phase].append(node) return headsbyphase -def _trackphasechange(data, rev, old, new): - """add a phase move the dictionnary - If data is None, nothing happens. +_transitions = [(p1, p2) for p1 in allphases + [None] for p2 in allphases] +phasemapping = dict(zip(_transitions, xrange(1, 1 + len(_transitions)))) +phasemapping2 = dict([ + ((phasemapping[p1, p2], p3), phasemapping[p1, p3]) + for p1 in allphases + [None] + for p2 in allphases + for p3 in allphases]) +phasemapping_reverse = dict([(p2, p1) for (p1, p2) in phasemapping.items()]) + +def phasechange(tr, rev): + """get the recorded phase move for revision + + If no change was recorded, None is returned. """ - if data is None: - return - existing = data.get(rev) - if existing is not None: - old = existing[0] - data[rev] = (old, new) + data = tr.changes.get('phases', {}) + transition = data.get(rev) + if not transition: + return None + return phasemapping_reverse[data[rev]] + +def phasechanges(tr): + """generate all recorded phase moves""" + data = tr.changes.get('phases', {}) + for rev in data: + yield (rev, phasemapping_reverse[data[rev]]) + +def initphasechange(tr): + tr.changes['phases'] = {} + +def addphasechange(tr, rev, old, new): + """add a phase move to the transaction""" + data = tr.changes.setdefault('phases', {}) + if rev in data: + data[rev] = phasemapping2[data[rev], new] + else: + data[rev] = phasemapping[old, new] class phasecache(object): def __init__(self, repo, phasedefaults, _load=True): @@ -337,13 +363,12 @@ repo = repo.unfiltered() self._retractboundary(repo, tr, targetphase, nodes) if tr is not None and 'phases' in tr.changes: - phasetracking = tr.changes['phases'] torev = repo.changelog.rev phase = self.phase for n in nodes: rev = torev(n) revphase = phase(repo, rev) - _trackphasechange(phasetracking, rev, None, revphase) + addphasechange(tr, rev, None, revphase) repo.invalidatevolatilesets() def advanceboundary(self, repo, tr, targetphase, nodes): @@ -353,11 +378,6 @@ """ # Be careful to preserve shallow-copied values: do not update # phaseroots values, replace them. - if tr is None: - phasetracking = None - else: - phasetracking = tr.changes.get('phases') - repo = repo.unfiltered() delroots = [] # set of root deleted by this path @@ -371,9 +391,10 @@ olds = self.phaseroots[phase] affected = repo.revs('%ln::%ln', olds, nodes) - for r in affected: - _trackphasechange(phasetracking, r, self.phase(repo, r), - targetphase) + if tr: + for r in affected: + addphasechange(tr, r, self.phase(repo, r), + targetphase) roots = set(ctx.node() for ctx in repo.set( 'roots((%ln::) - %ld)', olds, affected)) @@ -388,13 +409,8 @@ def retractboundary(self, repo, tr, targetphase, nodes): oldroots = self.phaseroots[:targetphase + 1] - if tr is None: - phasetracking = None - else: - phasetracking = tr.changes.get('phases') repo = repo.unfiltered() - if (self._retractboundary(repo, tr, targetphase, nodes) - and phasetracking is not None): + if self._retractboundary(repo, tr, targetphase, nodes) and tr: # find the affected revisions new = self.phaseroots[targetphase] @@ -410,7 +426,7 @@ else: # public phase revs = affected for r in revs: - _trackphasechange(phasetracking, r, phase, targetphase) + addphasechange(tr, r, phase, targetphase) repo.invalidatevolatilesets() def _retractboundary(self, repo, tr, targetphase, nodes): diff --git a/tests/testlib/ext-phase-report.py b/tests/testlib/ext-phase-report.py --- a/tests/testlib/ext-phase-report.py +++ b/tests/testlib/ext-phase-report.py @@ -1,11 +1,12 @@ # tiny extension to report phase changes during transaction from __future__ import absolute_import +from mercurial import phases def reposetup(ui, repo): def reportphasemove(tr): - for rev, move in sorted(tr.changes['phases'].iteritems()): + for rev, move in sorted(phases.phasechanges(tr)): if move[0] is None: ui.write(('test-debug-phase: new rev %d: x -> %d\n' % (rev, move[1])))