D4009: exchange: move _computeellipsis() from narrow
indygreg (Gregory Szorc)
phabricator at mercurial-scm.org
Wed Aug 1 17:25:34 EDT 2018
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGea9834aa017a: exchange: move _computeellipsis() from narrow (authored by indygreg, committed by ).
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D4009?vs=9701&id=9749
REVISION DETAIL
https://phab.mercurial-scm.org/D4009
AFFECTED FILES
hgext/narrow/narrowbundle2.py
mercurial/exchange.py
CHANGE DETAILS
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -15,14 +15,16 @@
bin,
hex,
nullid,
+ nullrev,
)
from .thirdparty import (
attr,
)
from . import (
bookmarks as bookmod,
bundle2,
changegroup,
+ dagutil,
discovery,
error,
lock as lockmod,
@@ -1875,6 +1877,131 @@
new_args['excludepats'] = req_excludes
return new_args
+def _computeellipsis(repo, common, heads, known, match, depth=None):
+ """Compute the shape of a narrowed DAG.
+
+ Args:
+ repo: The repository we're transferring.
+ common: The roots of the DAG range we're transferring.
+ May be just [nullid], which means all ancestors of heads.
+ heads: The heads of the DAG range we're transferring.
+ match: The narrowmatcher that allows us to identify relevant changes.
+ depth: If not None, only consider nodes to be full nodes if they are at
+ most depth changesets away from one of heads.
+
+ Returns:
+ A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
+
+ visitnodes: The list of nodes (either full or ellipsis) which
+ need to be sent to the client.
+ relevant_nodes: The set of changelog nodes which change a file inside
+ the narrowspec. The client needs these as non-ellipsis nodes.
+ ellipsisroots: A dict of {rev: parents} that is used in
+ narrowchangegroup to produce ellipsis nodes with the
+ correct parents.
+ """
+ cl = repo.changelog
+ mfl = repo.manifestlog
+
+ cldag = dagutil.revlogdag(cl)
+ # dagutil does not like nullid/nullrev
+ commonrevs = cldag.internalizeall(common - set([nullid])) | set([nullrev])
+ headsrevs = cldag.internalizeall(heads)
+ if depth:
+ revdepth = {h: 0 for h in headsrevs}
+
+ ellipsisheads = collections.defaultdict(set)
+ ellipsisroots = collections.defaultdict(set)
+
+ def addroot(head, curchange):
+ """Add a root to an ellipsis head, splitting heads with 3 roots."""
+ ellipsisroots[head].add(curchange)
+ # Recursively split ellipsis heads with 3 roots by finding the
+ # roots' youngest common descendant which is an elided merge commit.
+ # That descendant takes 2 of the 3 roots as its own, and becomes a
+ # root of the head.
+ while len(ellipsisroots[head]) > 2:
+ child, roots = splithead(head)
+ splitroots(head, child, roots)
+ head = child # Recurse in case we just added a 3rd root
+
+ def splitroots(head, child, roots):
+ ellipsisroots[head].difference_update(roots)
+ ellipsisroots[head].add(child)
+ ellipsisroots[child].update(roots)
+ ellipsisroots[child].discard(child)
+
+ def splithead(head):
+ r1, r2, r3 = sorted(ellipsisroots[head])
+ for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
+ mid = repo.revs('sort(merge() & %d::%d & %d::%d, -rev)',
+ nr1, head, nr2, head)
+ for j in mid:
+ if j == nr2:
+ return nr2, (nr1, nr2)
+ if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
+ return j, (nr1, nr2)
+ raise error.Abort(_('Failed to split up ellipsis node! head: %d, '
+ 'roots: %d %d %d') % (head, r1, r2, r3))
+
+ missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
+ visit = reversed(missing)
+ relevant_nodes = set()
+ visitnodes = [cl.node(m) for m in missing]
+ required = set(headsrevs) | known
+ for rev in visit:
+ clrev = cl.changelogrevision(rev)
+ ps = cldag.parents(rev)
+ if depth is not None:
+ curdepth = revdepth[rev]
+ for p in ps:
+ revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
+ needed = False
+ shallow_enough = depth is None or revdepth[rev] <= depth
+ if shallow_enough:
+ curmf = mfl[clrev.manifest].read()
+ if ps:
+ # We choose to not trust the changed files list in
+ # changesets because it's not always correct. TODO: could
+ # we trust it for the non-merge case?
+ p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
+ needed = bool(curmf.diff(p1mf, match))
+ if not needed and len(ps) > 1:
+ # For merge changes, the list of changed files is not
+ # helpful, since we need to emit the merge if a file
+ # in the narrow spec has changed on either side of the
+ # merge. As a result, we do a manifest diff to check.
+ p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
+ needed = bool(curmf.diff(p2mf, match))
+ else:
+ # For a root node, we need to include the node if any
+ # files in the node match the narrowspec.
+ needed = any(curmf.walk(match))
+
+ if needed:
+ for head in ellipsisheads[rev]:
+ addroot(head, rev)
+ for p in ps:
+ required.add(p)
+ relevant_nodes.add(cl.node(rev))
+ else:
+ if not ps:
+ ps = [nullrev]
+ if rev in required:
+ for head in ellipsisheads[rev]:
+ addroot(head, rev)
+ for p in ps:
+ ellipsisheads[p].add(rev)
+ else:
+ for p in ps:
+ ellipsisheads[p] |= ellipsisheads[rev]
+
+ # add common changesets as roots of their reachable ellipsis heads
+ for c in commonrevs:
+ for head in ellipsisheads[c]:
+ addroot(head, c)
+ return visitnodes, relevant_nodes, ellipsisroots
+
def caps20to10(repo, role):
"""return a set with appropriate options to use bundle20 during getbundle"""
caps = {'HG20'}
diff --git a/hgext/narrow/narrowbundle2.py b/hgext/narrow/narrowbundle2.py
--- a/hgext/narrow/narrowbundle2.py
+++ b/hgext/narrow/narrowbundle2.py
@@ -7,20 +7,17 @@
from __future__ import absolute_import
-import collections
import errno
import struct
from mercurial.i18n import _
from mercurial.node import (
bin,
nullid,
- nullrev,
)
from mercurial import (
bundle2,
changegroup,
- dagutil,
error,
exchange,
extensions,
@@ -52,131 +49,6 @@
caps[NARROWCAP] = ['v0']
return caps
-def _computeellipsis(repo, common, heads, known, match, depth=None):
- """Compute the shape of a narrowed DAG.
-
- Args:
- repo: The repository we're transferring.
- common: The roots of the DAG range we're transferring.
- May be just [nullid], which means all ancestors of heads.
- heads: The heads of the DAG range we're transferring.
- match: The narrowmatcher that allows us to identify relevant changes.
- depth: If not None, only consider nodes to be full nodes if they are at
- most depth changesets away from one of heads.
-
- Returns:
- A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
-
- visitnodes: The list of nodes (either full or ellipsis) which
- need to be sent to the client.
- relevant_nodes: The set of changelog nodes which change a file inside
- the narrowspec. The client needs these as non-ellipsis nodes.
- ellipsisroots: A dict of {rev: parents} that is used in
- narrowchangegroup to produce ellipsis nodes with the
- correct parents.
- """
- cl = repo.changelog
- mfl = repo.manifestlog
-
- cldag = dagutil.revlogdag(cl)
- # dagutil does not like nullid/nullrev
- commonrevs = cldag.internalizeall(common - set([nullid])) | set([nullrev])
- headsrevs = cldag.internalizeall(heads)
- if depth:
- revdepth = {h: 0 for h in headsrevs}
-
- ellipsisheads = collections.defaultdict(set)
- ellipsisroots = collections.defaultdict(set)
-
- def addroot(head, curchange):
- """Add a root to an ellipsis head, splitting heads with 3 roots."""
- ellipsisroots[head].add(curchange)
- # Recursively split ellipsis heads with 3 roots by finding the
- # roots' youngest common descendant which is an elided merge commit.
- # That descendant takes 2 of the 3 roots as its own, and becomes a
- # root of the head.
- while len(ellipsisroots[head]) > 2:
- child, roots = splithead(head)
- splitroots(head, child, roots)
- head = child # Recurse in case we just added a 3rd root
-
- def splitroots(head, child, roots):
- ellipsisroots[head].difference_update(roots)
- ellipsisroots[head].add(child)
- ellipsisroots[child].update(roots)
- ellipsisroots[child].discard(child)
-
- def splithead(head):
- r1, r2, r3 = sorted(ellipsisroots[head])
- for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
- mid = repo.revs('sort(merge() & %d::%d & %d::%d, -rev)',
- nr1, head, nr2, head)
- for j in mid:
- if j == nr2:
- return nr2, (nr1, nr2)
- if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
- return j, (nr1, nr2)
- raise error.Abort('Failed to split up ellipsis node! head: %d, '
- 'roots: %d %d %d' % (head, r1, r2, r3))
-
- missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
- visit = reversed(missing)
- relevant_nodes = set()
- visitnodes = [cl.node(m) for m in missing]
- required = set(headsrevs) | known
- for rev in visit:
- clrev = cl.changelogrevision(rev)
- ps = cldag.parents(rev)
- if depth is not None:
- curdepth = revdepth[rev]
- for p in ps:
- revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
- needed = False
- shallow_enough = depth is None or revdepth[rev] <= depth
- if shallow_enough:
- curmf = mfl[clrev.manifest].read()
- if ps:
- # We choose to not trust the changed files list in
- # changesets because it's not always correct. TODO: could
- # we trust it for the non-merge case?
- p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
- needed = bool(curmf.diff(p1mf, match))
- if not needed and len(ps) > 1:
- # For merge changes, the list of changed files is not
- # helpful, since we need to emit the merge if a file
- # in the narrow spec has changed on either side of the
- # merge. As a result, we do a manifest diff to check.
- p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
- needed = bool(curmf.diff(p2mf, match))
- else:
- # For a root node, we need to include the node if any
- # files in the node match the narrowspec.
- needed = any(curmf.walk(match))
-
- if needed:
- for head in ellipsisheads[rev]:
- addroot(head, rev)
- for p in ps:
- required.add(p)
- relevant_nodes.add(cl.node(rev))
- else:
- if not ps:
- ps = [nullrev]
- if rev in required:
- for head in ellipsisheads[rev]:
- addroot(head, rev)
- for p in ps:
- ellipsisheads[p].add(rev)
- else:
- for p in ps:
- ellipsisheads[p] |= ellipsisheads[rev]
-
- # add common changesets as roots of their reachable ellipsis heads
- for c in commonrevs:
- for head in ellipsisheads[c]:
- addroot(head, c)
- return visitnodes, relevant_nodes, ellipsisroots
-
def _packellipsischangegroup(repo, common, match, relevant_nodes,
ellipsisroots, visitnodes, depth, source, version):
if version in ('01', '02'):
@@ -300,7 +172,7 @@
yield repo.changelog.node(r)
yield _DONESIGNAL
bundler.newpart(_CHANGESPECPART, data=genkills())
- newvisit, newfull, newellipsis = _computeellipsis(
+ newvisit, newfull, newellipsis = exchange._computeellipsis(
repo, set(), common, known, newmatch)
if newvisit:
cg = _packellipsischangegroup(
@@ -311,7 +183,7 @@
if 'treemanifest' in repo.requirements:
part.addparam('treemanifest', '1')
- visitnodes, relevant_nodes, ellipsisroots = _computeellipsis(
+ visitnodes, relevant_nodes, ellipsisroots = exchange._computeellipsis(
repo, common, heads, set(), newmatch, depth=depth)
repo.ui.debug('Found %d relevant revs\n' % len(relevant_nodes))
To: indygreg, durin42, #hg-reviewers
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list