Mercurial > hg
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)) |