Patchwork D1621: transaction: encodes tuples in changes['phases'] as 4 bit ints

login
register
mail settings
Submitter phabricator
Date Dec. 10, 2017, 9:11 p.m.
Message ID <01c981116cd78492fc9bc9263a960798@localhost.localdomain>
Download mbox | patch
Permalink /patch/26205/
State Not Applicable
Headers show

Comments

phabricator - Dec. 10, 2017, 9:11 p.m.
joerg.sonnenberger updated this revision to Diff 4341.
joerg.sonnenberger edited the summary of this revision.
joerg.sonnenberger retitled this revision from "[PoC] transaction: split changes['phases'] into sets for src and target phase" to "transaction: encodes tuples in changes['phases'] as 4 bit ints".

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D1621?vs=4278&id=4341

REVISION DETAIL
  https://phab.mercurial-scm.org/D1621

AFFECTED FILES
  mercurial/localrepo.py
  mercurial/phases.py
  tests/testlib/ext-phase-report.py

CHANGE DETAILS




To: joerg.sonnenberger, #hg-reviewers, quark
Cc: yuja, quark, mercurial-devel

Patch

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])))
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 <data> 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 <rev>
+
+    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 <tr> 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/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))