[PATCH 09 of 10 V2] branchmap: extract _updatebranchcache from repo

pierre-yves.david at logilab.fr pierre-yves.david at logilab.fr
Thu Dec 20 08:08:39 CST 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at logilab.fr>
# Date 1355924946 -3600
# Node ID c00fe6d70cf45bc73a27d09b579a1bbffce2e0e8
# Parent  9609e1e17c9ef70cbaeac49bf3a30d823a626382
branchmap: extract _updatebranchcache from repo

diff --git a/mercurial/branchmap.py b/mercurial/branchmap.py
--- a/mercurial/branchmap.py
+++ b/mercurial/branchmap.py
@@ -48,5 +48,66 @@ def write(repo, branches, tip, tiprev):
             for node in nodes:
                 f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
         f.close()
     except (IOError, OSError):
         pass
+
+def update(repo, partial, ctxgen):
+    """Given a branchhead cache, partial, that may have extra nodes or be
+    missing heads, and a generator of nodes that are at least a superset of
+    heads missing, this function updates partial to be correct.
+    """
+    # collect new branch entries
+    newbranches = {}
+    for c in ctxgen:
+        newbranches.setdefault(c.branch(), []).append(c.node())
+    # if older branchheads are reachable from new ones, they aren't
+    # really branchheads. Note checking parents is insufficient:
+    # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
+    for branch, newnodes in newbranches.iteritems():
+        bheads = partial.setdefault(branch, [])
+        # Remove candidate heads that no longer are in the repo (e.g., as
+        # the result of a strip that just happened).  Avoid using 'node in
+        # self' here because that dives down into branchcache code somewhat
+        # recursively.
+        bheadrevs = [repo.changelog.rev(node) for node in bheads
+                     if repo.changelog.hasnode(node)]
+        newheadrevs = [repo.changelog.rev(node) for node in newnodes
+                       if repo.changelog.hasnode(node)]
+        ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
+        # Remove duplicates - nodes that are in newheadrevs and are already
+        # in bheadrevs.  This can happen if you strip a node whose parent
+        # was already a head (because they're on different branches).
+        bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
+
+        # Starting from tip means fewer passes over reachable.  If we know
+        # the new candidates are not ancestors of existing heads, we don't
+        # have to examine ancestors of existing heads
+        if ctxisnew:
+            iterrevs = sorted(newheadrevs)
+        else:
+            iterrevs = list(bheadrevs)
+
+        # This loop prunes out two kinds of heads - heads that are
+        # superseded by a head in newheadrevs, and newheadrevs that are not
+        # heads because an existing head is their descendant.
+        while iterrevs:
+            latest = iterrevs.pop()
+            if latest not in bheadrevs:
+                continue
+            ancestors = set(repo.changelog.ancestors([latest],
+                                                     bheadrevs[0]))
+            if ancestors:
+                bheadrevs = [b for b in bheadrevs if b not in ancestors]
+        partial[branch] = [repo.changelog.node(rev) for rev in bheadrevs]
+
+    # There may be branches that cease to exist when the last commit in the
+    # branch was stripped.  This code filters them out.  Note that the
+    # branch that ceased to exist may not be in newbranches because
+    # newbranches is the set of candidate heads, which when you strip the
+    # last commit in a branch will be the parent branch.
+    for branch in partial.keys():
+        nodes = [head for head in partial[branch]
+                 if repo.changelog.hasnode(head)]
+        if not nodes:
+            del partial[branch]
+
diff --git a/mercurial/discovery.py b/mercurial/discovery.py
--- a/mercurial/discovery.py
+++ b/mercurial/discovery.py
@@ -6,10 +6,11 @@
 # GNU General Public License version 2 or any later version.
 
 from node import nullid, short
 from i18n import _
 import util, setdiscovery, treediscovery, phases, obsolete, bookmarks
