comparison mercurial/exchange.py @ 38791:7e66e7999bdd

exchange: move _computeellipsis() from narrow This is also referenced as part of the narrow changegroup code and therefore needs to move to core before we can integrate the narrow changegroup code into core. Differential Revision: https://phab.mercurial-scm.org/D4009
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 02 Jul 2018 18:32:20 -0700
parents 3e7387337a3c
children afb442f58cbf
comparison
equal deleted inserted replaced
38790:3e7387337a3c 38791:7e66e7999bdd
13 from .i18n import _ 13 from .i18n import _
14 from .node import ( 14 from .node import (
15 bin, 15 bin,
16 hex, 16 hex,
17 nullid, 17 nullid,
18 nullrev,
18 ) 19 )
19 from .thirdparty import ( 20 from .thirdparty import (
20 attr, 21 attr,
21 ) 22 )
22 from . import ( 23 from . import (
23 bookmarks as bookmod, 24 bookmarks as bookmod,
24 bundle2, 25 bundle2,
25 changegroup, 26 changegroup,
27 dagutil,
26 discovery, 28 discovery,
27 error, 29 error,
28 lock as lockmod, 30 lock as lockmod,
29 logexchange, 31 logexchange,
30 narrowspec, 32 narrowspec,
1873 new_args['includepats'] = req_includes 1875 new_args['includepats'] = req_includes
1874 if req_excludes: 1876 if req_excludes:
1875 new_args['excludepats'] = req_excludes 1877 new_args['excludepats'] = req_excludes
1876 return new_args 1878 return new_args
1877 1879
1880 def _computeellipsis(repo, common, heads, known, match, depth=None):
1881 """Compute the shape of a narrowed DAG.
1882
1883 Args:
1884 repo: The repository we're transferring.
1885 common: The roots of the DAG range we're transferring.
1886 May be just [nullid], which means all ancestors of heads.
1887 heads: The heads of the DAG range we're transferring.
1888 match: The narrowmatcher that allows us to identify relevant changes.
1889 depth: If not None, only consider nodes to be full nodes if they are at
1890 most depth changesets away from one of heads.
1891
1892 Returns:
1893 A tuple of (visitnodes, relevant_nodes, ellipsisroots) where:
1894
1895 visitnodes: The list of nodes (either full or ellipsis) which
1896 need to be sent to the client.
1897 relevant_nodes: The set of changelog nodes which change a file inside
1898 the narrowspec. The client needs these as non-ellipsis nodes.
1899 ellipsisroots: A dict of {rev: parents} that is used in
1900 narrowchangegroup to produce ellipsis nodes with the
1901 correct parents.
1902 """
1903 cl = repo.changelog
1904 mfl = repo.manifestlog
1905
1906 cldag = dagutil.revlogdag(cl)
1907 # dagutil does not like nullid/nullrev
1908 commonrevs = cldag.internalizeall(common - set([nullid])) | set([nullrev])
1909 headsrevs = cldag.internalizeall(heads)
1910 if depth:
1911 revdepth = {h: 0 for h in headsrevs}
1912
1913 ellipsisheads = collections.defaultdict(set)
1914 ellipsisroots = collections.defaultdict(set)
1915
1916 def addroot(head, curchange):
1917 """Add a root to an ellipsis head, splitting heads with 3 roots."""
1918 ellipsisroots[head].add(curchange)
1919 # Recursively split ellipsis heads with 3 roots by finding the
1920 # roots' youngest common descendant which is an elided merge commit.
1921 # That descendant takes 2 of the 3 roots as its own, and becomes a
1922 # root of the head.
1923 while len(ellipsisroots[head]) > 2:
1924 child, roots = splithead(head)
1925 splitroots(head, child, roots)
1926 head = child # Recurse in case we just added a 3rd root
1927
1928 def splitroots(head, child, roots):
1929 ellipsisroots[head].difference_update(roots)
1930 ellipsisroots[head].add(child)
1931 ellipsisroots[child].update(roots)
1932 ellipsisroots[child].discard(child)
1933
1934 def splithead(head):
1935 r1, r2, r3 = sorted(ellipsisroots[head])
1936 for nr1, nr2 in ((r2, r3), (r1, r3), (r1, r2)):
1937 mid = repo.revs('sort(merge() & %d::%d & %d::%d, -rev)',
1938 nr1, head, nr2, head)
1939 for j in mid:
1940 if j == nr2:
1941 return nr2, (nr1, nr2)
1942 if j not in ellipsisroots or len(ellipsisroots[j]) < 2:
1943 return j, (nr1, nr2)
1944 raise error.Abort(_('Failed to split up ellipsis node! head: %d, '
1945 'roots: %d %d %d') % (head, r1, r2, r3))
1946
1947 missing = list(cl.findmissingrevs(common=commonrevs, heads=headsrevs))
1948 visit = reversed(missing)
1949 relevant_nodes = set()
1950 visitnodes = [cl.node(m) for m in missing]
1951 required = set(headsrevs) | known
1952 for rev in visit:
1953 clrev = cl.changelogrevision(rev)
1954 ps = cldag.parents(rev)
1955 if depth is not None:
1956 curdepth = revdepth[rev]
1957 for p in ps:
1958 revdepth[p] = min(curdepth + 1, revdepth.get(p, depth + 1))
1959 needed = False
1960 shallow_enough = depth is None or revdepth[rev] <= depth
1961 if shallow_enough:
1962 curmf = mfl[clrev.manifest].read()
1963 if ps:
1964 # We choose to not trust the changed files list in
1965 # changesets because it's not always correct. TODO: could
1966 # we trust it for the non-merge case?
1967 p1mf = mfl[cl.changelogrevision(ps[0]).manifest].read()
1968 needed = bool(curmf.diff(p1mf, match))
1969 if not needed and len(ps) > 1:
1970 # For merge changes, the list of changed files is not
1971 # helpful, since we need to emit the merge if a file
1972 # in the narrow spec has changed on either side of the
1973 # merge. As a result, we do a manifest diff to check.
1974 p2mf = mfl[cl.changelogrevision(ps[1]).manifest].read()
1975 needed = bool(curmf.diff(p2mf, match))
1976 else:
1977 # For a root node, we need to include the node if any
1978 # files in the node match the narrowspec.
1979 needed = any(curmf.walk(match))
1980
1981 if needed:
1982 for head in ellipsisheads[rev]:
1983 addroot(head, rev)
1984 for p in ps:
1985 required.add(p)
1986 relevant_nodes.add(cl.node(rev))
1987 else:
1988 if not ps:
1989 ps = [nullrev]
1990 if rev in required:
1991 for head in ellipsisheads[rev]:
1992 addroot(head, rev)
1993 for p in ps:
1994 ellipsisheads[p].add(rev)
1995 else:
1996 for p in ps:
1997 ellipsisheads[p] |= ellipsisheads[rev]
1998
1999 # add common changesets as roots of their reachable ellipsis heads
2000 for c in commonrevs:
2001 for head in ellipsisheads[c]:
2002 addroot(head, c)
2003 return visitnodes, relevant_nodes, ellipsisroots
2004
1878 def caps20to10(repo, role): 2005 def caps20to10(repo, role):
1879 """return a set with appropriate options to use bundle20 during getbundle""" 2006 """return a set with appropriate options to use bundle20 during getbundle"""
1880 caps = {'HG20'} 2007 caps = {'HG20'}
1881 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=role)) 2008 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role=role))
1882 caps.add('bundle2=' + urlreq.quote(capsblob)) 2009 caps.add('bundle2=' + urlreq.quote(capsblob))