[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