+import branchmap
 
 def findcommonincoming(repo, remote, heads=None, force=False):
     """Return a tuple (common, anyincoming, heads) used to identify the common
     subset of nodes between repo and remote.
 
@@ -192,22 +193,22 @@ def _headssummary(repo, remote, outgoing
 
     # D. Update newmap with outgoing changes.
     # This will possibly add new heads and remove existing ones.
     newmap = dict((branch, heads[1]) for branch, heads in headssum.iteritems()
                   if heads[0] is not None)
-    repo._updatebranchcache(newmap, missingctx)
+    branchmap.update(repo, newmap, missingctx)
     for branch, newheads in newmap.iteritems():
         headssum[branch][1][:] = newheads
     return headssum
 
 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).
-    # (code based on _updatebranchcache)
+    # (code based on update)
     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
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -663,30 +663,30 @@ class localrepository(object):
         # if lrev == catip: cache is already up to date
         # if lrev >  catip: we have uncachable element in `partial` can't write
         #                   on disk
         if lrev < catip:
             ctxgen = (self[r] for r in cl.revs(lrev + 1, catip))
-            self._updatebranchcache(partial, ctxgen)
+            branchmap.update(self, partial, ctxgen)
             branchmap.write(self, partial, cl.node(catip), catip)
             lrev = catip
         # If cacheable tip were lower than actual tip, we need to update the
         # cache up to tip. This update (from cacheable to actual tip) is not
         # written to disk since it's not cacheable.
         tiprev = len(self) - 1
         if lrev < tiprev:
             ctxgen = (self[r] for r in cl.revs(lrev + 1, tiprev))
-            self._updatebranchcache(partial, ctxgen)
+            branchmap.update(self, partial, ctxgen)
         self._branchcache = partial
         self._branchcachetip = tip
 
     def branchmap(self):
         '''returns a dictionary {branch: [branchheads]}'''
         if self.changelog.filteredrevs:
             # some changeset are excluded we can't use the cache
-            branchmap = {}
-            self._updatebranchcache(branchmap, (self[r] for r in self))
-            return branchmap
+            bmap = {}
+            branchmap.update(self, bmap, (self[r] for r in self))
+            return bmap
         else:
             self.updatebranchcache()
             return self._branchcache
 
 
@@ -711,70 +711,10 @@ class localrepository(object):
         bt = {}
         for bn, heads in self.branchmap().iteritems():
             bt[bn] = self._branchtip(heads)
         return bt
 
-    def _updatebranchcache(self, partial, ctxgen):
-        """Given a branchhead cache, partial, that may have extra nodes or be
-        missing heads, and a generator of nodes that are at least a superset of
-        heads missing, this function updates partial to be correct.
-        """
-        # collect new branch entries
-        newbranches = {}
-        for c in ctxgen:
-            newbranches.setdefault(c.branch(), []).append(c.node())
-        # if older branchheads are reachable from new ones, they aren't
-        # really branchheads. Note checking parents is insufficient:
-        # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
-        for branch, newnodes in newbranches.iteritems():
-            bheads = partial.setdefault(branch, [])
-            # Remove candidate heads that no longer are in the repo (e.g., as
-            # the result of a strip that just happened).  Avoid using 'node in
-            # self' here because that dives down into branchcache code somewhat
-            # recursively.
-            bheadrevs = [self.changelog.rev(node) for node in bheads
-                         if self.changelog.hasnode(node)]
-            newheadrevs = [self.changelog.rev(node) for node in newnodes
-                           if self.changelog.hasnode(node)]
-            ctxisnew = bheadrevs and min(newheadrevs) > max(bheadrevs)
-            # Remove duplicates - nodes that are in newheadrevs and are already
-            # in bheadrevs.  This can happen if you strip a node whose parent
-            # was already a head (because they're on different branches).
-            bheadrevs = sorted(set(bheadrevs).union(newheadrevs))
-
-            # Starting from tip means fewer passes over reachable.  If we know
-            # the new candidates are not ancestors of existing heads, we don't
-            # have to examine ancestors of existing heads
-            if ctxisnew:
-                iterrevs = sorted(newheadrevs)
-            else:
-                iterrevs = list(bheadrevs)
-
-            # This loop prunes out two kinds of heads - heads that are
-            # superseded by a head in newheadrevs, and newheadrevs that are not
-            # heads because an existing head is their descendant.
-            while iterrevs:
-                latest = iterrevs.pop()
-                if latest not in bheadrevs:
-                    continue
-                ancestors = set(self.changelog.ancestors([latest],
-                                                         bheadrevs[0]))
-                if ancestors:
-                    bheadrevs = [b for b in bheadrevs if b not in ancestors]
-            partial[branch] = [self.changelog.node(rev) for rev in bheadrevs]
-
-        # There may be branches that cease to exist when the last commit in the
-        # branch was stripped.  This code filters them out.  Note that the
-        # branch that ceased to exist may not be in newbranches because
-        # newbranches is the set of candidate heads, which when you strip the
-        # last commit in a branch will be the parent branch.
-        for branch in partial.keys():
-            nodes = [head for head in partial[branch]
-                     if self.changelog.hasnode(head)]
-            if not nodes:
-                del partial[branch]
-
     def lookup(self, key):
         return self[key].node()
 
     def lookupbranch(self, key, remote=None):
         repo = remote or self
@@ -1507,11 +1447,11 @@ class localrepository(object):
         # will be caught the next time it is read.
         if newheadnodes:
             tiprev = len(self) - 1
             ctxgen = (self[node] for node in newheadnodes
                       if self.changelog.hasnode(node))
-            self._updatebranchcache(self._branchcache, ctxgen)
+            branchmap.update(self, self._branchcache, ctxgen)
             branchmap.write(self, self._branchcache, self.changelog.tip(),
                             tiprev)
 
         # Ensure the persistent tag cache is updated.  Doing it now
         # means that the tag cache only has to worry about destroyed


More information about the Mercurial-devel mailing list