Patchwork [2,of,5,RFC] phases: enable listphases to emit public phase heads

mail settings
Submitter Gregory Szorc
Date Feb. 16, 2016, 2:03 a.m.
Message ID <52961d59851f8455f31c.1455588213@ubuntu-vm-main>
Download mbox | patch
Permalink /patch/13219/
State Accepted
Headers show


Gregory Szorc - Feb. 16, 2016, 2:03 a.m.
# HG changeset patch
# User Gregory Szorc <>
# Date 1455585317 28800
#      Mon Feb 15 17:15:17 2016 -0800
# Node ID 52961d59851f8455f31ca996134870b94e6ea8a0
# Parent  8449ef66f732a71008dc887ffd6efbfb6dc64ee0
phases: enable listphases to emit public phase heads

Currently, the phases namespace exposes draft phase roots. On
mega-headed non-publishing repositories such as Mozilla's Try and
code review repositories, the listkey for draft phase roots can be
>1 MB. For clients on slow connections, the exchange of phases
information in this manner is inefficient.

Upcoming patches will teach the phases discovery mechanism to talk
in terms of public phase heads as opposed to draft phase roots. This
will require phases exchange to use far less data.

The first step of this is enabling the phases listkeys to expose
public phase heads instead of draft phase roots. This patch
implements that.


diff --git a/mercurial/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -243,16 +243,23 @@  class phasecache(object):
             return public
         if rev < nullrev:
             raise ValueError(_('cannot lookup negative revision'))
         if self._phaserevs is None or rev >= len(self._phaserevs):
         return self._phaserevs[rev]
+    def publicheads(self, repo):
+        """Return the set of nodes that are "heads" of the public graph."""
+        revset = repo.set('parents(%ln + %ln) & public()',
+                          self.phaseroots[secret],
+                          self.phaseroots[draft])
+        return set(c.node() for c in revset)
     def write(self):
         if not self.dirty:
         f = self.opener('phaseroots', 'w', atomictemp=True)
@@ -369,22 +376,35 @@  def retractboundary(repo, tr, targetphas
     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."""
     phcache = repo._phasecache.copy()
     phcache.retractboundary(repo, tr, targetphase, nodes)
-def listphases(repo):
-    """List phases root for serialization over pushkey"""
+def listphases(repo, publicheads=False):
+    """List phases heads/root for serialization over pushkey.
+    By default, returns a dict of draft phase roots in hex to the draft
+    phase value (``1``). If ``publicheads`` is set, returns a
+    dict of public phase heads to the public phase value (``0``).
+    If the repo is publishing, the key ``publishing`` will be set to
+    string value ``True``.
+    """
     keys = {}
-    value = '%i' % draft
-    for root in repo._phasecache.phaseroots[draft]:
-        keys[hex(root)] = value
+    if publicheads:
+        value = '%i' % public
+        for head in repo._phasecache.publicheads(repo):
+            keys[hex(head)] = value
+    else:
+        value = '%i' % draft
+        for root in repo._phasecache.phaseroots[draft]:
+            keys[hex(root)] = value
     if repo.publishing():
         # Add an extra data to let remote know we are a publishing
         # repo. Publishing repo can't just pretend they are old repo.
         # When pushing to a publishing repo, the client still need to
         # push phase boundary
         # Push do not only push changeset. It also push phase data.
@@ -396,16 +416,20 @@  def listphases(repo):
         # 3) repo B push to repo A. X is not pushed but the data that
         #    X as now public should
         # The server can't handle it on it's own as it has no idea of
         # client phase data.
         keys['publishing'] = 'True'
     return keys
+def listpublicphases(repo):
+    """Wrapper to be used for listkey registration."""
+    return listphases(repo, publicheads=True)
 def pushphase(repo, nhex, oldphasestr, newphasestr):
     """List phases root for serialization over pushkey"""
     repo = repo.unfiltered()
     with repo.lock():
         currentphase = repo[nhex].phase()
         newphase = abs(int(newphasestr)) # let's avoid negative index surprise
         oldphase = abs(int(oldphasestr)) # let's avoid negative index surprise
         if currentphase == oldphase and newphase < oldphase: