[PATCH 06 of 11 full-series] checkheads: extract branchmap preprocessing

Pierre-Yves David pierre-yves.david at ens-lyon.org
Tue Jul 17 22:15:53 CDT 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at logilab.fr>
# Date 1342542109 -7200
# Node ID faad247099c2fa1a6c0bdba6e2d34d361133fe6f
# Parent  68b997f423b906173af68eff82425f771970542f
checkheads: extract branchmap preprocessing

The checkheads function is far too complicated. This extract help to explicite
what part of the preprocessing are reused by the actual check.

This the first step toward a wider refactoring.

diff --git a/mercurial/discovery.py b/mercurial/discovery.py
--- a/mercurial/discovery.py
+++ b/mercurial/discovery.py
@@ -149,77 +149,96 @@
 
     return og
 
+def _branchmapsummary(repo, remote, outgoing):
+    """compute a summary of branch and heads status before and after push
+
+    - oldmap:      {'branch': [heads]} mapping for remote
+    - newmap:      {'branch': [heads]} mapping for local
+    - unsynced:    set of branch that have unsynced remote changes
+    - branches:    set of all common branch pushed
+    - newbranches: list of plain new pushed branch
+    """
+    cl = repo.changelog
+
+    # A. Create set of branches involved in the push.
+    branches = set(repo[n].branch() for n in outgoing.missing)
+    remotemap = remote.branchmap()
+    newbranches = branches - set(remotemap)
+    branches.difference_update(newbranches)
+
+    # B. Construct the initial oldmap and newmap dicts.
+    # They contain information about the remote heads before and
+    # after the push, respectively.
+    # Heads not found locally are not included in either dict,
+    # since they won't be affected by the push.
+    # unsynced contains all branches with incoming changesets.
+    oldmap = {}
+    newmap = {}
+    unsynced = set()
+    for branch in branches:
+        remotebrheads = remotemap[branch]
+
+        prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
+        oldmap[branch] = prunedbrheads
+        newmap[branch] = list(prunedbrheads)
+        if len(remotebrheads) > len(prunedbrheads):
+            unsynced.add(branch)
+
+    # C. Update newmap with outgoing changes.
+    # This will possibly add new heads and remove existing ones.
+    ctxgen = (repo[n] for n in outgoing.missing)
+    repo._updatebranchcache(newmap, ctxgen)
+    return oldmap, newmap, unsynced, branches, newbranches
+
+def _oldbranchmapsummary(repo, remoteheads, outgoing, inc=False):
+    """Compute branchmapsummary for repo without branchmap support"""
+
+    cl = repo.changelog
+    # 1-4b. old servers: Check for new topological heads.
+    # Construct {old,new}map with branch = None (topological branch).
+    # (code based on _updatebranchcache)
+    oldheads = set(h for h in remoteheads if h in cl.nodemap)
+    # all nodes in outgoing.missing are children of either:
+    # - an element of oldheads
+    # - another element of outgoing.missing
+    # - nullrev
+    # This explains why the new head are very simple to compute.
+    r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
+    branches = set([None])
+    newmap = {None: list(c.node() for c in r)}
+    oldmap = {None: oldheads}
+    unsynced = inc and branches or set()
+    return oldmap, newmap, unsynced, branches, set()
+
 def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False):
     """Check that a push won't add any outgoing head
 
     raise Abort error and display ui message as needed.
     """
+    # Check for each named branch if we're creating new remote heads.
+    # To be a remote head after push, node must be either:
+    # - unknown locally
+    # - a local outgoing head descended from update
+    # - a remote head that's known locally and not
+    #   ancestral to an outgoing head
     if remoteheads == [nullid]:
         # remote is empty, nothing to check.
         return
 
-    cl = repo.changelog
     if remote.capable('branchmap'):
-        # Check for each named branch if we're creating new remote heads.
-        # To be a remote head after push, node must be either:
-        # - unknown locally
-        # - a local outgoing head descended from update
-        # - a remote head that's known locally and not
-        #   ancestral to an outgoing head
+        bms = _branchmapsummary(repo, remote, outgoing)
+    else:
+        bms = _oldbranchmapsummary(repo, remoteheads, outgoing, inc)
+    oldmap, newmap, unsynced, branches, newbranches = bms
+    # 1. Check for new branches on the remote.
+    if newbranches and not newbranch:  # new branch requires --new-branch
+        branchnames = ', '.join(sorted(newbranches))
+        raise util.Abort(_("push creates new remote branches: %s!")
+                           % branchnames,
+                         hint=_("use 'hg push --new-branch' to create"
+                                " new remote branches"))
 
-        # 1. Create set of branches involved in the push.
-        branches = set(repo[n].branch() for n in outgoing.missing)
-
-        # 2. Check for new branches on the remote.
-        remotemap = remote.branchmap()
-        newbranches = branches - set(remotemap)
-        if newbranches and not newbranch: # new branch requires --new-branch
-            branchnames = ', '.join(sorted(newbranches))
-            raise util.Abort(_("push creates new remote branches: %s!")
-                               % branchnames,
-                             hint=_("use 'hg push --new-branch' to create"
-                                    " new remote branches"))
-        branches.difference_update(newbranches)
-
-        # 3. Construct the initial oldmap and newmap dicts.
-        # They contain information about the remote heads before and
-        # after the push, respectively.
-        # Heads not found locally are not included in either dict,
-        # since they won't be affected by the push.
-        # unsynced contains all branches with incoming changesets.
-        oldmap = {}
-        newmap = {}
-        unsynced = set()
-        for branch in branches:
-            remotebrheads = remotemap[branch]
-            prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
-            oldmap[branch] = prunedbrheads
-            newmap[branch] = list(prunedbrheads)
-            if len(remotebrheads) > len(prunedbrheads):
-                unsynced.add(branch)
-
-        # 4. Update newmap with outgoing changes.
-        # This will possibly add new heads and remove existing ones.
-        ctxgen = (repo[n] for n in outgoing.missing)
-        repo._updatebranchcache(newmap, ctxgen)
-
-    else:
-        # 1-4b. old servers: Check for new topological heads.
-        # Construct {old,new}map with branch = None (topological branch).
-        # (code based on _updatebranchcache)
-        oldheads = set(h for h in remoteheads if h in cl.nodemap)
-        # all nodes in outgoing.missing are children of either:
-        # - an element of oldheads
-        # - another element of outgoing.missing
-        # - nullrev
-        # This explains why the new head are very simple to compute.
-        r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
-        branches = set([None])
-        newmap = {None: list(c.node() for c in r)}
-        oldmap = {None: oldheads}
-        unsynced = inc and branches or set()
-
-    # 5. Check for new heads.
+    # 2. Check for new heads.
     # If there are more heads after the push than before, a suitable
     # error message, depending on unsynced status, is displayed.
     error = None


More information about the Mercurial-devel mailing list