Mercurial > public > mercurial-scm > hg
comparison mercurial/localrepo.py @ 19203:627cd7842e5d
bundle-ng: unify _changegroup and _changegroupsubset
Refactor the two changegroup generation interface to share as much code as
possible.
author | Benoit Boissinot <benoit.boissinot@ens-lyon.org> |
---|---|
date | Fri, 10 May 2013 22:20:32 +0200 |
parents | 0455fc94ae00 |
children | e9c5b1c246dc |
comparison
equal
deleted
inserted
replaced
19202:0455fc94ae00 | 19203:627cd7842e5d |
---|---|
1843 # create a changegroup from local | 1843 # create a changegroup from local |
1844 if revs is None and not outgoing.excluded: | 1844 if revs is None and not outgoing.excluded: |
1845 # push everything, | 1845 # push everything, |
1846 # use the fast path, no race possible on push | 1846 # use the fast path, no race possible on push |
1847 bundler = changegroup.bundle10(self, bundlecaps) | 1847 bundler = changegroup.bundle10(self, bundlecaps) |
1848 cg = self._changegroup(outgoing.missing, bundler, | 1848 cg = self._changegroupsubset(outgoing, |
1849 'push') | 1849 bundler, |
1850 'push', | |
1851 fastpath=True) | |
1850 else: | 1852 else: |
1851 cg = self.getlocalbundle('push', outgoing, bundlecaps) | 1853 cg = self.getlocalbundle('push', outgoing, bundlecaps) |
1852 | 1854 |
1853 # apply changegroup to remote | 1855 # apply changegroup to remote |
1854 if unbundle: | 1856 if unbundle: |
1987 the changegroup a particular filenode or manifestnode belongs to. | 1989 the changegroup a particular filenode or manifestnode belongs to. |
1988 """ | 1990 """ |
1989 cl = self.changelog | 1991 cl = self.changelog |
1990 if not bases: | 1992 if not bases: |
1991 bases = [nullid] | 1993 bases = [nullid] |
1994 # TODO: remove call to nodesbetween. | |
1992 csets, bases, heads = cl.nodesbetween(bases, heads) | 1995 csets, bases, heads = cl.nodesbetween(bases, heads) |
1993 # We assume that all ancestors of bases are known | 1996 bases = [p for n in bases for p in cl.parents(n) if p != nullid] |
1994 common = cl.ancestors([cl.rev(n) for n in bases]) | 1997 outgoing = discovery.outgoing(cl, bases, heads) |
1995 bundler = changegroup.bundle10(self) | 1998 bundler = changegroup.bundle10(self) |
1996 return self._changegroupsubset(common, csets, heads, bundler, source) | 1999 return self._changegroupsubset(outgoing, bundler, source) |
1997 | 2000 |
1998 def getlocalbundle(self, source, outgoing, bundlecaps=None): | 2001 def getlocalbundle(self, source, outgoing, bundlecaps=None): |
1999 """Like getbundle, but taking a discovery.outgoing as an argument. | 2002 """Like getbundle, but taking a discovery.outgoing as an argument. |
2000 | 2003 |
2001 This is only implemented for local repos and reuses potentially | 2004 This is only implemented for local repos and reuses potentially |
2002 precomputed sets in outgoing.""" | 2005 precomputed sets in outgoing.""" |
2003 if not outgoing.missing: | 2006 if not outgoing.missing: |
2004 return None | 2007 return None |
2005 bundler = changegroup.bundle10(self, bundlecaps) | 2008 bundler = changegroup.bundle10(self, bundlecaps) |
2006 return self._changegroupsubset(outgoing.common, | 2009 return self._changegroupsubset(outgoing, bundler, source) |
2007 outgoing.missing, | |
2008 outgoing.missingheads, | |
2009 bundler, | |
2010 source) | |
2011 | 2010 |
2012 def getbundle(self, source, heads=None, common=None, bundlecaps=None): | 2011 def getbundle(self, source, heads=None, common=None, bundlecaps=None): |
2013 """Like changegroupsubset, but returns the set difference between the | 2012 """Like changegroupsubset, but returns the set difference between the |
2014 ancestors of heads and the ancestors common. | 2013 ancestors of heads and the ancestors common. |
2015 | 2014 |
2029 return self.getlocalbundle(source, | 2028 return self.getlocalbundle(source, |
2030 discovery.outgoing(cl, common, heads), | 2029 discovery.outgoing(cl, common, heads), |
2031 bundlecaps=bundlecaps) | 2030 bundlecaps=bundlecaps) |
2032 | 2031 |
2033 @unfilteredmethod | 2032 @unfilteredmethod |
2034 def _changegroupsubset(self, commonrevs, csets, heads, bundler, source): | 2033 def _changegroupsubset(self, outgoing, bundler, source, |
2035 | 2034 fastpath=False): |
2035 commonrevs = outgoing.common | |
2036 csets = outgoing.missing | |
2037 heads = outgoing.missingheads | |
2036 cl = bundler._changelog | 2038 cl = bundler._changelog |
2037 mf = bundler._manifest | 2039 mf = bundler._manifest |
2038 mfs = {} # needed manifests | 2040 mfs = {} # needed manifests |
2039 fnodes = {} # needed file nodes | 2041 fnodes = {} # needed file nodes |
2040 changedfiles = set() | 2042 changedfiles = set() |
2041 fstate = ['', {}] | 2043 fstate = ['', {}] |
2042 | 2044 |
2043 # can we go through the fast path ? | 2045 # We go through the fast path if we get told to, or if all (unfiltered |
2046 # heads have been requested (since we then know there all linkrevs will | |
2047 # be pulled by the client). | |
2044 heads.sort() | 2048 heads.sort() |
2045 if heads == sorted(self.heads()): | 2049 fastpathlinkrev = fastpath or ( |
2046 return self._changegroup(csets, bundler, source) | 2050 self.filtername is None and heads == sorted(self.heads())) |
2047 | 2051 |
2048 # slow path | |
2049 self.hook('preoutgoing', throw=True, source=source) | 2052 self.hook('preoutgoing', throw=True, source=source) |
2050 self.changegroupinfo(csets, source) | 2053 self.changegroupinfo(csets, source) |
2051 | 2054 |
2052 # filter any nodes that claim to be part of the known set | 2055 # filter any nodes that claim to be part of the known set |
2053 def prune(revlog, missing): | 2056 def prune(revlog, missing): |
2071 progress(_bundling, count[0], | 2074 progress(_bundling, count[0], |
2072 unit=_changesets, total=count[1]) | 2075 unit=_changesets, total=count[1]) |
2073 return x | 2076 return x |
2074 elif revlog == mf: | 2077 elif revlog == mf: |
2075 clnode = mfs[x] | 2078 clnode = mfs[x] |
2076 mdata = mf.readfast(x) | 2079 if not fastpathlinkrev: |
2077 for f, n in mdata.iteritems(): | 2080 mdata = mf.readfast(x) |
2078 if f in changedfiles: | 2081 for f, n in mdata.iteritems(): |
2079 fnodes[f].setdefault(n, clnode) | 2082 if f in changedfiles: |
2083 fnodes[f].setdefault(n, clnode) | |
2080 count[0] += 1 | 2084 count[0] += 1 |
2081 progress(_bundling, count[0], | 2085 progress(_bundling, count[0], |
2082 unit=_manifests, total=count[1]) | 2086 unit=_manifests, total=count[1]) |
2083 return clnode | 2087 return clnode |
2084 else: | 2088 else: |
2095 return prune(mf, mfs) | 2099 return prune(mf, mfs) |
2096 def getfiles(): | 2100 def getfiles(): |
2097 mfs.clear() | 2101 mfs.clear() |
2098 return changedfiles | 2102 return changedfiles |
2099 def getfilenodes(fname, filerevlog): | 2103 def getfilenodes(fname, filerevlog): |
2104 if fastpathlinkrev: | |
2105 ln, llr = filerevlog.node, filerevlog.linkrev | |
2106 def genfilenodes(): | |
2107 for r in filerevlog: | |
2108 linkrev = llr(r) | |
2109 if linkrev not in commonrevs: | |
2110 yield filerevlog.node(r), cl.node(linkrev) | |
2111 fnodes[fname] = dict(genfilenodes()) | |
2100 fstate[0] = fname | 2112 fstate[0] = fname |
2101 fstate[1] = fnodes.pop(fname, {}) | 2113 fstate[1] = fnodes.pop(fname, {}) |
2102 return prune(filerevlog, fstate[1]) | 2114 return prune(filerevlog, fstate[1]) |
2103 | 2115 |
2104 gengroup = bundler.generate(csets, getmfnodes, getfiles, getfilenodes, | 2116 gengroup = bundler.generate(csets, getmfnodes, getfiles, getfilenodes, |
2106 return changegroup.unbundle10(util.chunkbuffer(gengroup), 'UN') | 2118 return changegroup.unbundle10(util.chunkbuffer(gengroup), 'UN') |
2107 | 2119 |
2108 def changegroup(self, basenodes, source): | 2120 def changegroup(self, basenodes, source): |
2109 # to avoid a race we use changegroupsubset() (issue1320) | 2121 # to avoid a race we use changegroupsubset() (issue1320) |
2110 return self.changegroupsubset(basenodes, self.heads(), source) | 2122 return self.changegroupsubset(basenodes, self.heads(), source) |
2111 | |
2112 @unfilteredmethod | |
2113 def _changegroup(self, nodes, bundler, source): | |
2114 """Compute the changegroup of all nodes that we have that a recipient | |
2115 doesn't. Return a chunkbuffer object whose read() method will return | |
2116 successive changegroup chunks. | |
2117 | |
2118 This is much easier than the previous function as we can assume that | |
2119 the recipient has any changenode we aren't sending them. | |
2120 | |
2121 nodes is the set of nodes to send""" | |
2122 | |
2123 cl = bundler._changelog | |
2124 mf = bundler._manifest | |
2125 mfs = {} | |
2126 changedfiles = set() | |
2127 fstate = [''] | |
2128 | |
2129 self.hook('preoutgoing', throw=True, source=source) | |
2130 self.changegroupinfo(nodes, source) | |
2131 | |
2132 revset = set([cl.rev(n) for n in nodes]) | |
2133 | |
2134 def gennodelst(log): | |
2135 ln, llr = log.node, log.linkrev | |
2136 return [ln(r) for r in log if llr(r) in revset] | |
2137 | |
2138 progress = self.ui.progress | |
2139 _bundling = _('bundling') | |
2140 _changesets = _('changesets') | |
2141 _manifests = _('manifests') | |
2142 _files = _('files') | |
2143 | |
2144 def lookup(revlog, x): | |
2145 count = bundler.count | |
2146 if revlog == cl: | |
2147 c = cl.read(x) | |
2148 changedfiles.update(c[3]) | |
2149 mfs.setdefault(c[0], x) | |
2150 count[0] += 1 | |
2151 progress(_bundling, count[0], | |
2152 unit=_changesets, total=count[1]) | |
2153 return x | |
2154 elif revlog == mf: | |
2155 count[0] += 1 | |
2156 progress(_bundling, count[0], | |
2157 unit=_manifests, total=count[1]) | |
2158 return cl.node(revlog.linkrev(revlog.rev(x))) | |
2159 else: | |
2160 progress(_bundling, count[0], item=fstate[0], | |
2161 total=count[1], unit=_files) | |
2162 return cl.node(revlog.linkrev(revlog.rev(x))) | |
2163 | |
2164 bundler.start(lookup) | |
2165 | |
2166 def getmfnodes(): | |
2167 bundler.count[:] = [0, len(mfs)] | |
2168 return gennodelst(mf) | |
2169 def getfiles(): | |
2170 return changedfiles | |
2171 def getfilenodes(fname, filerevlog): | |
2172 fstate[0] = fname | |
2173 return gennodelst(filerevlog) | |
2174 | |
2175 gengroup = bundler.generate(nodes, getmfnodes, getfiles, getfilenodes, | |
2176 source) | |
2177 return changegroup.unbundle10(util.chunkbuffer(gengroup), 'UN') | |
2178 | 2123 |
2179 @unfilteredmethod | 2124 @unfilteredmethod |
2180 def addchangegroup(self, source, srctype, url, emptyok=False): | 2125 def addchangegroup(self, source, srctype, url, emptyok=False): |
2181 """Add the changegroup returned by source.read() to this repo. | 2126 """Add the changegroup returned by source.read() to this repo. |
2182 srctype is a string like 'push', 'pull', or 'unbundle'. url is | 2127 srctype is a string like 'push', 'pull', or 'unbundle'. url is |