[PATCH 3 of 6] checkheads: simplify the structure build by preprocessing

pierre-yves.david at logilab.fr pierre-yves.david at logilab.fr
Tue Jul 17 11:49:34 CDT 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at logilab.fr>
# Date 1342535442 -7200
# Node ID 2c5cd8a23acd23b9b001ee0e9fe4c11269d4eaac
# Parent  23c7f8cc9cea4c7784577815296fe51ed018542f
checkheads: simplify the structure build by preprocessing

All useful data are now gathered in a single dictionnary.  `branchmapsummary` is
renamed to `headssummary` and its return value is greatly simplified.

diff --git a/mercurial/discovery.py b/mercurial/discovery.py
--- a/mercurial/discovery.py
+++ b/mercurial/discovery.py
@@ -147,52 +147,64 @@ def findcommonoutgoing(repo, other, only
         commonheads = set(og.commonheads)
         og.missingheads = [h for h in og.missingheads if h not in commonheads]
 
     return og
 
-def _branchmapsummary(repo, remote, outgoing):
+def _headssummary(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
+    return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping
+
+    - branch: the branch name
+    - remoteheads: the list of remote heads known locally
+                   None is the branch is new
+    - newheads: the new remote heads (known locally) with outgoing pushed
+    - unsyncedheads: the list of remote heads unknown locally.
     """
     cl = repo.changelog
-
+    headssum = {}
     # 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]
+    # A. register remote heads
+    remotebranches = set()
+    for branch, heads in remote.branchmap().iteritems():
+        remotebranches.add(branch)
+        known = []
+        unsynced = []
+        for h in heads:
+            if h in cl.nodemap:
+                known.append(h)
+            else:
+                unsynced.append(h)
+        headssum[branch] = (known, list(known), unsynced)
+    # B. add new branch data
+    missingctx = list(repo[n] for n in outgoing.missing)
+    touchedbranches = set()
+    for ctx in missingctx:
+        branch = ctx.branch()
+        touchedbranches.add(branch)
+        if branch not in headssum:
+            headssum[branch] = (None, [], [])
 
-        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 drop data about untouched branches:
+    for branch in remotebranches - touchedbranches:
+        del headssum[branch]
 
-    # C. Update newmap with outgoing changes.
+    # D. 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
+    newmap = dict((branch, heads[1]) for branch, heads in headssum.iteritems()
+                  if heads[0] is not None)
+    repo._updatebranchcache(newmap, missingctx)
+    for branch, newheads in newmap.iteritems():
+        headssum[branch][1][:] = newheads
+    return headssum
 
-def _oldbranchmapsummary(repo, remoteheads, outgoing, inc=False):
+def _oldheadssummary(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).
@@ -202,15 +214,13 @@ def _oldbranchmapsummary(repo, remotehea
     # - 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()
+    newheads = list(c.node() for c in r)
+    unsynced = inc and set([None]) or set()
+    return {None: (oldheads, newheads, unsynced)}
 
 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.
@@ -224,14 +234,15 @@ def checkheads(repo, remote, outgoing, r
     if remoteheads == [nullid]:
         # remote is empty, nothing to check.
         return
 
     if remote.capable('branchmap'):
-        bms = _branchmapsummary(repo, remote, outgoing)
+        headssum = _headssummary(repo, remote, outgoing)
     else:
-        bms = _oldbranchmapsummary(repo, remoteheads, outgoing, inc)
-    oldmap, newmap, unsynced, branches, newbranches = bms
+        headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
+    newbranches = [branch for branch, heads in headssum.iteritems()
+                   if heads[0] is None]
     # 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,
@@ -242,34 +253,41 @@ def checkheads(repo, remote, outgoing, r
     # If there are more heads after the push than before, a suitable
     # error message, depending on unsynced status, is displayed.
     error = None
     localbookmarks = repo._bookmarks
 
-    for branch in branches:
-        newhs = set(newmap[branch])
-        oldhs = set(oldmap[branch])
+    unsynced = False
+    for branch, heads in headssum.iteritems():
+        if heads[0] is None:
+            # Maybe we should abort if we push more that one head
+            # for new branches ?
+            continue
+        if heads[2]:
+            unsynced = True
+        oldhs = set(heads[0])
+        newhs = set(heads[1])
         dhs = None
         if len(newhs) > len(oldhs):
-            # strip updates to existing remote heads from the new heads list
             remotebookmarks = remote.listkeys('bookmarks')
             bookmarkedheads = set()
             for bm in localbookmarks:
                 rnode = remotebookmarks.get(bm)
                 if rnode and rnode in repo:
                     lctx, rctx = repo[bm], repo[rnode]
                     if rctx == lctx.ancestor(rctx):
                         bookmarkedheads.add(lctx.node())
+            # strip updates to existing remote heads from the new heads list
             dhs = list(newhs - bookmarkedheads - oldhs)
         if dhs:
             if error is None:
                 if branch not in ('default', None):
                     error = _("push creates new remote head %s "
                               "on branch '%s'!") % (short(dhs[0]), branch)
                 else:
                     error = _("push creates new remote head %s!"
                               ) % short(dhs[0])
-                if branch in unsynced:
+                if heads[2]: # unsynced
                     hint = _("you should pull and merge or "
                              "use push -f to force")
                 else:
                     hint = _("did you forget to merge? "
                              "use push -f to force")


More information about the Mercurial-devel mailing list