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