##// END OF EJS Templates
changegroup: fix deltachunk API to be consistent from one class to another...
marmoute -
r48131:3f00665b default
parent child Browse files
Show More
@@ -1,319 +1,339 b''
1 # shallowbundle.py - bundle10 implementation for use with shallow repositories
1 # shallowbundle.py - bundle10 implementation for use with shallow repositories
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 from __future__ import absolute_import
7 from __future__ import absolute_import
8
8
9 from mercurial.i18n import _
9 from mercurial.i18n import _
10 from mercurial.node import bin, hex
10 from mercurial.node import bin, hex
11 from mercurial import (
11 from mercurial import (
12 bundlerepo,
12 bundlerepo,
13 changegroup,
13 changegroup,
14 error,
14 error,
15 match,
15 match,
16 mdiff,
16 mdiff,
17 pycompat,
17 pycompat,
18 )
18 )
19 from . import (
19 from . import (
20 constants,
20 constants,
21 remotefilelog,
21 remotefilelog,
22 shallowutil,
22 shallowutil,
23 )
23 )
24
24
25 NoFiles = 0
25 NoFiles = 0
26 LocalFiles = 1
26 LocalFiles = 1
27 AllFiles = 2
27 AllFiles = 2
28
28
29
29
30 def shallowgroup(cls, self, nodelist, rlog, lookup, units=None, reorder=None):
30 def shallowgroup(cls, self, nodelist, rlog, lookup, units=None, reorder=None):
31 if not isinstance(rlog, remotefilelog.remotefilelog):
31 if not isinstance(rlog, remotefilelog.remotefilelog):
32 for c in super(cls, self).group(nodelist, rlog, lookup, units=units):
32 for c in super(cls, self).group(nodelist, rlog, lookup, units=units):
33 yield c
33 yield c
34 return
34 return
35
35
36 if len(nodelist) == 0:
36 if len(nodelist) == 0:
37 yield self.close()
37 yield self.close()
38 return
38 return
39
39
40 nodelist = shallowutil.sortnodes(nodelist, rlog.parents)
40 nodelist = shallowutil.sortnodes(nodelist, rlog.parents)
41
41
42 # add the parent of the first rev
42 # add the parent of the first rev
43 p = rlog.parents(nodelist[0])[0]
43 p = rlog.parents(nodelist[0])[0]
44 nodelist.insert(0, p)
44 nodelist.insert(0, p)
45
45
46 # build deltas
46 # build deltas
47 for i in pycompat.xrange(len(nodelist) - 1):
47 for i in pycompat.xrange(len(nodelist) - 1):
48 prev, curr = nodelist[i], nodelist[i + 1]
48 prev, curr = nodelist[i], nodelist[i + 1]
49 linknode = lookup(curr)
49 linknode = lookup(curr)
50 for c in self.nodechunk(rlog, curr, prev, linknode):
50 for c in self.nodechunk(rlog, curr, prev, linknode):
51 yield c
51 yield c
52
52
53 yield self.close()
53 yield self.close()
54
54
55
55
56 class shallowcg1packer(changegroup.cgpacker):
56 class shallowcg1packer(changegroup.cgpacker):
57 def generate(self, commonrevs, clnodes, fastpathlinkrev, source, **kwargs):
57 def generate(self, commonrevs, clnodes, fastpathlinkrev, source, **kwargs):
58 if shallowutil.isenabled(self._repo):
58 if shallowutil.isenabled(self._repo):
59 fastpathlinkrev = False
59 fastpathlinkrev = False
60
60
61 return super(shallowcg1packer, self).generate(
61 return super(shallowcg1packer, self).generate(
62 commonrevs, clnodes, fastpathlinkrev, source, **kwargs
62 commonrevs, clnodes, fastpathlinkrev, source, **kwargs
63 )
63 )
64
64
65 def group(self, nodelist, rlog, lookup, units=None, reorder=None):
65 def group(self, nodelist, rlog, lookup, units=None, reorder=None):
66 return shallowgroup(
66 return shallowgroup(
67 shallowcg1packer, self, nodelist, rlog, lookup, units=units
67 shallowcg1packer, self, nodelist, rlog, lookup, units=units
68 )
68 )
69
69
70 def generatefiles(self, changedfiles, *args, **kwargs):
70 def generatefiles(self, changedfiles, *args, **kwargs):
71 try:
71 try:
72 linknodes, commonrevs, source = args
72 linknodes, commonrevs, source = args
73 except ValueError:
73 except ValueError:
74 commonrevs, source, mfdicts, fastpathlinkrev, fnodes, clrevs = args
74 commonrevs, source, mfdicts, fastpathlinkrev, fnodes, clrevs = args
75 if shallowutil.isenabled(self._repo):
75 if shallowutil.isenabled(self._repo):
76 repo = self._repo
76 repo = self._repo
77 if isinstance(repo, bundlerepo.bundlerepository):
77 if isinstance(repo, bundlerepo.bundlerepository):
78 # If the bundle contains filelogs, we can't pull from it, since
78 # If the bundle contains filelogs, we can't pull from it, since
79 # bundlerepo is heavily tied to revlogs. Instead require that
79 # bundlerepo is heavily tied to revlogs. Instead require that
80 # the user use unbundle instead.
80 # the user use unbundle instead.
81 # Force load the filelog data.
81 # Force load the filelog data.
82 bundlerepo.bundlerepository.file(repo, b'foo')
82 bundlerepo.bundlerepository.file(repo, b'foo')
83 if repo._cgfilespos:
83 if repo._cgfilespos:
84 raise error.Abort(
84 raise error.Abort(
85 b"cannot pull from full bundles",
85 b"cannot pull from full bundles",
86 hint=b"use `hg unbundle` instead",
86 hint=b"use `hg unbundle` instead",
87 )
87 )
88 return []
88 return []
89 filestosend = self.shouldaddfilegroups(source)
89 filestosend = self.shouldaddfilegroups(source)
90 if filestosend == NoFiles:
90 if filestosend == NoFiles:
91 changedfiles = list(
91 changedfiles = list(
92 [f for f in changedfiles if not repo.shallowmatch(f)]
92 [f for f in changedfiles if not repo.shallowmatch(f)]
93 )
93 )
94
94
95 return super(shallowcg1packer, self).generatefiles(
95 return super(shallowcg1packer, self).generatefiles(
96 changedfiles, *args, **kwargs
96 changedfiles, *args, **kwargs
97 )
97 )
98
98
99 def shouldaddfilegroups(self, source):
99 def shouldaddfilegroups(self, source):
100 repo = self._repo
100 repo = self._repo
101 if not shallowutil.isenabled(repo):
101 if not shallowutil.isenabled(repo):
102 return AllFiles
102 return AllFiles
103
103
104 if source == b"push" or source == b"bundle":
104 if source == b"push" or source == b"bundle":
105 return AllFiles
105 return AllFiles
106
106
107 # We won't actually strip the files, but we should put them in any
107 # We won't actually strip the files, but we should put them in any
108 # backup bundle generated by strip (especially for cases like narrow's
108 # backup bundle generated by strip (especially for cases like narrow's
109 # `hg tracked --removeinclude`, as failing to do so means that the
109 # `hg tracked --removeinclude`, as failing to do so means that the
110 # "saved" changesets during a strip won't have their files reapplied and
110 # "saved" changesets during a strip won't have their files reapplied and
111 # thus their linknode adjusted, if necessary).
111 # thus their linknode adjusted, if necessary).
112 if source == b"strip":
112 if source == b"strip":
113 cfg = repo.ui.config(b'remotefilelog', b'strip.includefiles')
113 cfg = repo.ui.config(b'remotefilelog', b'strip.includefiles')
114 if cfg == b'local':
114 if cfg == b'local':
115 return LocalFiles
115 return LocalFiles
116 elif cfg != b'none':
116 elif cfg != b'none':
117 return AllFiles
117 return AllFiles
118
118
119 caps = self._bundlecaps or []
119 caps = self._bundlecaps or []
120 if source == b"serve" or source == b"pull":
120 if source == b"serve" or source == b"pull":
121 if constants.BUNDLE2_CAPABLITY in caps:
121 if constants.BUNDLE2_CAPABLITY in caps:
122 return LocalFiles
122 return LocalFiles
123 else:
123 else:
124 # Serving to a full repo requires us to serve everything
124 # Serving to a full repo requires us to serve everything
125 repo.ui.warn(_(b"pulling from a shallow repo\n"))
125 repo.ui.warn(_(b"pulling from a shallow repo\n"))
126 return AllFiles
126 return AllFiles
127
127
128 return NoFiles
128 return NoFiles
129
129
130 def prune(self, rlog, missing, commonrevs):
130 def prune(self, rlog, missing, commonrevs):
131 if not isinstance(rlog, remotefilelog.remotefilelog):
131 if not isinstance(rlog, remotefilelog.remotefilelog):
132 return super(shallowcg1packer, self).prune(
132 return super(shallowcg1packer, self).prune(
133 rlog, missing, commonrevs
133 rlog, missing, commonrevs
134 )
134 )
135
135
136 repo = self._repo
136 repo = self._repo
137 results = []
137 results = []
138 for fnode in missing:
138 for fnode in missing:
139 fctx = repo.filectx(rlog.filename, fileid=fnode)
139 fctx = repo.filectx(rlog.filename, fileid=fnode)
140 if fctx.linkrev() not in commonrevs:
140 if fctx.linkrev() not in commonrevs:
141 results.append(fnode)
141 results.append(fnode)
142 return results
142 return results
143
143
144 def nodechunk(self, revlog, node, prevnode, linknode):
144 def nodechunk(self, revlog, node, prevnode, linknode):
145 prefix = b''
145 prefix = b''
146 if prevnode == revlog.nullid:
146 if prevnode == revlog.nullid:
147 delta = revlog.rawdata(node)
147 delta = revlog.rawdata(node)
148 prefix = mdiff.trivialdiffheader(len(delta))
148 prefix = mdiff.trivialdiffheader(len(delta))
149 else:
149 else:
150 # Actually uses remotefilelog.revdiff which works on nodes, not revs
150 # Actually uses remotefilelog.revdiff which works on nodes, not revs
151 delta = revlog.revdiff(prevnode, node)
151 delta = revlog.revdiff(prevnode, node)
152 p1, p2 = revlog.parents(node)
152 p1, p2 = revlog.parents(node)
153 flags = revlog.flags(node)
153 flags = revlog.flags(node)
154 meta = self.builddeltaheader(node, p1, p2, prevnode, linknode, flags)
154 meta = self.builddeltaheader(node, p1, p2, prevnode, linknode, flags)
155 meta += prefix
155 meta += prefix
156 l = len(meta) + len(delta)
156 l = len(meta) + len(delta)
157 yield changegroup.chunkheader(l)
157 yield changegroup.chunkheader(l)
158 yield meta
158 yield meta
159 yield delta
159 yield delta
160
160
161
161
162 def makechangegroup(orig, repo, outgoing, version, source, *args, **kwargs):
162 def makechangegroup(orig, repo, outgoing, version, source, *args, **kwargs):
163 if not shallowutil.isenabled(repo):
163 if not shallowutil.isenabled(repo):
164 return orig(repo, outgoing, version, source, *args, **kwargs)
164 return orig(repo, outgoing, version, source, *args, **kwargs)
165
165
166 original = repo.shallowmatch
166 original = repo.shallowmatch
167 try:
167 try:
168 # if serving, only send files the clients has patterns for
168 # if serving, only send files the clients has patterns for
169 if source == b'serve':
169 if source == b'serve':
170 bundlecaps = kwargs.get('bundlecaps')
170 bundlecaps = kwargs.get('bundlecaps')
171 includepattern = None
171 includepattern = None
172 excludepattern = None
172 excludepattern = None
173 for cap in bundlecaps or []:
173 for cap in bundlecaps or []:
174 if cap.startswith(b"includepattern="):
174 if cap.startswith(b"includepattern="):
175 raw = cap[len(b"includepattern=") :]
175 raw = cap[len(b"includepattern=") :]
176 if raw:
176 if raw:
177 includepattern = raw.split(b'\0')
177 includepattern = raw.split(b'\0')
178 elif cap.startswith(b"excludepattern="):
178 elif cap.startswith(b"excludepattern="):
179 raw = cap[len(b"excludepattern=") :]
179 raw = cap[len(b"excludepattern=") :]
180 if raw:
180 if raw:
181 excludepattern = raw.split(b'\0')
181 excludepattern = raw.split(b'\0')
182 if includepattern or excludepattern:
182 if includepattern or excludepattern:
183 repo.shallowmatch = match.match(
183 repo.shallowmatch = match.match(
184 repo.root, b'', None, includepattern, excludepattern
184 repo.root, b'', None, includepattern, excludepattern
185 )
185 )
186 else:
186 else:
187 repo.shallowmatch = match.always()
187 repo.shallowmatch = match.always()
188 return orig(repo, outgoing, version, source, *args, **kwargs)
188 return orig(repo, outgoing, version, source, *args, **kwargs)
189 finally:
189 finally:
190 repo.shallowmatch = original
190 repo.shallowmatch = original
191
191
192
192
193 def addchangegroupfiles(
193 def addchangegroupfiles(
194 orig, repo, source, revmap, trp, expectedfiles, *args, **kwargs
194 orig, repo, source, revmap, trp, expectedfiles, *args, **kwargs
195 ):
195 ):
196 if not shallowutil.isenabled(repo):
196 if not shallowutil.isenabled(repo):
197 return orig(repo, source, revmap, trp, expectedfiles, *args, **kwargs)
197 return orig(repo, source, revmap, trp, expectedfiles, *args, **kwargs)
198
198
199 newfiles = 0
199 newfiles = 0
200 visited = set()
200 visited = set()
201 revisiondatas = {}
201 revisiondatas = {}
202 queue = []
202 queue = []
203
203
204 # Normal Mercurial processes each file one at a time, adding all
204 # Normal Mercurial processes each file one at a time, adding all
205 # the new revisions for that file at once. In remotefilelog a file
205 # the new revisions for that file at once. In remotefilelog a file
206 # revision may depend on a different file's revision (in the case
206 # revision may depend on a different file's revision (in the case
207 # of a rename/copy), so we must lay all revisions down across all
207 # of a rename/copy), so we must lay all revisions down across all
208 # files in topological order.
208 # files in topological order.
209
209
210 # read all the file chunks but don't add them
210 # read all the file chunks but don't add them
211 progress = repo.ui.makeprogress(_(b'files'), total=expectedfiles)
211 progress = repo.ui.makeprogress(_(b'files'), total=expectedfiles)
212 while True:
212 while True:
213 chunkdata = source.filelogheader()
213 chunkdata = source.filelogheader()
214 if not chunkdata:
214 if not chunkdata:
215 break
215 break
216 f = chunkdata[b"filename"]
216 f = chunkdata[b"filename"]
217 repo.ui.debug(b"adding %s revisions\n" % f)
217 repo.ui.debug(b"adding %s revisions\n" % f)
218 progress.increment()
218 progress.increment()
219
219
220 if not repo.shallowmatch(f):
220 if not repo.shallowmatch(f):
221 fl = repo.file(f)
221 fl = repo.file(f)
222 deltas = source.deltaiter()
222 deltas = source.deltaiter()
223 fl.addgroup(deltas, revmap, trp)
223 fl.addgroup(deltas, revmap, trp)
224 continue
224 continue
225
225
226 chain = None
226 chain = None
227 while True:
227 while True:
228 # returns: (node, p1, p2, cs, deltabase, delta, flags) or None
228 # returns: None or (
229 # node,
230 # p1,
231 # p2,
232 # cs,
233 # deltabase,
234 # delta,
235 # flags,
236 # sidedata,
237 # proto_flags
238 # )
229 revisiondata = source.deltachunk(chain)
239 revisiondata = source.deltachunk(chain)
230 if not revisiondata:
240 if not revisiondata:
231 break
241 break
232
242
233 chain = revisiondata[0]
243 chain = revisiondata[0]
234
244
235 revisiondatas[(f, chain)] = revisiondata
245 revisiondatas[(f, chain)] = revisiondata
236 queue.append((f, chain))
246 queue.append((f, chain))
237
247
238 if f not in visited:
248 if f not in visited:
239 newfiles += 1
249 newfiles += 1
240 visited.add(f)
250 visited.add(f)
241
251
242 if chain is None:
252 if chain is None:
243 raise error.Abort(_(b"received file revlog group is empty"))
253 raise error.Abort(_(b"received file revlog group is empty"))
244
254
245 processed = set()
255 processed = set()
246
256
247 def available(f, node, depf, depnode):
257 def available(f, node, depf, depnode):
248 if depnode != repo.nullid and (depf, depnode) not in processed:
258 if depnode != repo.nullid and (depf, depnode) not in processed:
249 if not (depf, depnode) in revisiondatas:
259 if not (depf, depnode) in revisiondatas:
250 # It's not in the changegroup, assume it's already
260 # It's not in the changegroup, assume it's already
251 # in the repo
261 # in the repo
252 return True
262 return True
253 # re-add self to queue
263 # re-add self to queue
254 queue.insert(0, (f, node))
264 queue.insert(0, (f, node))
255 # add dependency in front
265 # add dependency in front
256 queue.insert(0, (depf, depnode))
266 queue.insert(0, (depf, depnode))
257 return False
267 return False
258 return True
268 return True
259
269
260 skipcount = 0
270 skipcount = 0
261
271
262 # Prefetch the non-bundled revisions that we will need
272 # Prefetch the non-bundled revisions that we will need
263 prefetchfiles = []
273 prefetchfiles = []
264 for f, node in queue:
274 for f, node in queue:
265 revisiondata = revisiondatas[(f, node)]
275 revisiondata = revisiondatas[(f, node)]
266 # revisiondata: (node, p1, p2, cs, deltabase, delta, flags)
276 # revisiondata: (node, p1, p2, cs, deltabase, delta, flags, sdata, pfl)
267 dependents = [revisiondata[1], revisiondata[2], revisiondata[4]]
277 dependents = [revisiondata[1], revisiondata[2], revisiondata[4]]
268
278
269 for dependent in dependents:
279 for dependent in dependents:
270 if dependent == repo.nullid or (f, dependent) in revisiondatas:
280 if dependent == repo.nullid or (f, dependent) in revisiondatas:
271 continue
281 continue
272 prefetchfiles.append((f, hex(dependent)))
282 prefetchfiles.append((f, hex(dependent)))
273
283
274 repo.fileservice.prefetch(prefetchfiles)
284 repo.fileservice.prefetch(prefetchfiles)
275
285
276 # Apply the revisions in topological order such that a revision
286 # Apply the revisions in topological order such that a revision
277 # is only written once it's deltabase and parents have been written.
287 # is only written once it's deltabase and parents have been written.
278 while queue:
288 while queue:
279 f, node = queue.pop(0)
289 f, node = queue.pop(0)
280 if (f, node) in processed:
290 if (f, node) in processed:
281 continue
291 continue
282
292
283 skipcount += 1
293 skipcount += 1
284 if skipcount > len(queue) + 1:
294 if skipcount > len(queue) + 1:
285 raise error.Abort(_(b"circular node dependency"))
295 raise error.Abort(_(b"circular node dependency"))
286
296
287 fl = repo.file(f)
297 fl = repo.file(f)
288
298
289 revisiondata = revisiondatas[(f, node)]
299 revisiondata = revisiondatas[(f, node)]
290 # revisiondata: (node, p1, p2, cs, deltabase, delta, flags)
300 # revisiondata: (node, p1, p2, cs, deltabase, delta, flags, sdata, pfl)
291 node, p1, p2, linknode, deltabase, delta, flags, sidedata = revisiondata
301 (
302 node,
303 p1,
304 p2,
305 linknode,
306 deltabase,
307 delta,
308 flags,
309 sidedata,
310 proto_flags,
311 ) = revisiondata
292
312
293 if not available(f, node, f, deltabase):
313 if not available(f, node, f, deltabase):
294 continue
314 continue
295
315
296 base = fl.rawdata(deltabase)
316 base = fl.rawdata(deltabase)
297 text = mdiff.patch(base, delta)
317 text = mdiff.patch(base, delta)
298 if not isinstance(text, bytes):
318 if not isinstance(text, bytes):
299 text = bytes(text)
319 text = bytes(text)
300
320
301 meta, text = shallowutil.parsemeta(text)
321 meta, text = shallowutil.parsemeta(text)
302 if b'copy' in meta:
322 if b'copy' in meta:
303 copyfrom = meta[b'copy']
323 copyfrom = meta[b'copy']
304 copynode = bin(meta[b'copyrev'])
324 copynode = bin(meta[b'copyrev'])
305 if not available(f, node, copyfrom, copynode):
325 if not available(f, node, copyfrom, copynode):
306 continue
326 continue
307
327
308 for p in [p1, p2]:
328 for p in [p1, p2]:
309 if p != repo.nullid:
329 if p != repo.nullid:
310 if not available(f, node, f, p):
330 if not available(f, node, f, p):
311 continue
331 continue
312
332
313 fl.add(text, meta, trp, linknode, p1, p2)
333 fl.add(text, meta, trp, linknode, p1, p2)
314 processed.add((f, node))
334 processed.add((f, node))
315 skipcount = 0
335 skipcount = 0
316
336
317 progress.complete()
337 progress.complete()
318
338
319 return len(revisiondatas), newfiles
339 return len(revisiondatas), newfiles
@@ -1,1958 +1,1980 b''
1 # changegroup.py - Mercurial changegroup manipulation functions
1 # changegroup.py - Mercurial changegroup manipulation functions
2 #
2 #
3 # Copyright 2006 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2006 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11 import struct
11 import struct
12 import weakref
12 import weakref
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 hex,
16 hex,
17 nullrev,
17 nullrev,
18 short,
18 short,
19 )
19 )
20 from .pycompat import open
20 from .pycompat import open
21
21
22 from . import (
22 from . import (
23 error,
23 error,
24 match as matchmod,
24 match as matchmod,
25 mdiff,
25 mdiff,
26 phases,
26 phases,
27 pycompat,
27 pycompat,
28 requirements,
28 requirements,
29 scmutil,
29 scmutil,
30 util,
30 util,
31 )
31 )
32
32
33 from .interfaces import repository
33 from .interfaces import repository
34 from .revlogutils import sidedata as sidedatamod
34 from .revlogutils import sidedata as sidedatamod
35 from .revlogutils import constants as revlog_constants
35 from .revlogutils import constants as revlog_constants
36 from .utils import storageutil
36 from .utils import storageutil
37
37
38 _CHANGEGROUPV1_DELTA_HEADER = struct.Struct(b"20s20s20s20s")
38 _CHANGEGROUPV1_DELTA_HEADER = struct.Struct(b"20s20s20s20s")
39 _CHANGEGROUPV2_DELTA_HEADER = struct.Struct(b"20s20s20s20s20s")
39 _CHANGEGROUPV2_DELTA_HEADER = struct.Struct(b"20s20s20s20s20s")
40 _CHANGEGROUPV3_DELTA_HEADER = struct.Struct(b">20s20s20s20s20sH")
40 _CHANGEGROUPV3_DELTA_HEADER = struct.Struct(b">20s20s20s20s20sH")
41 _CHANGEGROUPV4_DELTA_HEADER = struct.Struct(b">B20s20s20s20s20sH")
41 _CHANGEGROUPV4_DELTA_HEADER = struct.Struct(b">B20s20s20s20s20sH")
42
42
43 LFS_REQUIREMENT = b'lfs'
43 LFS_REQUIREMENT = b'lfs'
44
44
45 readexactly = util.readexactly
45 readexactly = util.readexactly
46
46
47
47
48 def getchunk(stream):
48 def getchunk(stream):
49 """return the next chunk from stream as a string"""
49 """return the next chunk from stream as a string"""
50 d = readexactly(stream, 4)
50 d = readexactly(stream, 4)
51 l = struct.unpack(b">l", d)[0]
51 l = struct.unpack(b">l", d)[0]
52 if l <= 4:
52 if l <= 4:
53 if l:
53 if l:
54 raise error.Abort(_(b"invalid chunk length %d") % l)
54 raise error.Abort(_(b"invalid chunk length %d") % l)
55 return b""
55 return b""
56 return readexactly(stream, l - 4)
56 return readexactly(stream, l - 4)
57
57
58
58
59 def chunkheader(length):
59 def chunkheader(length):
60 """return a changegroup chunk header (string)"""
60 """return a changegroup chunk header (string)"""
61 return struct.pack(b">l", length + 4)
61 return struct.pack(b">l", length + 4)
62
62
63
63
64 def closechunk():
64 def closechunk():
65 """return a changegroup chunk header (string) for a zero-length chunk"""
65 """return a changegroup chunk header (string) for a zero-length chunk"""
66 return struct.pack(b">l", 0)
66 return struct.pack(b">l", 0)
67
67
68
68
69 def _fileheader(path):
69 def _fileheader(path):
70 """Obtain a changegroup chunk header for a named path."""
70 """Obtain a changegroup chunk header for a named path."""
71 return chunkheader(len(path)) + path
71 return chunkheader(len(path)) + path
72
72
73
73
74 def writechunks(ui, chunks, filename, vfs=None):
74 def writechunks(ui, chunks, filename, vfs=None):
75 """Write chunks to a file and return its filename.
75 """Write chunks to a file and return its filename.
76
76
77 The stream is assumed to be a bundle file.
77 The stream is assumed to be a bundle file.
78 Existing files will not be overwritten.
78 Existing files will not be overwritten.
79 If no filename is specified, a temporary file is created.
79 If no filename is specified, a temporary file is created.
80 """
80 """
81 fh = None
81 fh = None
82 cleanup = None
82 cleanup = None
83 try:
83 try:
84 if filename:
84 if filename:
85 if vfs:
85 if vfs:
86 fh = vfs.open(filename, b"wb")
86 fh = vfs.open(filename, b"wb")
87 else:
87 else:
88 # Increase default buffer size because default is usually
88 # Increase default buffer size because default is usually
89 # small (4k is common on Linux).
89 # small (4k is common on Linux).
90 fh = open(filename, b"wb", 131072)
90 fh = open(filename, b"wb", 131072)
91 else:
91 else:
92 fd, filename = pycompat.mkstemp(prefix=b"hg-bundle-", suffix=b".hg")
92 fd, filename = pycompat.mkstemp(prefix=b"hg-bundle-", suffix=b".hg")
93 fh = os.fdopen(fd, "wb")
93 fh = os.fdopen(fd, "wb")
94 cleanup = filename
94 cleanup = filename
95 for c in chunks:
95 for c in chunks:
96 fh.write(c)
96 fh.write(c)
97 cleanup = None
97 cleanup = None
98 return filename
98 return filename
99 finally:
99 finally:
100 if fh is not None:
100 if fh is not None:
101 fh.close()
101 fh.close()
102 if cleanup is not None:
102 if cleanup is not None:
103 if filename and vfs:
103 if filename and vfs:
104 vfs.unlink(cleanup)
104 vfs.unlink(cleanup)
105 else:
105 else:
106 os.unlink(cleanup)
106 os.unlink(cleanup)
107
107
108
108
109 class cg1unpacker(object):
109 class cg1unpacker(object):
110 """Unpacker for cg1 changegroup streams.
110 """Unpacker for cg1 changegroup streams.
111
111
112 A changegroup unpacker handles the framing of the revision data in
112 A changegroup unpacker handles the framing of the revision data in
113 the wire format. Most consumers will want to use the apply()
113 the wire format. Most consumers will want to use the apply()
114 method to add the changes from the changegroup to a repository.
114 method to add the changes from the changegroup to a repository.
115
115
116 If you're forwarding a changegroup unmodified to another consumer,
116 If you're forwarding a changegroup unmodified to another consumer,
117 use getchunks(), which returns an iterator of changegroup
117 use getchunks(), which returns an iterator of changegroup
118 chunks. This is mostly useful for cases where you need to know the
118 chunks. This is mostly useful for cases where you need to know the
119 data stream has ended by observing the end of the changegroup.
119 data stream has ended by observing the end of the changegroup.
120
120
121 deltachunk() is useful only if you're applying delta data. Most
121 deltachunk() is useful only if you're applying delta data. Most
122 consumers should prefer apply() instead.
122 consumers should prefer apply() instead.
123
123
124 A few other public methods exist. Those are used only for
124 A few other public methods exist. Those are used only for
125 bundlerepo and some debug commands - their use is discouraged.
125 bundlerepo and some debug commands - their use is discouraged.
126 """
126 """
127
127
128 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
128 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
129 deltaheadersize = deltaheader.size
129 deltaheadersize = deltaheader.size
130 version = b'01'
130 version = b'01'
131 _grouplistcount = 1 # One list of files after the manifests
131 _grouplistcount = 1 # One list of files after the manifests
132
132
133 def __init__(self, fh, alg, extras=None):
133 def __init__(self, fh, alg, extras=None):
134 if alg is None:
134 if alg is None:
135 alg = b'UN'
135 alg = b'UN'
136 if alg not in util.compengines.supportedbundletypes:
136 if alg not in util.compengines.supportedbundletypes:
137 raise error.Abort(_(b'unknown stream compression type: %s') % alg)
137 raise error.Abort(_(b'unknown stream compression type: %s') % alg)
138 if alg == b'BZ':
138 if alg == b'BZ':
139 alg = b'_truncatedBZ'
139 alg = b'_truncatedBZ'
140
140
141 compengine = util.compengines.forbundletype(alg)
141 compengine = util.compengines.forbundletype(alg)
142 self._stream = compengine.decompressorreader(fh)
142 self._stream = compengine.decompressorreader(fh)
143 self._type = alg
143 self._type = alg
144 self.extras = extras or {}
144 self.extras = extras or {}
145 self.callback = None
145 self.callback = None
146
146
147 # These methods (compressed, read, seek, tell) all appear to only
147 # These methods (compressed, read, seek, tell) all appear to only
148 # be used by bundlerepo, but it's a little hard to tell.
148 # be used by bundlerepo, but it's a little hard to tell.
149 def compressed(self):
149 def compressed(self):
150 return self._type is not None and self._type != b'UN'
150 return self._type is not None and self._type != b'UN'
151
151
152 def read(self, l):
152 def read(self, l):
153 return self._stream.read(l)
153 return self._stream.read(l)
154
154
155 def seek(self, pos):
155 def seek(self, pos):
156 return self._stream.seek(pos)
156 return self._stream.seek(pos)
157
157
158 def tell(self):
158 def tell(self):
159 return self._stream.tell()
159 return self._stream.tell()
160
160
161 def close(self):
161 def close(self):
162 return self._stream.close()
162 return self._stream.close()
163
163
164 def _chunklength(self):
164 def _chunklength(self):
165 d = readexactly(self._stream, 4)
165 d = readexactly(self._stream, 4)
166 l = struct.unpack(b">l", d)[0]
166 l = struct.unpack(b">l", d)[0]
167 if l <= 4:
167 if l <= 4:
168 if l:
168 if l:
169 raise error.Abort(_(b"invalid chunk length %d") % l)
169 raise error.Abort(_(b"invalid chunk length %d") % l)
170 return 0
170 return 0
171 if self.callback:
171 if self.callback:
172 self.callback()
172 self.callback()
173 return l - 4
173 return l - 4
174
174
175 def changelogheader(self):
175 def changelogheader(self):
176 """v10 does not have a changelog header chunk"""
176 """v10 does not have a changelog header chunk"""
177 return {}
177 return {}
178
178
179 def manifestheader(self):
179 def manifestheader(self):
180 """v10 does not have a manifest header chunk"""
180 """v10 does not have a manifest header chunk"""
181 return {}
181 return {}
182
182
183 def filelogheader(self):
183 def filelogheader(self):
184 """return the header of the filelogs chunk, v10 only has the filename"""
184 """return the header of the filelogs chunk, v10 only has the filename"""
185 l = self._chunklength()
185 l = self._chunklength()
186 if not l:
186 if not l:
187 return {}
187 return {}
188 fname = readexactly(self._stream, l)
188 fname = readexactly(self._stream, l)
189 return {b'filename': fname}
189 return {b'filename': fname}
190
190
191 def _deltaheader(self, headertuple, prevnode):
191 def _deltaheader(self, headertuple, prevnode):
192 node, p1, p2, cs = headertuple
192 node, p1, p2, cs = headertuple
193 if prevnode is None:
193 if prevnode is None:
194 deltabase = p1
194 deltabase = p1
195 else:
195 else:
196 deltabase = prevnode
196 deltabase = prevnode
197 flags = 0
197 flags = 0
198 protocol_flags = 0
198 protocol_flags = 0
199 return node, p1, p2, deltabase, cs, flags, protocol_flags
199 return node, p1, p2, deltabase, cs, flags, protocol_flags
200
200
201 def deltachunk(self, prevnode):
201 def deltachunk(self, prevnode):
202 # Chunkdata: (node, p1, p2, cs, deltabase, delta, flags, sidedata, proto_flags)
202 l = self._chunklength()
203 l = self._chunklength()
203 if not l:
204 if not l:
204 return {}
205 return {}
205 headerdata = readexactly(self._stream, self.deltaheadersize)
206 headerdata = readexactly(self._stream, self.deltaheadersize)
206 header = self.deltaheader.unpack(headerdata)
207 header = self.deltaheader.unpack(headerdata)
207 delta = readexactly(self._stream, l - self.deltaheadersize)
208 delta = readexactly(self._stream, l - self.deltaheadersize)
208 header = self._deltaheader(header, prevnode)
209 header = self._deltaheader(header, prevnode)
209 node, p1, p2, deltabase, cs, flags, protocol_flags = header
210 node, p1, p2, deltabase, cs, flags, protocol_flags = header
210 return node, p1, p2, cs, deltabase, delta, flags, protocol_flags
211 return node, p1, p2, cs, deltabase, delta, flags, {}, protocol_flags
211
212
212 def getchunks(self):
213 def getchunks(self):
213 """returns all the chunks contains in the bundle
214 """returns all the chunks contains in the bundle
214
215
215 Used when you need to forward the binary stream to a file or another
216 Used when you need to forward the binary stream to a file or another
216 network API. To do so, it parse the changegroup data, otherwise it will
217 network API. To do so, it parse the changegroup data, otherwise it will
217 block in case of sshrepo because it don't know the end of the stream.
218 block in case of sshrepo because it don't know the end of the stream.
218 """
219 """
219 # For changegroup 1 and 2, we expect 3 parts: changelog, manifestlog,
220 # For changegroup 1 and 2, we expect 3 parts: changelog, manifestlog,
220 # and a list of filelogs. For changegroup 3, we expect 4 parts:
221 # and a list of filelogs. For changegroup 3, we expect 4 parts:
221 # changelog, manifestlog, a list of tree manifestlogs, and a list of
222 # changelog, manifestlog, a list of tree manifestlogs, and a list of
222 # filelogs.
223 # filelogs.
223 #
224 #
224 # Changelog and manifestlog parts are terminated with empty chunks. The
225 # Changelog and manifestlog parts are terminated with empty chunks. The
225 # tree and file parts are a list of entry sections. Each entry section
226 # tree and file parts are a list of entry sections. Each entry section
226 # is a series of chunks terminating in an empty chunk. The list of these
227 # is a series of chunks terminating in an empty chunk. The list of these
227 # entry sections is terminated in yet another empty chunk, so we know
228 # entry sections is terminated in yet another empty chunk, so we know
228 # we've reached the end of the tree/file list when we reach an empty
229 # we've reached the end of the tree/file list when we reach an empty
229 # chunk that was proceeded by no non-empty chunks.
230 # chunk that was proceeded by no non-empty chunks.
230
231
231 parts = 0
232 parts = 0
232 while parts < 2 + self._grouplistcount:
233 while parts < 2 + self._grouplistcount:
233 noentries = True
234 noentries = True
234 while True:
235 while True:
235 chunk = getchunk(self)
236 chunk = getchunk(self)
236 if not chunk:
237 if not chunk:
237 # The first two empty chunks represent the end of the
238 # The first two empty chunks represent the end of the
238 # changelog and the manifestlog portions. The remaining
239 # changelog and the manifestlog portions. The remaining
239 # empty chunks represent either A) the end of individual
240 # empty chunks represent either A) the end of individual
240 # tree or file entries in the file list, or B) the end of
241 # tree or file entries in the file list, or B) the end of
241 # the entire list. It's the end of the entire list if there
242 # the entire list. It's the end of the entire list if there
242 # were no entries (i.e. noentries is True).
243 # were no entries (i.e. noentries is True).
243 if parts < 2:
244 if parts < 2:
244 parts += 1
245 parts += 1
245 elif noentries:
246 elif noentries:
246 parts += 1
247 parts += 1
247 break
248 break
248 noentries = False
249 noentries = False
249 yield chunkheader(len(chunk))
250 yield chunkheader(len(chunk))
250 pos = 0
251 pos = 0
251 while pos < len(chunk):
252 while pos < len(chunk):
252 next = pos + 2 ** 20
253 next = pos + 2 ** 20
253 yield chunk[pos:next]
254 yield chunk[pos:next]
254 pos = next
255 pos = next
255 yield closechunk()
256 yield closechunk()
256
257
257 def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
258 def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
258 self.callback = prog.increment
259 self.callback = prog.increment
259 # no need to check for empty manifest group here:
260 # no need to check for empty manifest group here:
260 # if the result of the merge of 1 and 2 is the same in 3 and 4,
261 # if the result of the merge of 1 and 2 is the same in 3 and 4,
261 # no new manifest will be created and the manifest group will
262 # no new manifest will be created and the manifest group will
262 # be empty during the pull
263 # be empty during the pull
263 self.manifestheader()
264 self.manifestheader()
264 deltas = self.deltaiter()
265 deltas = self.deltaiter()
265 storage = repo.manifestlog.getstorage(b'')
266 storage = repo.manifestlog.getstorage(b'')
266 storage.addgroup(deltas, revmap, trp, addrevisioncb=addrevisioncb)
267 storage.addgroup(deltas, revmap, trp, addrevisioncb=addrevisioncb)
267 prog.complete()
268 prog.complete()
268 self.callback = None
269 self.callback = None
269
270
270 def apply(
271 def apply(
271 self,
272 self,
272 repo,
273 repo,
273 tr,
274 tr,
274 srctype,
275 srctype,
275 url,
276 url,
276 targetphase=phases.draft,
277 targetphase=phases.draft,
277 expectedtotal=None,
278 expectedtotal=None,
278 sidedata_categories=None,
279 sidedata_categories=None,
279 ):
280 ):
280 """Add the changegroup returned by source.read() to this repo.
281 """Add the changegroup returned by source.read() to this repo.
281 srctype is a string like 'push', 'pull', or 'unbundle'. url is
282 srctype is a string like 'push', 'pull', or 'unbundle'. url is
282 the URL of the repo where this changegroup is coming from.
283 the URL of the repo where this changegroup is coming from.
283
284
284 Return an integer summarizing the change to this repo:
285 Return an integer summarizing the change to this repo:
285 - nothing changed or no source: 0
286 - nothing changed or no source: 0
286 - more heads than before: 1+added heads (2..n)
287 - more heads than before: 1+added heads (2..n)
287 - fewer heads than before: -1-removed heads (-2..-n)
288 - fewer heads than before: -1-removed heads (-2..-n)
288 - number of heads stays the same: 1
289 - number of heads stays the same: 1
289
290
290 `sidedata_categories` is an optional set of the remote's sidedata wanted
291 `sidedata_categories` is an optional set of the remote's sidedata wanted
291 categories.
292 categories.
292 """
293 """
293 repo = repo.unfiltered()
294 repo = repo.unfiltered()
294
295
295 # Only useful if we're adding sidedata categories. If both peers have
296 # Only useful if we're adding sidedata categories. If both peers have
296 # the same categories, then we simply don't do anything.
297 # the same categories, then we simply don't do anything.
297 adding_sidedata = (
298 adding_sidedata = (
298 (
299 (
299 requirements.REVLOGV2_REQUIREMENT in repo.requirements
300 requirements.REVLOGV2_REQUIREMENT in repo.requirements
300 or requirements.CHANGELOGV2_REQUIREMENT in repo.requirements
301 or requirements.CHANGELOGV2_REQUIREMENT in repo.requirements
301 )
302 )
302 and self.version == b'04'
303 and self.version == b'04'
303 and srctype == b'pull'
304 and srctype == b'pull'
304 )
305 )
305 if adding_sidedata:
306 if adding_sidedata:
306 sidedata_helpers = sidedatamod.get_sidedata_helpers(
307 sidedata_helpers = sidedatamod.get_sidedata_helpers(
307 repo,
308 repo,
308 sidedata_categories or set(),
309 sidedata_categories or set(),
309 pull=True,
310 pull=True,
310 )
311 )
311 else:
312 else:
312 sidedata_helpers = None
313 sidedata_helpers = None
313
314
314 def csmap(x):
315 def csmap(x):
315 repo.ui.debug(b"add changeset %s\n" % short(x))
316 repo.ui.debug(b"add changeset %s\n" % short(x))
316 return len(cl)
317 return len(cl)
317
318
318 def revmap(x):
319 def revmap(x):
319 return cl.rev(x)
320 return cl.rev(x)
320
321
321 try:
322 try:
322 # The transaction may already carry source information. In this
323 # The transaction may already carry source information. In this
323 # case we use the top level data. We overwrite the argument
324 # case we use the top level data. We overwrite the argument
324 # because we need to use the top level value (if they exist)
325 # because we need to use the top level value (if they exist)
325 # in this function.
326 # in this function.
326 srctype = tr.hookargs.setdefault(b'source', srctype)
327 srctype = tr.hookargs.setdefault(b'source', srctype)
327 tr.hookargs.setdefault(b'url', url)
328 tr.hookargs.setdefault(b'url', url)
328 repo.hook(
329 repo.hook(
329 b'prechangegroup', throw=True, **pycompat.strkwargs(tr.hookargs)
330 b'prechangegroup', throw=True, **pycompat.strkwargs(tr.hookargs)
330 )
331 )
331
332
332 # write changelog data to temp files so concurrent readers
333 # write changelog data to temp files so concurrent readers
333 # will not see an inconsistent view
334 # will not see an inconsistent view
334 cl = repo.changelog
335 cl = repo.changelog
335 cl.delayupdate(tr)
336 cl.delayupdate(tr)
336 oldheads = set(cl.heads())
337 oldheads = set(cl.heads())
337
338
338 trp = weakref.proxy(tr)
339 trp = weakref.proxy(tr)
339 # pull off the changeset group
340 # pull off the changeset group
340 repo.ui.status(_(b"adding changesets\n"))
341 repo.ui.status(_(b"adding changesets\n"))
341 clstart = len(cl)
342 clstart = len(cl)
342 progress = repo.ui.makeprogress(
343 progress = repo.ui.makeprogress(
343 _(b'changesets'), unit=_(b'chunks'), total=expectedtotal
344 _(b'changesets'), unit=_(b'chunks'), total=expectedtotal
344 )
345 )
345 self.callback = progress.increment
346 self.callback = progress.increment
346
347
347 efilesset = set()
348 efilesset = set()
348 duprevs = []
349 duprevs = []
349
350
350 def ondupchangelog(cl, rev):
351 def ondupchangelog(cl, rev):
351 if rev < clstart:
352 if rev < clstart:
352 duprevs.append(rev)
353 duprevs.append(rev)
353
354
354 def onchangelog(cl, rev):
355 def onchangelog(cl, rev):
355 ctx = cl.changelogrevision(rev)
356 ctx = cl.changelogrevision(rev)
356 efilesset.update(ctx.files)
357 efilesset.update(ctx.files)
357 repo.register_changeset(rev, ctx)
358 repo.register_changeset(rev, ctx)
358
359
359 self.changelogheader()
360 self.changelogheader()
360 deltas = self.deltaiter()
361 deltas = self.deltaiter()
361 if not cl.addgroup(
362 if not cl.addgroup(
362 deltas,
363 deltas,
363 csmap,
364 csmap,
364 trp,
365 trp,
365 alwayscache=True,
366 alwayscache=True,
366 addrevisioncb=onchangelog,
367 addrevisioncb=onchangelog,
367 duplicaterevisioncb=ondupchangelog,
368 duplicaterevisioncb=ondupchangelog,
368 ):
369 ):
369 repo.ui.develwarn(
370 repo.ui.develwarn(
370 b'applied empty changelog from changegroup',
371 b'applied empty changelog from changegroup',
371 config=b'warn-empty-changegroup',
372 config=b'warn-empty-changegroup',
372 )
373 )
373 efiles = len(efilesset)
374 efiles = len(efilesset)
374 clend = len(cl)
375 clend = len(cl)
375 changesets = clend - clstart
376 changesets = clend - clstart
376 progress.complete()
377 progress.complete()
377 del deltas
378 del deltas
378 # TODO Python 2.7 removal
379 # TODO Python 2.7 removal
379 # del efilesset
380 # del efilesset
380 efilesset = None
381 efilesset = None
381 self.callback = None
382 self.callback = None
382
383
383 # Keep track of the (non-changelog) revlogs we've updated and their
384 # Keep track of the (non-changelog) revlogs we've updated and their
384 # range of new revisions for sidedata rewrite.
385 # range of new revisions for sidedata rewrite.
385 # TODO do something more efficient than keeping the reference to
386 # TODO do something more efficient than keeping the reference to
386 # the revlogs, especially memory-wise.
387 # the revlogs, especially memory-wise.
387 touched_manifests = {}
388 touched_manifests = {}
388 touched_filelogs = {}
389 touched_filelogs = {}
389
390
390 # pull off the manifest group
391 # pull off the manifest group
391 repo.ui.status(_(b"adding manifests\n"))
392 repo.ui.status(_(b"adding manifests\n"))
392 # We know that we'll never have more manifests than we had
393 # We know that we'll never have more manifests than we had
393 # changesets.
394 # changesets.
394 progress = repo.ui.makeprogress(
395 progress = repo.ui.makeprogress(
395 _(b'manifests'), unit=_(b'chunks'), total=changesets
396 _(b'manifests'), unit=_(b'chunks'), total=changesets
396 )
397 )
397 on_manifest_rev = None
398 on_manifest_rev = None
398 if sidedata_helpers:
399 if sidedata_helpers:
399 if revlog_constants.KIND_MANIFESTLOG in sidedata_helpers[1]:
400 if revlog_constants.KIND_MANIFESTLOG in sidedata_helpers[1]:
400
401
401 def on_manifest_rev(manifest, rev):
402 def on_manifest_rev(manifest, rev):
402 range = touched_manifests.get(manifest)
403 range = touched_manifests.get(manifest)
403 if not range:
404 if not range:
404 touched_manifests[manifest] = (rev, rev)
405 touched_manifests[manifest] = (rev, rev)
405 else:
406 else:
406 assert rev == range[1] + 1
407 assert rev == range[1] + 1
407 touched_manifests[manifest] = (range[0], rev)
408 touched_manifests[manifest] = (range[0], rev)
408
409
409 self._unpackmanifests(
410 self._unpackmanifests(
410 repo,
411 repo,
411 revmap,
412 revmap,
412 trp,
413 trp,
413 progress,
414 progress,
414 addrevisioncb=on_manifest_rev,
415 addrevisioncb=on_manifest_rev,
415 )
416 )
416
417
417 needfiles = {}
418 needfiles = {}
418 if repo.ui.configbool(b'server', b'validate'):
419 if repo.ui.configbool(b'server', b'validate'):
419 cl = repo.changelog
420 cl = repo.changelog
420 ml = repo.manifestlog
421 ml = repo.manifestlog
421 # validate incoming csets have their manifests
422 # validate incoming csets have their manifests
422 for cset in pycompat.xrange(clstart, clend):
423 for cset in pycompat.xrange(clstart, clend):
423 mfnode = cl.changelogrevision(cset).manifest
424 mfnode = cl.changelogrevision(cset).manifest
424 mfest = ml[mfnode].readdelta()
425 mfest = ml[mfnode].readdelta()
425 # store file nodes we must see
426 # store file nodes we must see
426 for f, n in pycompat.iteritems(mfest):
427 for f, n in pycompat.iteritems(mfest):
427 needfiles.setdefault(f, set()).add(n)
428 needfiles.setdefault(f, set()).add(n)
428
429
429 on_filelog_rev = None
430 on_filelog_rev = None
430 if sidedata_helpers:
431 if sidedata_helpers:
431 if revlog_constants.KIND_FILELOG in sidedata_helpers[1]:
432 if revlog_constants.KIND_FILELOG in sidedata_helpers[1]:
432
433
433 def on_filelog_rev(filelog, rev):
434 def on_filelog_rev(filelog, rev):
434 range = touched_filelogs.get(filelog)
435 range = touched_filelogs.get(filelog)
435 if not range:
436 if not range:
436 touched_filelogs[filelog] = (rev, rev)
437 touched_filelogs[filelog] = (rev, rev)
437 else:
438 else:
438 assert rev == range[1] + 1
439 assert rev == range[1] + 1
439 touched_filelogs[filelog] = (range[0], rev)
440 touched_filelogs[filelog] = (range[0], rev)
440
441
441 # process the files
442 # process the files
442 repo.ui.status(_(b"adding file changes\n"))
443 repo.ui.status(_(b"adding file changes\n"))
443 newrevs, newfiles = _addchangegroupfiles(
444 newrevs, newfiles = _addchangegroupfiles(
444 repo,
445 repo,
445 self,
446 self,
446 revmap,
447 revmap,
447 trp,
448 trp,
448 efiles,
449 efiles,
449 needfiles,
450 needfiles,
450 addrevisioncb=on_filelog_rev,
451 addrevisioncb=on_filelog_rev,
451 )
452 )
452
453
453 if sidedata_helpers:
454 if sidedata_helpers:
454 if revlog_constants.KIND_CHANGELOG in sidedata_helpers[1]:
455 if revlog_constants.KIND_CHANGELOG in sidedata_helpers[1]:
455 cl.rewrite_sidedata(
456 cl.rewrite_sidedata(
456 trp, sidedata_helpers, clstart, clend - 1
457 trp, sidedata_helpers, clstart, clend - 1
457 )
458 )
458 for mf, (startrev, endrev) in touched_manifests.items():
459 for mf, (startrev, endrev) in touched_manifests.items():
459 mf.rewrite_sidedata(trp, sidedata_helpers, startrev, endrev)
460 mf.rewrite_sidedata(trp, sidedata_helpers, startrev, endrev)
460 for fl, (startrev, endrev) in touched_filelogs.items():
461 for fl, (startrev, endrev) in touched_filelogs.items():
461 fl.rewrite_sidedata(trp, sidedata_helpers, startrev, endrev)
462 fl.rewrite_sidedata(trp, sidedata_helpers, startrev, endrev)
462
463
463 # making sure the value exists
464 # making sure the value exists
464 tr.changes.setdefault(b'changegroup-count-changesets', 0)
465 tr.changes.setdefault(b'changegroup-count-changesets', 0)
465 tr.changes.setdefault(b'changegroup-count-revisions', 0)
466 tr.changes.setdefault(b'changegroup-count-revisions', 0)
466 tr.changes.setdefault(b'changegroup-count-files', 0)
467 tr.changes.setdefault(b'changegroup-count-files', 0)
467 tr.changes.setdefault(b'changegroup-count-heads', 0)
468 tr.changes.setdefault(b'changegroup-count-heads', 0)
468
469
469 # some code use bundle operation for internal purpose. They usually
470 # some code use bundle operation for internal purpose. They usually
470 # set `ui.quiet` to do this outside of user sight. Size the report
471 # set `ui.quiet` to do this outside of user sight. Size the report
471 # of such operation now happens at the end of the transaction, that
472 # of such operation now happens at the end of the transaction, that
472 # ui.quiet has not direct effect on the output.
473 # ui.quiet has not direct effect on the output.
473 #
474 #
474 # To preserve this intend use an inelegant hack, we fail to report
475 # To preserve this intend use an inelegant hack, we fail to report
475 # the change if `quiet` is set. We should probably move to
476 # the change if `quiet` is set. We should probably move to
476 # something better, but this is a good first step to allow the "end
477 # something better, but this is a good first step to allow the "end
477 # of transaction report" to pass tests.
478 # of transaction report" to pass tests.
478 if not repo.ui.quiet:
479 if not repo.ui.quiet:
479 tr.changes[b'changegroup-count-changesets'] += changesets
480 tr.changes[b'changegroup-count-changesets'] += changesets
480 tr.changes[b'changegroup-count-revisions'] += newrevs
481 tr.changes[b'changegroup-count-revisions'] += newrevs
481 tr.changes[b'changegroup-count-files'] += newfiles
482 tr.changes[b'changegroup-count-files'] += newfiles
482
483
483 deltaheads = 0
484 deltaheads = 0
484 if oldheads:
485 if oldheads:
485 heads = cl.heads()
486 heads = cl.heads()
486 deltaheads += len(heads) - len(oldheads)
487 deltaheads += len(heads) - len(oldheads)
487 for h in heads:
488 for h in heads:
488 if h not in oldheads and repo[h].closesbranch():
489 if h not in oldheads and repo[h].closesbranch():
489 deltaheads -= 1
490 deltaheads -= 1
490
491
491 # see previous comment about checking ui.quiet
492 # see previous comment about checking ui.quiet
492 if not repo.ui.quiet:
493 if not repo.ui.quiet:
493 tr.changes[b'changegroup-count-heads'] += deltaheads
494 tr.changes[b'changegroup-count-heads'] += deltaheads
494 repo.invalidatevolatilesets()
495 repo.invalidatevolatilesets()
495
496
496 if changesets > 0:
497 if changesets > 0:
497 if b'node' not in tr.hookargs:
498 if b'node' not in tr.hookargs:
498 tr.hookargs[b'node'] = hex(cl.node(clstart))
499 tr.hookargs[b'node'] = hex(cl.node(clstart))
499 tr.hookargs[b'node_last'] = hex(cl.node(clend - 1))
500 tr.hookargs[b'node_last'] = hex(cl.node(clend - 1))
500 hookargs = dict(tr.hookargs)
501 hookargs = dict(tr.hookargs)
501 else:
502 else:
502 hookargs = dict(tr.hookargs)
503 hookargs = dict(tr.hookargs)
503 hookargs[b'node'] = hex(cl.node(clstart))
504 hookargs[b'node'] = hex(cl.node(clstart))
504 hookargs[b'node_last'] = hex(cl.node(clend - 1))
505 hookargs[b'node_last'] = hex(cl.node(clend - 1))
505 repo.hook(
506 repo.hook(
506 b'pretxnchangegroup',
507 b'pretxnchangegroup',
507 throw=True,
508 throw=True,
508 **pycompat.strkwargs(hookargs)
509 **pycompat.strkwargs(hookargs)
509 )
510 )
510
511
511 added = pycompat.xrange(clstart, clend)
512 added = pycompat.xrange(clstart, clend)
512 phaseall = None
513 phaseall = None
513 if srctype in (b'push', b'serve'):
514 if srctype in (b'push', b'serve'):
514 # Old servers can not push the boundary themselves.
515 # Old servers can not push the boundary themselves.
515 # New servers won't push the boundary if changeset already
516 # New servers won't push the boundary if changeset already
516 # exists locally as secret
517 # exists locally as secret
517 #
518 #
518 # We should not use added here but the list of all change in
519 # We should not use added here but the list of all change in
519 # the bundle
520 # the bundle
520 if repo.publishing():
521 if repo.publishing():
521 targetphase = phaseall = phases.public
522 targetphase = phaseall = phases.public
522 else:
523 else:
523 # closer target phase computation
524 # closer target phase computation
524
525
525 # Those changesets have been pushed from the
526 # Those changesets have been pushed from the
526 # outside, their phases are going to be pushed
527 # outside, their phases are going to be pushed
527 # alongside. Therefor `targetphase` is
528 # alongside. Therefor `targetphase` is
528 # ignored.
529 # ignored.
529 targetphase = phaseall = phases.draft
530 targetphase = phaseall = phases.draft
530 if added:
531 if added:
531 phases.registernew(repo, tr, targetphase, added)
532 phases.registernew(repo, tr, targetphase, added)
532 if phaseall is not None:
533 if phaseall is not None:
533 if duprevs:
534 if duprevs:
534 duprevs.extend(added)
535 duprevs.extend(added)
535 else:
536 else:
536 duprevs = added
537 duprevs = added
537 phases.advanceboundary(repo, tr, phaseall, [], revs=duprevs)
538 phases.advanceboundary(repo, tr, phaseall, [], revs=duprevs)
538 duprevs = []
539 duprevs = []
539
540
540 if changesets > 0:
541 if changesets > 0:
541
542
542 def runhooks(unused_success):
543 def runhooks(unused_success):
543 # These hooks run when the lock releases, not when the
544 # These hooks run when the lock releases, not when the
544 # transaction closes. So it's possible for the changelog
545 # transaction closes. So it's possible for the changelog
545 # to have changed since we last saw it.
546 # to have changed since we last saw it.
546 if clstart >= len(repo):
547 if clstart >= len(repo):
547 return
548 return
548
549
549 repo.hook(b"changegroup", **pycompat.strkwargs(hookargs))
550 repo.hook(b"changegroup", **pycompat.strkwargs(hookargs))
550
551
551 for rev in added:
552 for rev in added:
552 args = hookargs.copy()
553 args = hookargs.copy()
553 args[b'node'] = hex(cl.node(rev))
554 args[b'node'] = hex(cl.node(rev))
554 del args[b'node_last']
555 del args[b'node_last']
555 repo.hook(b"incoming", **pycompat.strkwargs(args))
556 repo.hook(b"incoming", **pycompat.strkwargs(args))
556
557
557 newheads = [h for h in repo.heads() if h not in oldheads]
558 newheads = [h for h in repo.heads() if h not in oldheads]
558 repo.ui.log(
559 repo.ui.log(
559 b"incoming",
560 b"incoming",
560 b"%d incoming changes - new heads: %s\n",
561 b"%d incoming changes - new heads: %s\n",
561 len(added),
562 len(added),
562 b', '.join([hex(c[:6]) for c in newheads]),
563 b', '.join([hex(c[:6]) for c in newheads]),
563 )
564 )
564
565
565 tr.addpostclose(
566 tr.addpostclose(
566 b'changegroup-runhooks-%020i' % clstart,
567 b'changegroup-runhooks-%020i' % clstart,
567 lambda tr: repo._afterlock(runhooks),
568 lambda tr: repo._afterlock(runhooks),
568 )
569 )
569 finally:
570 finally:
570 repo.ui.flush()
571 repo.ui.flush()
571 # never return 0 here:
572 # never return 0 here:
572 if deltaheads < 0:
573 if deltaheads < 0:
573 ret = deltaheads - 1
574 ret = deltaheads - 1
574 else:
575 else:
575 ret = deltaheads + 1
576 ret = deltaheads + 1
576 return ret
577 return ret
577
578
578 def deltaiter(self):
579 def deltaiter(self):
579 """
580 """
580 returns an iterator of the deltas in this changegroup
581 returns an iterator of the deltas in this changegroup
581
582
582 Useful for passing to the underlying storage system to be stored.
583 Useful for passing to the underlying storage system to be stored.
583 """
584 """
584 chain = None
585 chain = None
585 for chunkdata in iter(lambda: self.deltachunk(chain), {}):
586 for chunkdata in iter(lambda: self.deltachunk(chain), {}):
586 # Chunkdata: (node, p1, p2, cs, deltabase, delta, flags, sidedata)
587 # Chunkdata: (node, p1, p2, cs, deltabase, delta, flags, sidedata, proto_flags)
587 yield chunkdata
588 yield chunkdata[:8]
588 chain = chunkdata[0]
589 chain = chunkdata[0]
589
590
590
591
591 class cg2unpacker(cg1unpacker):
592 class cg2unpacker(cg1unpacker):
592 """Unpacker for cg2 streams.
593 """Unpacker for cg2 streams.
593
594
594 cg2 streams add support for generaldelta, so the delta header
595 cg2 streams add support for generaldelta, so the delta header
595 format is slightly different. All other features about the data
596 format is slightly different. All other features about the data
596 remain the same.
597 remain the same.
597 """
598 """
598
599
599 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
600 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
600 deltaheadersize = deltaheader.size
601 deltaheadersize = deltaheader.size
601 version = b'02'
602 version = b'02'
602
603
603 def _deltaheader(self, headertuple, prevnode):
604 def _deltaheader(self, headertuple, prevnode):
604 node, p1, p2, deltabase, cs = headertuple
605 node, p1, p2, deltabase, cs = headertuple
605 flags = 0
606 flags = 0
606 protocol_flags = 0
607 protocol_flags = 0
607 return node, p1, p2, deltabase, cs, flags, protocol_flags
608 return node, p1, p2, deltabase, cs, flags, protocol_flags
608
609
609
610
610 class cg3unpacker(cg2unpacker):
611 class cg3unpacker(cg2unpacker):
611 """Unpacker for cg3 streams.
612 """Unpacker for cg3 streams.
612
613
613 cg3 streams add support for exchanging treemanifests and revlog
614 cg3 streams add support for exchanging treemanifests and revlog
614 flags. It adds the revlog flags to the delta header and an empty chunk
615 flags. It adds the revlog flags to the delta header and an empty chunk
615 separating manifests and files.
616 separating manifests and files.
616 """
617 """
617
618
618 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
619 deltaheader = _CHANGEGROUPV3_DELTA_HEADER
619 deltaheadersize = deltaheader.size
620 deltaheadersize = deltaheader.size
620 version = b'03'
621 version = b'03'
621 _grouplistcount = 2 # One list of manifests and one list of files
622 _grouplistcount = 2 # One list of manifests and one list of files
622
623
623 def _deltaheader(self, headertuple, prevnode):
624 def _deltaheader(self, headertuple, prevnode):
624 node, p1, p2, deltabase, cs, flags = headertuple
625 node, p1, p2, deltabase, cs, flags = headertuple
625 protocol_flags = 0
626 protocol_flags = 0
626 return node, p1, p2, deltabase, cs, flags, protocol_flags
627 return node, p1, p2, deltabase, cs, flags, protocol_flags
627
628
628 def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
629 def _unpackmanifests(self, repo, revmap, trp, prog, addrevisioncb=None):
629 super(cg3unpacker, self)._unpackmanifests(
630 super(cg3unpacker, self)._unpackmanifests(
630 repo, revmap, trp, prog, addrevisioncb=addrevisioncb
631 repo, revmap, trp, prog, addrevisioncb=addrevisioncb
631 )
632 )
632 for chunkdata in iter(self.filelogheader, {}):
633 for chunkdata in iter(self.filelogheader, {}):
633 # If we get here, there are directory manifests in the changegroup
634 # If we get here, there are directory manifests in the changegroup
634 d = chunkdata[b"filename"]
635 d = chunkdata[b"filename"]
635 repo.ui.debug(b"adding %s revisions\n" % d)
636 repo.ui.debug(b"adding %s revisions\n" % d)
636 deltas = self.deltaiter()
637 deltas = self.deltaiter()
637 if not repo.manifestlog.getstorage(d).addgroup(
638 if not repo.manifestlog.getstorage(d).addgroup(
638 deltas, revmap, trp, addrevisioncb=addrevisioncb
639 deltas, revmap, trp, addrevisioncb=addrevisioncb
639 ):
640 ):
640 raise error.Abort(_(b"received dir revlog group is empty"))
641 raise error.Abort(_(b"received dir revlog group is empty"))
641
642
642
643
643 class cg4unpacker(cg3unpacker):
644 class cg4unpacker(cg3unpacker):
644 """Unpacker for cg4 streams.
645 """Unpacker for cg4 streams.
645
646
646 cg4 streams add support for exchanging sidedata.
647 cg4 streams add support for exchanging sidedata.
647 """
648 """
648
649
649 deltaheader = _CHANGEGROUPV4_DELTA_HEADER
650 deltaheader = _CHANGEGROUPV4_DELTA_HEADER
650 deltaheadersize = deltaheader.size
651 deltaheadersize = deltaheader.size
651 version = b'04'
652 version = b'04'
652
653
653 def _deltaheader(self, headertuple, prevnode):
654 def _deltaheader(self, headertuple, prevnode):
654 protocol_flags, node, p1, p2, deltabase, cs, flags = headertuple
655 protocol_flags, node, p1, p2, deltabase, cs, flags = headertuple
655 return node, p1, p2, deltabase, cs, flags, protocol_flags
656 return node, p1, p2, deltabase, cs, flags, protocol_flags
656
657
657 def deltachunk(self, prevnode):
658 def deltachunk(self, prevnode):
658 res = super(cg4unpacker, self).deltachunk(prevnode)
659 res = super(cg4unpacker, self).deltachunk(prevnode)
659 if not res:
660 if not res:
660 return res
661 return res
661
662
662 (node, p1, p2, cs, deltabase, delta, flags, protocol_flags) = res
663 (
664 node,
665 p1,
666 p2,
667 cs,
668 deltabase,
669 delta,
670 flags,
671 sidedata,
672 protocol_flags,
673 ) = res
674 assert not sidedata
663
675
664 sidedata = {}
676 sidedata = {}
665 if protocol_flags & storageutil.CG_FLAG_SIDEDATA:
677 if protocol_flags & storageutil.CG_FLAG_SIDEDATA:
666 sidedata_raw = getchunk(self._stream)
678 sidedata_raw = getchunk(self._stream)
667 sidedata = sidedatamod.deserialize_sidedata(sidedata_raw)
679 sidedata = sidedatamod.deserialize_sidedata(sidedata_raw)
668
680
669 return node, p1, p2, cs, deltabase, delta, flags, sidedata
681 return (
682 node,
683 p1,
684 p2,
685 cs,
686 deltabase,
687 delta,
688 flags,
689 sidedata,
690 protocol_flags,
691 )
670
692
671
693
672 class headerlessfixup(object):
694 class headerlessfixup(object):
673 def __init__(self, fh, h):
695 def __init__(self, fh, h):
674 self._h = h
696 self._h = h
675 self._fh = fh
697 self._fh = fh
676
698
677 def read(self, n):
699 def read(self, n):
678 if self._h:
700 if self._h:
679 d, self._h = self._h[:n], self._h[n:]
701 d, self._h = self._h[:n], self._h[n:]
680 if len(d) < n:
702 if len(d) < n:
681 d += readexactly(self._fh, n - len(d))
703 d += readexactly(self._fh, n - len(d))
682 return d
704 return d
683 return readexactly(self._fh, n)
705 return readexactly(self._fh, n)
684
706
685
707
686 def _revisiondeltatochunks(repo, delta, headerfn):
708 def _revisiondeltatochunks(repo, delta, headerfn):
687 """Serialize a revisiondelta to changegroup chunks."""
709 """Serialize a revisiondelta to changegroup chunks."""
688
710
689 # The captured revision delta may be encoded as a delta against
711 # The captured revision delta may be encoded as a delta against
690 # a base revision or as a full revision. The changegroup format
712 # a base revision or as a full revision. The changegroup format
691 # requires that everything on the wire be deltas. So for full
713 # requires that everything on the wire be deltas. So for full
692 # revisions, we need to invent a header that says to rewrite
714 # revisions, we need to invent a header that says to rewrite
693 # data.
715 # data.
694
716
695 if delta.delta is not None:
717 if delta.delta is not None:
696 prefix, data = b'', delta.delta
718 prefix, data = b'', delta.delta
697 elif delta.basenode == repo.nullid:
719 elif delta.basenode == repo.nullid:
698 data = delta.revision
720 data = delta.revision
699 prefix = mdiff.trivialdiffheader(len(data))
721 prefix = mdiff.trivialdiffheader(len(data))
700 else:
722 else:
701 data = delta.revision
723 data = delta.revision
702 prefix = mdiff.replacediffheader(delta.baserevisionsize, len(data))
724 prefix = mdiff.replacediffheader(delta.baserevisionsize, len(data))
703
725
704 meta = headerfn(delta)
726 meta = headerfn(delta)
705
727
706 yield chunkheader(len(meta) + len(prefix) + len(data))
728 yield chunkheader(len(meta) + len(prefix) + len(data))
707 yield meta
729 yield meta
708 if prefix:
730 if prefix:
709 yield prefix
731 yield prefix
710 yield data
732 yield data
711
733
712 if delta.protocol_flags & storageutil.CG_FLAG_SIDEDATA:
734 if delta.protocol_flags & storageutil.CG_FLAG_SIDEDATA:
713 # Need a separate chunk for sidedata to be able to differentiate
735 # Need a separate chunk for sidedata to be able to differentiate
714 # "raw delta" length and sidedata length
736 # "raw delta" length and sidedata length
715 sidedata = delta.sidedata
737 sidedata = delta.sidedata
716 yield chunkheader(len(sidedata))
738 yield chunkheader(len(sidedata))
717 yield sidedata
739 yield sidedata
718
740
719
741
720 def _sortnodesellipsis(store, nodes, cl, lookup):
742 def _sortnodesellipsis(store, nodes, cl, lookup):
721 """Sort nodes for changegroup generation."""
743 """Sort nodes for changegroup generation."""
722 # Ellipses serving mode.
744 # Ellipses serving mode.
723 #
745 #
724 # In a perfect world, we'd generate better ellipsis-ified graphs
746 # In a perfect world, we'd generate better ellipsis-ified graphs
725 # for non-changelog revlogs. In practice, we haven't started doing
747 # for non-changelog revlogs. In practice, we haven't started doing
726 # that yet, so the resulting DAGs for the manifestlog and filelogs
748 # that yet, so the resulting DAGs for the manifestlog and filelogs
727 # are actually full of bogus parentage on all the ellipsis
749 # are actually full of bogus parentage on all the ellipsis
728 # nodes. This has the side effect that, while the contents are
750 # nodes. This has the side effect that, while the contents are
729 # correct, the individual DAGs might be completely out of whack in
751 # correct, the individual DAGs might be completely out of whack in
730 # a case like 882681bc3166 and its ancestors (back about 10
752 # a case like 882681bc3166 and its ancestors (back about 10
731 # revisions or so) in the main hg repo.
753 # revisions or so) in the main hg repo.
732 #
754 #
733 # The one invariant we *know* holds is that the new (potentially
755 # The one invariant we *know* holds is that the new (potentially
734 # bogus) DAG shape will be valid if we order the nodes in the
756 # bogus) DAG shape will be valid if we order the nodes in the
735 # order that they're introduced in dramatis personae by the
757 # order that they're introduced in dramatis personae by the
736 # changelog, so what we do is we sort the non-changelog histories
758 # changelog, so what we do is we sort the non-changelog histories
737 # by the order in which they are used by the changelog.
759 # by the order in which they are used by the changelog.
738 key = lambda n: cl.rev(lookup(n))
760 key = lambda n: cl.rev(lookup(n))
739 return sorted(nodes, key=key)
761 return sorted(nodes, key=key)
740
762
741
763
742 def _resolvenarrowrevisioninfo(
764 def _resolvenarrowrevisioninfo(
743 cl,
765 cl,
744 store,
766 store,
745 ischangelog,
767 ischangelog,
746 rev,
768 rev,
747 linkrev,
769 linkrev,
748 linknode,
770 linknode,
749 clrevtolocalrev,
771 clrevtolocalrev,
750 fullclnodes,
772 fullclnodes,
751 precomputedellipsis,
773 precomputedellipsis,
752 ):
774 ):
753 linkparents = precomputedellipsis[linkrev]
775 linkparents = precomputedellipsis[linkrev]
754
776
755 def local(clrev):
777 def local(clrev):
756 """Turn a changelog revnum into a local revnum.
778 """Turn a changelog revnum into a local revnum.
757
779
758 The ellipsis dag is stored as revnums on the changelog,
780 The ellipsis dag is stored as revnums on the changelog,
759 but when we're producing ellipsis entries for
781 but when we're producing ellipsis entries for
760 non-changelog revlogs, we need to turn those numbers into
782 non-changelog revlogs, we need to turn those numbers into
761 something local. This does that for us, and during the
783 something local. This does that for us, and during the
762 changelog sending phase will also expand the stored
784 changelog sending phase will also expand the stored
763 mappings as needed.
785 mappings as needed.
764 """
786 """
765 if clrev == nullrev:
787 if clrev == nullrev:
766 return nullrev
788 return nullrev
767
789
768 if ischangelog:
790 if ischangelog:
769 return clrev
791 return clrev
770
792
771 # Walk the ellipsis-ized changelog breadth-first looking for a
793 # Walk the ellipsis-ized changelog breadth-first looking for a
772 # change that has been linked from the current revlog.
794 # change that has been linked from the current revlog.
773 #
795 #
774 # For a flat manifest revlog only a single step should be necessary
796 # For a flat manifest revlog only a single step should be necessary
775 # as all relevant changelog entries are relevant to the flat
797 # as all relevant changelog entries are relevant to the flat
776 # manifest.
798 # manifest.
777 #
799 #
778 # For a filelog or tree manifest dirlog however not every changelog
800 # For a filelog or tree manifest dirlog however not every changelog
779 # entry will have been relevant, so we need to skip some changelog
801 # entry will have been relevant, so we need to skip some changelog
780 # nodes even after ellipsis-izing.
802 # nodes even after ellipsis-izing.
781 walk = [clrev]
803 walk = [clrev]
782 while walk:
804 while walk:
783 p = walk[0]
805 p = walk[0]
784 walk = walk[1:]
806 walk = walk[1:]
785 if p in clrevtolocalrev:
807 if p in clrevtolocalrev:
786 return clrevtolocalrev[p]
808 return clrevtolocalrev[p]
787 elif p in fullclnodes:
809 elif p in fullclnodes:
788 walk.extend([pp for pp in cl.parentrevs(p) if pp != nullrev])
810 walk.extend([pp for pp in cl.parentrevs(p) if pp != nullrev])
789 elif p in precomputedellipsis:
811 elif p in precomputedellipsis:
790 walk.extend(
812 walk.extend(
791 [pp for pp in precomputedellipsis[p] if pp != nullrev]
813 [pp for pp in precomputedellipsis[p] if pp != nullrev]
792 )
814 )
793 else:
815 else:
794 # In this case, we've got an ellipsis with parents
816 # In this case, we've got an ellipsis with parents
795 # outside the current bundle (likely an
817 # outside the current bundle (likely an
796 # incremental pull). We "know" that we can use the
818 # incremental pull). We "know" that we can use the
797 # value of this same revlog at whatever revision
819 # value of this same revlog at whatever revision
798 # is pointed to by linknode. "Know" is in scare
820 # is pointed to by linknode. "Know" is in scare
799 # quotes because I haven't done enough examination
821 # quotes because I haven't done enough examination
800 # of edge cases to convince myself this is really
822 # of edge cases to convince myself this is really
801 # a fact - it works for all the (admittedly
823 # a fact - it works for all the (admittedly
802 # thorough) cases in our testsuite, but I would be
824 # thorough) cases in our testsuite, but I would be
803 # somewhat unsurprised to find a case in the wild
825 # somewhat unsurprised to find a case in the wild
804 # where this breaks down a bit. That said, I don't
826 # where this breaks down a bit. That said, I don't
805 # know if it would hurt anything.
827 # know if it would hurt anything.
806 for i in pycompat.xrange(rev, 0, -1):
828 for i in pycompat.xrange(rev, 0, -1):
807 if store.linkrev(i) == clrev:
829 if store.linkrev(i) == clrev:
808 return i
830 return i
809 # We failed to resolve a parent for this node, so
831 # We failed to resolve a parent for this node, so
810 # we crash the changegroup construction.
832 # we crash the changegroup construction.
811 if util.safehasattr(store, 'target'):
833 if util.safehasattr(store, 'target'):
812 target = store.display_id
834 target = store.display_id
813 else:
835 else:
814 # some revlog not actually a revlog
836 # some revlog not actually a revlog
815 target = store._revlog.display_id
837 target = store._revlog.display_id
816
838
817 raise error.Abort(
839 raise error.Abort(
818 b"unable to resolve parent while packing '%s' %r"
840 b"unable to resolve parent while packing '%s' %r"
819 b' for changeset %r' % (target, rev, clrev)
841 b' for changeset %r' % (target, rev, clrev)
820 )
842 )
821
843
822 return nullrev
844 return nullrev
823
845
824 if not linkparents or (store.parentrevs(rev) == (nullrev, nullrev)):
846 if not linkparents or (store.parentrevs(rev) == (nullrev, nullrev)):
825 p1, p2 = nullrev, nullrev
847 p1, p2 = nullrev, nullrev
826 elif len(linkparents) == 1:
848 elif len(linkparents) == 1:
827 (p1,) = sorted(local(p) for p in linkparents)
849 (p1,) = sorted(local(p) for p in linkparents)
828 p2 = nullrev
850 p2 = nullrev
829 else:
851 else:
830 p1, p2 = sorted(local(p) for p in linkparents)
852 p1, p2 = sorted(local(p) for p in linkparents)
831
853
832 p1node, p2node = store.node(p1), store.node(p2)
854 p1node, p2node = store.node(p1), store.node(p2)
833
855
834 return p1node, p2node, linknode
856 return p1node, p2node, linknode
835
857
836
858
837 def deltagroup(
859 def deltagroup(
838 repo,
860 repo,
839 store,
861 store,
840 nodes,
862 nodes,
841 ischangelog,
863 ischangelog,
842 lookup,
864 lookup,
843 forcedeltaparentprev,
865 forcedeltaparentprev,
844 topic=None,
866 topic=None,
845 ellipses=False,
867 ellipses=False,
846 clrevtolocalrev=None,
868 clrevtolocalrev=None,
847 fullclnodes=None,
869 fullclnodes=None,
848 precomputedellipsis=None,
870 precomputedellipsis=None,
849 sidedata_helpers=None,
871 sidedata_helpers=None,
850 ):
872 ):
851 """Calculate deltas for a set of revisions.
873 """Calculate deltas for a set of revisions.
852
874
853 Is a generator of ``revisiondelta`` instances.
875 Is a generator of ``revisiondelta`` instances.
854
876
855 If topic is not None, progress detail will be generated using this
877 If topic is not None, progress detail will be generated using this
856 topic name (e.g. changesets, manifests, etc).
878 topic name (e.g. changesets, manifests, etc).
857
879
858 See `revlogutil.sidedata.get_sidedata_helpers` for the doc on
880 See `revlogutil.sidedata.get_sidedata_helpers` for the doc on
859 `sidedata_helpers`.
881 `sidedata_helpers`.
860 """
882 """
861 if not nodes:
883 if not nodes:
862 return
884 return
863
885
864 cl = repo.changelog
886 cl = repo.changelog
865
887
866 if ischangelog:
888 if ischangelog:
867 # `hg log` shows changesets in storage order. To preserve order
889 # `hg log` shows changesets in storage order. To preserve order
868 # across clones, send out changesets in storage order.
890 # across clones, send out changesets in storage order.
869 nodesorder = b'storage'
891 nodesorder = b'storage'
870 elif ellipses:
892 elif ellipses:
871 nodes = _sortnodesellipsis(store, nodes, cl, lookup)
893 nodes = _sortnodesellipsis(store, nodes, cl, lookup)
872 nodesorder = b'nodes'
894 nodesorder = b'nodes'
873 else:
895 else:
874 nodesorder = None
896 nodesorder = None
875
897
876 # Perform ellipses filtering and revision massaging. We do this before
898 # Perform ellipses filtering and revision massaging. We do this before
877 # emitrevisions() because a) filtering out revisions creates less work
899 # emitrevisions() because a) filtering out revisions creates less work
878 # for emitrevisions() b) dropping revisions would break emitrevisions()'s
900 # for emitrevisions() b) dropping revisions would break emitrevisions()'s
879 # assumptions about delta choices and we would possibly send a delta
901 # assumptions about delta choices and we would possibly send a delta
880 # referencing a missing base revision.
902 # referencing a missing base revision.
881 #
903 #
882 # Also, calling lookup() has side-effects with regards to populating
904 # Also, calling lookup() has side-effects with regards to populating
883 # data structures. If we don't call lookup() for each node or if we call
905 # data structures. If we don't call lookup() for each node or if we call
884 # lookup() after the first pass through each node, things can break -
906 # lookup() after the first pass through each node, things can break -
885 # possibly intermittently depending on the python hash seed! For that
907 # possibly intermittently depending on the python hash seed! For that
886 # reason, we store a mapping of all linknodes during the initial node
908 # reason, we store a mapping of all linknodes during the initial node
887 # pass rather than use lookup() on the output side.
909 # pass rather than use lookup() on the output side.
888 if ellipses:
910 if ellipses:
889 filtered = []
911 filtered = []
890 adjustedparents = {}
912 adjustedparents = {}
891 linknodes = {}
913 linknodes = {}
892
914
893 for node in nodes:
915 for node in nodes:
894 rev = store.rev(node)
916 rev = store.rev(node)
895 linknode = lookup(node)
917 linknode = lookup(node)
896 linkrev = cl.rev(linknode)
918 linkrev = cl.rev(linknode)
897 clrevtolocalrev[linkrev] = rev
919 clrevtolocalrev[linkrev] = rev
898
920
899 # If linknode is in fullclnodes, it means the corresponding
921 # If linknode is in fullclnodes, it means the corresponding
900 # changeset was a full changeset and is being sent unaltered.
922 # changeset was a full changeset and is being sent unaltered.
901 if linknode in fullclnodes:
923 if linknode in fullclnodes:
902 linknodes[node] = linknode
924 linknodes[node] = linknode
903
925
904 # If the corresponding changeset wasn't in the set computed
926 # If the corresponding changeset wasn't in the set computed
905 # as relevant to us, it should be dropped outright.
927 # as relevant to us, it should be dropped outright.
906 elif linkrev not in precomputedellipsis:
928 elif linkrev not in precomputedellipsis:
907 continue
929 continue
908
930
909 else:
931 else:
910 # We could probably do this later and avoid the dict
932 # We could probably do this later and avoid the dict
911 # holding state. But it likely doesn't matter.
933 # holding state. But it likely doesn't matter.
912 p1node, p2node, linknode = _resolvenarrowrevisioninfo(
934 p1node, p2node, linknode = _resolvenarrowrevisioninfo(
913 cl,
935 cl,
914 store,
936 store,
915 ischangelog,
937 ischangelog,
916 rev,
938 rev,
917 linkrev,
939 linkrev,
918 linknode,
940 linknode,
919 clrevtolocalrev,
941 clrevtolocalrev,
920 fullclnodes,
942 fullclnodes,
921 precomputedellipsis,
943 precomputedellipsis,
922 )
944 )
923
945
924 adjustedparents[node] = (p1node, p2node)
946 adjustedparents[node] = (p1node, p2node)
925 linknodes[node] = linknode
947 linknodes[node] = linknode
926
948
927 filtered.append(node)
949 filtered.append(node)
928
950
929 nodes = filtered
951 nodes = filtered
930
952
931 # We expect the first pass to be fast, so we only engage the progress
953 # We expect the first pass to be fast, so we only engage the progress
932 # meter for constructing the revision deltas.
954 # meter for constructing the revision deltas.
933 progress = None
955 progress = None
934 if topic is not None:
956 if topic is not None:
935 progress = repo.ui.makeprogress(
957 progress = repo.ui.makeprogress(
936 topic, unit=_(b'chunks'), total=len(nodes)
958 topic, unit=_(b'chunks'), total=len(nodes)
937 )
959 )
938
960
939 configtarget = repo.ui.config(b'devel', b'bundle.delta')
961 configtarget = repo.ui.config(b'devel', b'bundle.delta')
940 if configtarget not in (b'', b'p1', b'full'):
962 if configtarget not in (b'', b'p1', b'full'):
941 msg = _(b"""config "devel.bundle.delta" as unknown value: %s""")
963 msg = _(b"""config "devel.bundle.delta" as unknown value: %s""")
942 repo.ui.warn(msg % configtarget)
964 repo.ui.warn(msg % configtarget)
943
965
944 deltamode = repository.CG_DELTAMODE_STD
966 deltamode = repository.CG_DELTAMODE_STD
945 if forcedeltaparentprev:
967 if forcedeltaparentprev:
946 deltamode = repository.CG_DELTAMODE_PREV
968 deltamode = repository.CG_DELTAMODE_PREV
947 elif configtarget == b'p1':
969 elif configtarget == b'p1':
948 deltamode = repository.CG_DELTAMODE_P1
970 deltamode = repository.CG_DELTAMODE_P1
949 elif configtarget == b'full':
971 elif configtarget == b'full':
950 deltamode = repository.CG_DELTAMODE_FULL
972 deltamode = repository.CG_DELTAMODE_FULL
951
973
952 revisions = store.emitrevisions(
974 revisions = store.emitrevisions(
953 nodes,
975 nodes,
954 nodesorder=nodesorder,
976 nodesorder=nodesorder,
955 revisiondata=True,
977 revisiondata=True,
956 assumehaveparentrevisions=not ellipses,
978 assumehaveparentrevisions=not ellipses,
957 deltamode=deltamode,
979 deltamode=deltamode,
958 sidedata_helpers=sidedata_helpers,
980 sidedata_helpers=sidedata_helpers,
959 )
981 )
960
982
961 for i, revision in enumerate(revisions):
983 for i, revision in enumerate(revisions):
962 if progress:
984 if progress:
963 progress.update(i + 1)
985 progress.update(i + 1)
964
986
965 if ellipses:
987 if ellipses:
966 linknode = linknodes[revision.node]
988 linknode = linknodes[revision.node]
967
989
968 if revision.node in adjustedparents:
990 if revision.node in adjustedparents:
969 p1node, p2node = adjustedparents[revision.node]
991 p1node, p2node = adjustedparents[revision.node]
970 revision.p1node = p1node
992 revision.p1node = p1node
971 revision.p2node = p2node
993 revision.p2node = p2node
972 revision.flags |= repository.REVISION_FLAG_ELLIPSIS
994 revision.flags |= repository.REVISION_FLAG_ELLIPSIS
973
995
974 else:
996 else:
975 linknode = lookup(revision.node)
997 linknode = lookup(revision.node)
976
998
977 revision.linknode = linknode
999 revision.linknode = linknode
978 yield revision
1000 yield revision
979
1001
980 if progress:
1002 if progress:
981 progress.complete()
1003 progress.complete()
982
1004
983
1005
984 class cgpacker(object):
1006 class cgpacker(object):
985 def __init__(
1007 def __init__(
986 self,
1008 self,
987 repo,
1009 repo,
988 oldmatcher,
1010 oldmatcher,
989 matcher,
1011 matcher,
990 version,
1012 version,
991 builddeltaheader,
1013 builddeltaheader,
992 manifestsend,
1014 manifestsend,
993 forcedeltaparentprev=False,
1015 forcedeltaparentprev=False,
994 bundlecaps=None,
1016 bundlecaps=None,
995 ellipses=False,
1017 ellipses=False,
996 shallow=False,
1018 shallow=False,
997 ellipsisroots=None,
1019 ellipsisroots=None,
998 fullnodes=None,
1020 fullnodes=None,
999 remote_sidedata=None,
1021 remote_sidedata=None,
1000 ):
1022 ):
1001 """Given a source repo, construct a bundler.
1023 """Given a source repo, construct a bundler.
1002
1024
1003 oldmatcher is a matcher that matches on files the client already has.
1025 oldmatcher is a matcher that matches on files the client already has.
1004 These will not be included in the changegroup.
1026 These will not be included in the changegroup.
1005
1027
1006 matcher is a matcher that matches on files to include in the
1028 matcher is a matcher that matches on files to include in the
1007 changegroup. Used to facilitate sparse changegroups.
1029 changegroup. Used to facilitate sparse changegroups.
1008
1030
1009 forcedeltaparentprev indicates whether delta parents must be against
1031 forcedeltaparentprev indicates whether delta parents must be against
1010 the previous revision in a delta group. This should only be used for
1032 the previous revision in a delta group. This should only be used for
1011 compatibility with changegroup version 1.
1033 compatibility with changegroup version 1.
1012
1034
1013 builddeltaheader is a callable that constructs the header for a group
1035 builddeltaheader is a callable that constructs the header for a group
1014 delta.
1036 delta.
1015
1037
1016 manifestsend is a chunk to send after manifests have been fully emitted.
1038 manifestsend is a chunk to send after manifests have been fully emitted.
1017
1039
1018 ellipses indicates whether ellipsis serving mode is enabled.
1040 ellipses indicates whether ellipsis serving mode is enabled.
1019
1041
1020 bundlecaps is optional and can be used to specify the set of
1042 bundlecaps is optional and can be used to specify the set of
1021 capabilities which can be used to build the bundle. While bundlecaps is
1043 capabilities which can be used to build the bundle. While bundlecaps is
1022 unused in core Mercurial, extensions rely on this feature to communicate
1044 unused in core Mercurial, extensions rely on this feature to communicate
1023 capabilities to customize the changegroup packer.
1045 capabilities to customize the changegroup packer.
1024
1046
1025 shallow indicates whether shallow data might be sent. The packer may
1047 shallow indicates whether shallow data might be sent. The packer may
1026 need to pack file contents not introduced by the changes being packed.
1048 need to pack file contents not introduced by the changes being packed.
1027
1049
1028 fullnodes is the set of changelog nodes which should not be ellipsis
1050 fullnodes is the set of changelog nodes which should not be ellipsis
1029 nodes. We store this rather than the set of nodes that should be
1051 nodes. We store this rather than the set of nodes that should be
1030 ellipsis because for very large histories we expect this to be
1052 ellipsis because for very large histories we expect this to be
1031 significantly smaller.
1053 significantly smaller.
1032
1054
1033 remote_sidedata is the set of sidedata categories wanted by the remote.
1055 remote_sidedata is the set of sidedata categories wanted by the remote.
1034 """
1056 """
1035 assert oldmatcher
1057 assert oldmatcher
1036 assert matcher
1058 assert matcher
1037 self._oldmatcher = oldmatcher
1059 self._oldmatcher = oldmatcher
1038 self._matcher = matcher
1060 self._matcher = matcher
1039
1061
1040 self.version = version
1062 self.version = version
1041 self._forcedeltaparentprev = forcedeltaparentprev
1063 self._forcedeltaparentprev = forcedeltaparentprev
1042 self._builddeltaheader = builddeltaheader
1064 self._builddeltaheader = builddeltaheader
1043 self._manifestsend = manifestsend
1065 self._manifestsend = manifestsend
1044 self._ellipses = ellipses
1066 self._ellipses = ellipses
1045
1067
1046 # Set of capabilities we can use to build the bundle.
1068 # Set of capabilities we can use to build the bundle.
1047 if bundlecaps is None:
1069 if bundlecaps is None:
1048 bundlecaps = set()
1070 bundlecaps = set()
1049 self._bundlecaps = bundlecaps
1071 self._bundlecaps = bundlecaps
1050 if remote_sidedata is None:
1072 if remote_sidedata is None:
1051 remote_sidedata = set()
1073 remote_sidedata = set()
1052 self._remote_sidedata = remote_sidedata
1074 self._remote_sidedata = remote_sidedata
1053 self._isshallow = shallow
1075 self._isshallow = shallow
1054 self._fullclnodes = fullnodes
1076 self._fullclnodes = fullnodes
1055
1077
1056 # Maps ellipsis revs to their roots at the changelog level.
1078 # Maps ellipsis revs to their roots at the changelog level.
1057 self._precomputedellipsis = ellipsisroots
1079 self._precomputedellipsis = ellipsisroots
1058
1080
1059 self._repo = repo
1081 self._repo = repo
1060
1082
1061 if self._repo.ui.verbose and not self._repo.ui.debugflag:
1083 if self._repo.ui.verbose and not self._repo.ui.debugflag:
1062 self._verbosenote = self._repo.ui.note
1084 self._verbosenote = self._repo.ui.note
1063 else:
1085 else:
1064 self._verbosenote = lambda s: None
1086 self._verbosenote = lambda s: None
1065
1087
1066 def generate(
1088 def generate(
1067 self, commonrevs, clnodes, fastpathlinkrev, source, changelog=True
1089 self, commonrevs, clnodes, fastpathlinkrev, source, changelog=True
1068 ):
1090 ):
1069 """Yield a sequence of changegroup byte chunks.
1091 """Yield a sequence of changegroup byte chunks.
1070 If changelog is False, changelog data won't be added to changegroup
1092 If changelog is False, changelog data won't be added to changegroup
1071 """
1093 """
1072
1094
1073 repo = self._repo
1095 repo = self._repo
1074 cl = repo.changelog
1096 cl = repo.changelog
1075
1097
1076 self._verbosenote(_(b'uncompressed size of bundle content:\n'))
1098 self._verbosenote(_(b'uncompressed size of bundle content:\n'))
1077 size = 0
1099 size = 0
1078
1100
1079 sidedata_helpers = None
1101 sidedata_helpers = None
1080 if self.version == b'04':
1102 if self.version == b'04':
1081 remote_sidedata = self._remote_sidedata
1103 remote_sidedata = self._remote_sidedata
1082 if source == b'strip':
1104 if source == b'strip':
1083 # We're our own remote when stripping, get the no-op helpers
1105 # We're our own remote when stripping, get the no-op helpers
1084 # TODO a better approach would be for the strip bundle to
1106 # TODO a better approach would be for the strip bundle to
1085 # correctly advertise its sidedata categories directly.
1107 # correctly advertise its sidedata categories directly.
1086 remote_sidedata = repo._wanted_sidedata
1108 remote_sidedata = repo._wanted_sidedata
1087 sidedata_helpers = sidedatamod.get_sidedata_helpers(
1109 sidedata_helpers = sidedatamod.get_sidedata_helpers(
1088 repo, remote_sidedata
1110 repo, remote_sidedata
1089 )
1111 )
1090
1112
1091 clstate, deltas = self._generatechangelog(
1113 clstate, deltas = self._generatechangelog(
1092 cl,
1114 cl,
1093 clnodes,
1115 clnodes,
1094 generate=changelog,
1116 generate=changelog,
1095 sidedata_helpers=sidedata_helpers,
1117 sidedata_helpers=sidedata_helpers,
1096 )
1118 )
1097 for delta in deltas:
1119 for delta in deltas:
1098 for chunk in _revisiondeltatochunks(
1120 for chunk in _revisiondeltatochunks(
1099 self._repo, delta, self._builddeltaheader
1121 self._repo, delta, self._builddeltaheader
1100 ):
1122 ):
1101 size += len(chunk)
1123 size += len(chunk)
1102 yield chunk
1124 yield chunk
1103
1125
1104 close = closechunk()
1126 close = closechunk()
1105 size += len(close)
1127 size += len(close)
1106 yield closechunk()
1128 yield closechunk()
1107
1129
1108 self._verbosenote(_(b'%8.i (changelog)\n') % size)
1130 self._verbosenote(_(b'%8.i (changelog)\n') % size)
1109
1131
1110 clrevorder = clstate[b'clrevorder']
1132 clrevorder = clstate[b'clrevorder']
1111 manifests = clstate[b'manifests']
1133 manifests = clstate[b'manifests']
1112 changedfiles = clstate[b'changedfiles']
1134 changedfiles = clstate[b'changedfiles']
1113
1135
1114 # We need to make sure that the linkrev in the changegroup refers to
1136 # We need to make sure that the linkrev in the changegroup refers to
1115 # the first changeset that introduced the manifest or file revision.
1137 # the first changeset that introduced the manifest or file revision.
1116 # The fastpath is usually safer than the slowpath, because the filelogs
1138 # The fastpath is usually safer than the slowpath, because the filelogs
1117 # are walked in revlog order.
1139 # are walked in revlog order.
1118 #
1140 #
1119 # When taking the slowpath when the manifest revlog uses generaldelta,
1141 # When taking the slowpath when the manifest revlog uses generaldelta,
1120 # the manifest may be walked in the "wrong" order. Without 'clrevorder',
1142 # the manifest may be walked in the "wrong" order. Without 'clrevorder',
1121 # we would get an incorrect linkrev (see fix in cc0ff93d0c0c).
1143 # we would get an incorrect linkrev (see fix in cc0ff93d0c0c).
1122 #
1144 #
1123 # When taking the fastpath, we are only vulnerable to reordering
1145 # When taking the fastpath, we are only vulnerable to reordering
1124 # of the changelog itself. The changelog never uses generaldelta and is
1146 # of the changelog itself. The changelog never uses generaldelta and is
1125 # never reordered. To handle this case, we simply take the slowpath,
1147 # never reordered. To handle this case, we simply take the slowpath,
1126 # which already has the 'clrevorder' logic. This was also fixed in
1148 # which already has the 'clrevorder' logic. This was also fixed in
1127 # cc0ff93d0c0c.
1149 # cc0ff93d0c0c.
1128
1150
1129 # Treemanifests don't work correctly with fastpathlinkrev
1151 # Treemanifests don't work correctly with fastpathlinkrev
1130 # either, because we don't discover which directory nodes to
1152 # either, because we don't discover which directory nodes to
1131 # send along with files. This could probably be fixed.
1153 # send along with files. This could probably be fixed.
1132 fastpathlinkrev = fastpathlinkrev and not scmutil.istreemanifest(repo)
1154 fastpathlinkrev = fastpathlinkrev and not scmutil.istreemanifest(repo)
1133
1155
1134 fnodes = {} # needed file nodes
1156 fnodes = {} # needed file nodes
1135
1157
1136 size = 0
1158 size = 0
1137 it = self.generatemanifests(
1159 it = self.generatemanifests(
1138 commonrevs,
1160 commonrevs,
1139 clrevorder,
1161 clrevorder,
1140 fastpathlinkrev,
1162 fastpathlinkrev,
1141 manifests,
1163 manifests,
1142 fnodes,
1164 fnodes,
1143 source,
1165 source,
1144 clstate[b'clrevtomanifestrev'],
1166 clstate[b'clrevtomanifestrev'],
1145 sidedata_helpers=sidedata_helpers,
1167 sidedata_helpers=sidedata_helpers,
1146 )
1168 )
1147
1169
1148 for tree, deltas in it:
1170 for tree, deltas in it:
1149 if tree:
1171 if tree:
1150 assert self.version in (b'03', b'04')
1172 assert self.version in (b'03', b'04')
1151 chunk = _fileheader(tree)
1173 chunk = _fileheader(tree)
1152 size += len(chunk)
1174 size += len(chunk)
1153 yield chunk
1175 yield chunk
1154
1176
1155 for delta in deltas:
1177 for delta in deltas:
1156 chunks = _revisiondeltatochunks(
1178 chunks = _revisiondeltatochunks(
1157 self._repo, delta, self._builddeltaheader
1179 self._repo, delta, self._builddeltaheader
1158 )
1180 )
1159 for chunk in chunks:
1181 for chunk in chunks:
1160 size += len(chunk)
1182 size += len(chunk)
1161 yield chunk
1183 yield chunk
1162
1184
1163 close = closechunk()
1185 close = closechunk()
1164 size += len(close)
1186 size += len(close)
1165 yield close
1187 yield close
1166
1188
1167 self._verbosenote(_(b'%8.i (manifests)\n') % size)
1189 self._verbosenote(_(b'%8.i (manifests)\n') % size)
1168 yield self._manifestsend
1190 yield self._manifestsend
1169
1191
1170 mfdicts = None
1192 mfdicts = None
1171 if self._ellipses and self._isshallow:
1193 if self._ellipses and self._isshallow:
1172 mfdicts = [
1194 mfdicts = [
1173 (repo.manifestlog[n].read(), lr)
1195 (repo.manifestlog[n].read(), lr)
1174 for (n, lr) in pycompat.iteritems(manifests)
1196 for (n, lr) in pycompat.iteritems(manifests)
1175 ]
1197 ]
1176
1198
1177 manifests.clear()
1199 manifests.clear()
1178 clrevs = {cl.rev(x) for x in clnodes}
1200 clrevs = {cl.rev(x) for x in clnodes}
1179
1201
1180 it = self.generatefiles(
1202 it = self.generatefiles(
1181 changedfiles,
1203 changedfiles,
1182 commonrevs,
1204 commonrevs,
1183 source,
1205 source,
1184 mfdicts,
1206 mfdicts,
1185 fastpathlinkrev,
1207 fastpathlinkrev,
1186 fnodes,
1208 fnodes,
1187 clrevs,
1209 clrevs,
1188 sidedata_helpers=sidedata_helpers,
1210 sidedata_helpers=sidedata_helpers,
1189 )
1211 )
1190
1212
1191 for path, deltas in it:
1213 for path, deltas in it:
1192 h = _fileheader(path)
1214 h = _fileheader(path)
1193 size = len(h)
1215 size = len(h)
1194 yield h
1216 yield h
1195
1217
1196 for delta in deltas:
1218 for delta in deltas:
1197 chunks = _revisiondeltatochunks(
1219 chunks = _revisiondeltatochunks(
1198 self._repo, delta, self._builddeltaheader
1220 self._repo, delta, self._builddeltaheader
1199 )
1221 )
1200 for chunk in chunks:
1222 for chunk in chunks:
1201 size += len(chunk)
1223 size += len(chunk)
1202 yield chunk
1224 yield chunk
1203
1225
1204 close = closechunk()
1226 close = closechunk()
1205 size += len(close)
1227 size += len(close)
1206 yield close
1228 yield close
1207
1229
1208 self._verbosenote(_(b'%8.i %s\n') % (size, path))
1230 self._verbosenote(_(b'%8.i %s\n') % (size, path))
1209
1231
1210 yield closechunk()
1232 yield closechunk()
1211
1233
1212 if clnodes:
1234 if clnodes:
1213 repo.hook(b'outgoing', node=hex(clnodes[0]), source=source)
1235 repo.hook(b'outgoing', node=hex(clnodes[0]), source=source)
1214
1236
1215 def _generatechangelog(
1237 def _generatechangelog(
1216 self, cl, nodes, generate=True, sidedata_helpers=None
1238 self, cl, nodes, generate=True, sidedata_helpers=None
1217 ):
1239 ):
1218 """Generate data for changelog chunks.
1240 """Generate data for changelog chunks.
1219
1241
1220 Returns a 2-tuple of a dict containing state and an iterable of
1242 Returns a 2-tuple of a dict containing state and an iterable of
1221 byte chunks. The state will not be fully populated until the
1243 byte chunks. The state will not be fully populated until the
1222 chunk stream has been fully consumed.
1244 chunk stream has been fully consumed.
1223
1245
1224 if generate is False, the state will be fully populated and no chunk
1246 if generate is False, the state will be fully populated and no chunk
1225 stream will be yielded
1247 stream will be yielded
1226
1248
1227 See `revlogutil.sidedata.get_sidedata_helpers` for the doc on
1249 See `revlogutil.sidedata.get_sidedata_helpers` for the doc on
1228 `sidedata_helpers`.
1250 `sidedata_helpers`.
1229 """
1251 """
1230 clrevorder = {}
1252 clrevorder = {}
1231 manifests = {}
1253 manifests = {}
1232 mfl = self._repo.manifestlog
1254 mfl = self._repo.manifestlog
1233 changedfiles = set()
1255 changedfiles = set()
1234 clrevtomanifestrev = {}
1256 clrevtomanifestrev = {}
1235
1257
1236 state = {
1258 state = {
1237 b'clrevorder': clrevorder,
1259 b'clrevorder': clrevorder,
1238 b'manifests': manifests,
1260 b'manifests': manifests,
1239 b'changedfiles': changedfiles,
1261 b'changedfiles': changedfiles,
1240 b'clrevtomanifestrev': clrevtomanifestrev,
1262 b'clrevtomanifestrev': clrevtomanifestrev,
1241 }
1263 }
1242
1264
1243 if not (generate or self._ellipses):
1265 if not (generate or self._ellipses):
1244 # sort the nodes in storage order
1266 # sort the nodes in storage order
1245 nodes = sorted(nodes, key=cl.rev)
1267 nodes = sorted(nodes, key=cl.rev)
1246 for node in nodes:
1268 for node in nodes:
1247 c = cl.changelogrevision(node)
1269 c = cl.changelogrevision(node)
1248 clrevorder[node] = len(clrevorder)
1270 clrevorder[node] = len(clrevorder)
1249 # record the first changeset introducing this manifest version
1271 # record the first changeset introducing this manifest version
1250 manifests.setdefault(c.manifest, node)
1272 manifests.setdefault(c.manifest, node)
1251 # Record a complete list of potentially-changed files in
1273 # Record a complete list of potentially-changed files in
1252 # this manifest.
1274 # this manifest.
1253 changedfiles.update(c.files)
1275 changedfiles.update(c.files)
1254
1276
1255 return state, ()
1277 return state, ()
1256
1278
1257 # Callback for the changelog, used to collect changed files and
1279 # Callback for the changelog, used to collect changed files and
1258 # manifest nodes.
1280 # manifest nodes.
1259 # Returns the linkrev node (identity in the changelog case).
1281 # Returns the linkrev node (identity in the changelog case).
1260 def lookupcl(x):
1282 def lookupcl(x):
1261 c = cl.changelogrevision(x)
1283 c = cl.changelogrevision(x)
1262 clrevorder[x] = len(clrevorder)
1284 clrevorder[x] = len(clrevorder)
1263
1285
1264 if self._ellipses:
1286 if self._ellipses:
1265 # Only update manifests if x is going to be sent. Otherwise we
1287 # Only update manifests if x is going to be sent. Otherwise we
1266 # end up with bogus linkrevs specified for manifests and
1288 # end up with bogus linkrevs specified for manifests and
1267 # we skip some manifest nodes that we should otherwise
1289 # we skip some manifest nodes that we should otherwise
1268 # have sent.
1290 # have sent.
1269 if (
1291 if (
1270 x in self._fullclnodes
1292 x in self._fullclnodes
1271 or cl.rev(x) in self._precomputedellipsis
1293 or cl.rev(x) in self._precomputedellipsis
1272 ):
1294 ):
1273
1295
1274 manifestnode = c.manifest
1296 manifestnode = c.manifest
1275 # Record the first changeset introducing this manifest
1297 # Record the first changeset introducing this manifest
1276 # version.
1298 # version.
1277 manifests.setdefault(manifestnode, x)
1299 manifests.setdefault(manifestnode, x)
1278 # Set this narrow-specific dict so we have the lowest
1300 # Set this narrow-specific dict so we have the lowest
1279 # manifest revnum to look up for this cl revnum. (Part of
1301 # manifest revnum to look up for this cl revnum. (Part of
1280 # mapping changelog ellipsis parents to manifest ellipsis
1302 # mapping changelog ellipsis parents to manifest ellipsis
1281 # parents)
1303 # parents)
1282 clrevtomanifestrev.setdefault(
1304 clrevtomanifestrev.setdefault(
1283 cl.rev(x), mfl.rev(manifestnode)
1305 cl.rev(x), mfl.rev(manifestnode)
1284 )
1306 )
1285 # We can't trust the changed files list in the changeset if the
1307 # We can't trust the changed files list in the changeset if the
1286 # client requested a shallow clone.
1308 # client requested a shallow clone.
1287 if self._isshallow:
1309 if self._isshallow:
1288 changedfiles.update(mfl[c.manifest].read().keys())
1310 changedfiles.update(mfl[c.manifest].read().keys())
1289 else:
1311 else:
1290 changedfiles.update(c.files)
1312 changedfiles.update(c.files)
1291 else:
1313 else:
1292 # record the first changeset introducing this manifest version
1314 # record the first changeset introducing this manifest version
1293 manifests.setdefault(c.manifest, x)
1315 manifests.setdefault(c.manifest, x)
1294 # Record a complete list of potentially-changed files in
1316 # Record a complete list of potentially-changed files in
1295 # this manifest.
1317 # this manifest.
1296 changedfiles.update(c.files)
1318 changedfiles.update(c.files)
1297
1319
1298 return x
1320 return x
1299
1321
1300 gen = deltagroup(
1322 gen = deltagroup(
1301 self._repo,
1323 self._repo,
1302 cl,
1324 cl,
1303 nodes,
1325 nodes,
1304 True,
1326 True,
1305 lookupcl,
1327 lookupcl,
1306 self._forcedeltaparentprev,
1328 self._forcedeltaparentprev,
1307 ellipses=self._ellipses,
1329 ellipses=self._ellipses,
1308 topic=_(b'changesets'),
1330 topic=_(b'changesets'),
1309 clrevtolocalrev={},
1331 clrevtolocalrev={},
1310 fullclnodes=self._fullclnodes,
1332 fullclnodes=self._fullclnodes,
1311 precomputedellipsis=self._precomputedellipsis,
1333 precomputedellipsis=self._precomputedellipsis,
1312 sidedata_helpers=sidedata_helpers,
1334 sidedata_helpers=sidedata_helpers,
1313 )
1335 )
1314
1336
1315 return state, gen
1337 return state, gen
1316
1338
1317 def generatemanifests(
1339 def generatemanifests(
1318 self,
1340 self,
1319 commonrevs,
1341 commonrevs,
1320 clrevorder,
1342 clrevorder,
1321 fastpathlinkrev,
1343 fastpathlinkrev,
1322 manifests,
1344 manifests,
1323 fnodes,
1345 fnodes,
1324 source,
1346 source,
1325 clrevtolocalrev,
1347 clrevtolocalrev,
1326 sidedata_helpers=None,
1348 sidedata_helpers=None,
1327 ):
1349 ):
1328 """Returns an iterator of changegroup chunks containing manifests.
1350 """Returns an iterator of changegroup chunks containing manifests.
1329
1351
1330 `source` is unused here, but is used by extensions like remotefilelog to
1352 `source` is unused here, but is used by extensions like remotefilelog to
1331 change what is sent based in pulls vs pushes, etc.
1353 change what is sent based in pulls vs pushes, etc.
1332
1354
1333 See `revlogutil.sidedata.get_sidedata_helpers` for the doc on
1355 See `revlogutil.sidedata.get_sidedata_helpers` for the doc on
1334 `sidedata_helpers`.
1356 `sidedata_helpers`.
1335 """
1357 """
1336 repo = self._repo
1358 repo = self._repo
1337 mfl = repo.manifestlog
1359 mfl = repo.manifestlog
1338 tmfnodes = {b'': manifests}
1360 tmfnodes = {b'': manifests}
1339
1361
1340 # Callback for the manifest, used to collect linkrevs for filelog
1362 # Callback for the manifest, used to collect linkrevs for filelog
1341 # revisions.
1363 # revisions.
1342 # Returns the linkrev node (collected in lookupcl).
1364 # Returns the linkrev node (collected in lookupcl).
1343 def makelookupmflinknode(tree, nodes):
1365 def makelookupmflinknode(tree, nodes):
1344 if fastpathlinkrev:
1366 if fastpathlinkrev:
1345 assert not tree
1367 assert not tree
1346
1368
1347 # pytype: disable=unsupported-operands
1369 # pytype: disable=unsupported-operands
1348 return manifests.__getitem__
1370 return manifests.__getitem__
1349 # pytype: enable=unsupported-operands
1371 # pytype: enable=unsupported-operands
1350
1372
1351 def lookupmflinknode(x):
1373 def lookupmflinknode(x):
1352 """Callback for looking up the linknode for manifests.
1374 """Callback for looking up the linknode for manifests.
1353
1375
1354 Returns the linkrev node for the specified manifest.
1376 Returns the linkrev node for the specified manifest.
1355
1377
1356 SIDE EFFECT:
1378 SIDE EFFECT:
1357
1379
1358 1) fclnodes gets populated with the list of relevant
1380 1) fclnodes gets populated with the list of relevant
1359 file nodes if we're not using fastpathlinkrev
1381 file nodes if we're not using fastpathlinkrev
1360 2) When treemanifests are in use, collects treemanifest nodes
1382 2) When treemanifests are in use, collects treemanifest nodes
1361 to send
1383 to send
1362
1384
1363 Note that this means manifests must be completely sent to
1385 Note that this means manifests must be completely sent to
1364 the client before you can trust the list of files and
1386 the client before you can trust the list of files and
1365 treemanifests to send.
1387 treemanifests to send.
1366 """
1388 """
1367 clnode = nodes[x]
1389 clnode = nodes[x]
1368 mdata = mfl.get(tree, x).readfast(shallow=True)
1390 mdata = mfl.get(tree, x).readfast(shallow=True)
1369 for p, n, fl in mdata.iterentries():
1391 for p, n, fl in mdata.iterentries():
1370 if fl == b't': # subdirectory manifest
1392 if fl == b't': # subdirectory manifest
1371 subtree = tree + p + b'/'
1393 subtree = tree + p + b'/'
1372 tmfclnodes = tmfnodes.setdefault(subtree, {})
1394 tmfclnodes = tmfnodes.setdefault(subtree, {})
1373 tmfclnode = tmfclnodes.setdefault(n, clnode)
1395 tmfclnode = tmfclnodes.setdefault(n, clnode)
1374 if clrevorder[clnode] < clrevorder[tmfclnode]:
1396 if clrevorder[clnode] < clrevorder[tmfclnode]:
1375 tmfclnodes[n] = clnode
1397 tmfclnodes[n] = clnode
1376 else:
1398 else:
1377 f = tree + p
1399 f = tree + p
1378 fclnodes = fnodes.setdefault(f, {})
1400 fclnodes = fnodes.setdefault(f, {})
1379 fclnode = fclnodes.setdefault(n, clnode)
1401 fclnode = fclnodes.setdefault(n, clnode)
1380 if clrevorder[clnode] < clrevorder[fclnode]:
1402 if clrevorder[clnode] < clrevorder[fclnode]:
1381 fclnodes[n] = clnode
1403 fclnodes[n] = clnode
1382 return clnode
1404 return clnode
1383
1405
1384 return lookupmflinknode
1406 return lookupmflinknode
1385
1407
1386 while tmfnodes:
1408 while tmfnodes:
1387 tree, nodes = tmfnodes.popitem()
1409 tree, nodes = tmfnodes.popitem()
1388
1410
1389 should_visit = self._matcher.visitdir(tree[:-1])
1411 should_visit = self._matcher.visitdir(tree[:-1])
1390 if tree and not should_visit:
1412 if tree and not should_visit:
1391 continue
1413 continue
1392
1414
1393 store = mfl.getstorage(tree)
1415 store = mfl.getstorage(tree)
1394
1416
1395 if not should_visit:
1417 if not should_visit:
1396 # No nodes to send because this directory is out of
1418 # No nodes to send because this directory is out of
1397 # the client's view of the repository (probably
1419 # the client's view of the repository (probably
1398 # because of narrow clones). Do this even for the root
1420 # because of narrow clones). Do this even for the root
1399 # directory (tree=='')
1421 # directory (tree=='')
1400 prunednodes = []
1422 prunednodes = []
1401 else:
1423 else:
1402 # Avoid sending any manifest nodes we can prove the
1424 # Avoid sending any manifest nodes we can prove the
1403 # client already has by checking linkrevs. See the
1425 # client already has by checking linkrevs. See the
1404 # related comment in generatefiles().
1426 # related comment in generatefiles().
1405 prunednodes = self._prunemanifests(store, nodes, commonrevs)
1427 prunednodes = self._prunemanifests(store, nodes, commonrevs)
1406
1428
1407 if tree and not prunednodes:
1429 if tree and not prunednodes:
1408 continue
1430 continue
1409
1431
1410 lookupfn = makelookupmflinknode(tree, nodes)
1432 lookupfn = makelookupmflinknode(tree, nodes)
1411
1433
1412 deltas = deltagroup(
1434 deltas = deltagroup(
1413 self._repo,
1435 self._repo,
1414 store,
1436 store,
1415 prunednodes,
1437 prunednodes,
1416 False,
1438 False,
1417 lookupfn,
1439 lookupfn,
1418 self._forcedeltaparentprev,
1440 self._forcedeltaparentprev,
1419 ellipses=self._ellipses,
1441 ellipses=self._ellipses,
1420 topic=_(b'manifests'),
1442 topic=_(b'manifests'),
1421 clrevtolocalrev=clrevtolocalrev,
1443 clrevtolocalrev=clrevtolocalrev,
1422 fullclnodes=self._fullclnodes,
1444 fullclnodes=self._fullclnodes,
1423 precomputedellipsis=self._precomputedellipsis,
1445 precomputedellipsis=self._precomputedellipsis,
1424 sidedata_helpers=sidedata_helpers,
1446 sidedata_helpers=sidedata_helpers,
1425 )
1447 )
1426
1448
1427 if not self._oldmatcher.visitdir(store.tree[:-1]):
1449 if not self._oldmatcher.visitdir(store.tree[:-1]):
1428 yield tree, deltas
1450 yield tree, deltas
1429 else:
1451 else:
1430 # 'deltas' is a generator and we need to consume it even if
1452 # 'deltas' is a generator and we need to consume it even if
1431 # we are not going to send it because a side-effect is that
1453 # we are not going to send it because a side-effect is that
1432 # it updates tmdnodes (via lookupfn)
1454 # it updates tmdnodes (via lookupfn)
1433 for d in deltas:
1455 for d in deltas:
1434 pass
1456 pass
1435 if not tree:
1457 if not tree:
1436 yield tree, []
1458 yield tree, []
1437
1459
1438 def _prunemanifests(self, store, nodes, commonrevs):
1460 def _prunemanifests(self, store, nodes, commonrevs):
1439 if not self._ellipses:
1461 if not self._ellipses:
1440 # In non-ellipses case and large repositories, it is better to
1462 # In non-ellipses case and large repositories, it is better to
1441 # prevent calling of store.rev and store.linkrev on a lot of
1463 # prevent calling of store.rev and store.linkrev on a lot of
1442 # nodes as compared to sending some extra data
1464 # nodes as compared to sending some extra data
1443 return nodes.copy()
1465 return nodes.copy()
1444 # This is split out as a separate method to allow filtering
1466 # This is split out as a separate method to allow filtering
1445 # commonrevs in extension code.
1467 # commonrevs in extension code.
1446 #
1468 #
1447 # TODO(augie): this shouldn't be required, instead we should
1469 # TODO(augie): this shouldn't be required, instead we should
1448 # make filtering of revisions to send delegated to the store
1470 # make filtering of revisions to send delegated to the store
1449 # layer.
1471 # layer.
1450 frev, flr = store.rev, store.linkrev
1472 frev, flr = store.rev, store.linkrev
1451 return [n for n in nodes if flr(frev(n)) not in commonrevs]
1473 return [n for n in nodes if flr(frev(n)) not in commonrevs]
1452
1474
1453 # The 'source' parameter is useful for extensions
1475 # The 'source' parameter is useful for extensions
1454 def generatefiles(
1476 def generatefiles(
1455 self,
1477 self,
1456 changedfiles,
1478 changedfiles,
1457 commonrevs,
1479 commonrevs,
1458 source,
1480 source,
1459 mfdicts,
1481 mfdicts,
1460 fastpathlinkrev,
1482 fastpathlinkrev,
1461 fnodes,
1483 fnodes,
1462 clrevs,
1484 clrevs,
1463 sidedata_helpers=None,
1485 sidedata_helpers=None,
1464 ):
1486 ):
1465 changedfiles = [
1487 changedfiles = [
1466 f
1488 f
1467 for f in changedfiles
1489 for f in changedfiles
1468 if self._matcher(f) and not self._oldmatcher(f)
1490 if self._matcher(f) and not self._oldmatcher(f)
1469 ]
1491 ]
1470
1492
1471 if not fastpathlinkrev:
1493 if not fastpathlinkrev:
1472
1494
1473 def normallinknodes(unused, fname):
1495 def normallinknodes(unused, fname):
1474 return fnodes.get(fname, {})
1496 return fnodes.get(fname, {})
1475
1497
1476 else:
1498 else:
1477 cln = self._repo.changelog.node
1499 cln = self._repo.changelog.node
1478
1500
1479 def normallinknodes(store, fname):
1501 def normallinknodes(store, fname):
1480 flinkrev = store.linkrev
1502 flinkrev = store.linkrev
1481 fnode = store.node
1503 fnode = store.node
1482 revs = ((r, flinkrev(r)) for r in store)
1504 revs = ((r, flinkrev(r)) for r in store)
1483 return {fnode(r): cln(lr) for r, lr in revs if lr in clrevs}
1505 return {fnode(r): cln(lr) for r, lr in revs if lr in clrevs}
1484
1506
1485 clrevtolocalrev = {}
1507 clrevtolocalrev = {}
1486
1508
1487 if self._isshallow:
1509 if self._isshallow:
1488 # In a shallow clone, the linknodes callback needs to also include
1510 # In a shallow clone, the linknodes callback needs to also include
1489 # those file nodes that are in the manifests we sent but weren't
1511 # those file nodes that are in the manifests we sent but weren't
1490 # introduced by those manifests.
1512 # introduced by those manifests.
1491 commonctxs = [self._repo[c] for c in commonrevs]
1513 commonctxs = [self._repo[c] for c in commonrevs]
1492 clrev = self._repo.changelog.rev
1514 clrev = self._repo.changelog.rev
1493
1515
1494 def linknodes(flog, fname):
1516 def linknodes(flog, fname):
1495 for c in commonctxs:
1517 for c in commonctxs:
1496 try:
1518 try:
1497 fnode = c.filenode(fname)
1519 fnode = c.filenode(fname)
1498 clrevtolocalrev[c.rev()] = flog.rev(fnode)
1520 clrevtolocalrev[c.rev()] = flog.rev(fnode)
1499 except error.ManifestLookupError:
1521 except error.ManifestLookupError:
1500 pass
1522 pass
1501 links = normallinknodes(flog, fname)
1523 links = normallinknodes(flog, fname)
1502 if len(links) != len(mfdicts):
1524 if len(links) != len(mfdicts):
1503 for mf, lr in mfdicts:
1525 for mf, lr in mfdicts:
1504 fnode = mf.get(fname, None)
1526 fnode = mf.get(fname, None)
1505 if fnode in links:
1527 if fnode in links:
1506 links[fnode] = min(links[fnode], lr, key=clrev)
1528 links[fnode] = min(links[fnode], lr, key=clrev)
1507 elif fnode:
1529 elif fnode:
1508 links[fnode] = lr
1530 links[fnode] = lr
1509 return links
1531 return links
1510
1532
1511 else:
1533 else:
1512 linknodes = normallinknodes
1534 linknodes = normallinknodes
1513
1535
1514 repo = self._repo
1536 repo = self._repo
1515 progress = repo.ui.makeprogress(
1537 progress = repo.ui.makeprogress(
1516 _(b'files'), unit=_(b'files'), total=len(changedfiles)
1538 _(b'files'), unit=_(b'files'), total=len(changedfiles)
1517 )
1539 )
1518 for i, fname in enumerate(sorted(changedfiles)):
1540 for i, fname in enumerate(sorted(changedfiles)):
1519 filerevlog = repo.file(fname)
1541 filerevlog = repo.file(fname)
1520 if not filerevlog:
1542 if not filerevlog:
1521 raise error.Abort(
1543 raise error.Abort(
1522 _(b"empty or missing file data for %s") % fname
1544 _(b"empty or missing file data for %s") % fname
1523 )
1545 )
1524
1546
1525 clrevtolocalrev.clear()
1547 clrevtolocalrev.clear()
1526
1548
1527 linkrevnodes = linknodes(filerevlog, fname)
1549 linkrevnodes = linknodes(filerevlog, fname)
1528 # Lookup for filenodes, we collected the linkrev nodes above in the
1550 # Lookup for filenodes, we collected the linkrev nodes above in the
1529 # fastpath case and with lookupmf in the slowpath case.
1551 # fastpath case and with lookupmf in the slowpath case.
1530 def lookupfilelog(x):
1552 def lookupfilelog(x):
1531 return linkrevnodes[x]
1553 return linkrevnodes[x]
1532
1554
1533 frev, flr = filerevlog.rev, filerevlog.linkrev
1555 frev, flr = filerevlog.rev, filerevlog.linkrev
1534 # Skip sending any filenode we know the client already
1556 # Skip sending any filenode we know the client already
1535 # has. This avoids over-sending files relatively
1557 # has. This avoids over-sending files relatively
1536 # inexpensively, so it's not a problem if we under-filter
1558 # inexpensively, so it's not a problem if we under-filter
1537 # here.
1559 # here.
1538 filenodes = [
1560 filenodes = [
1539 n for n in linkrevnodes if flr(frev(n)) not in commonrevs
1561 n for n in linkrevnodes if flr(frev(n)) not in commonrevs
1540 ]
1562 ]
1541
1563
1542 if not filenodes:
1564 if not filenodes:
1543 continue
1565 continue
1544
1566
1545 progress.update(i + 1, item=fname)
1567 progress.update(i + 1, item=fname)
1546
1568
1547 deltas = deltagroup(
1569 deltas = deltagroup(
1548 self._repo,
1570 self._repo,
1549 filerevlog,
1571 filerevlog,
1550 filenodes,
1572 filenodes,
1551 False,
1573 False,
1552 lookupfilelog,
1574 lookupfilelog,
1553 self._forcedeltaparentprev,
1575 self._forcedeltaparentprev,
1554 ellipses=self._ellipses,
1576 ellipses=self._ellipses,
1555 clrevtolocalrev=clrevtolocalrev,
1577 clrevtolocalrev=clrevtolocalrev,
1556 fullclnodes=self._fullclnodes,
1578 fullclnodes=self._fullclnodes,
1557 precomputedellipsis=self._precomputedellipsis,
1579 precomputedellipsis=self._precomputedellipsis,
1558 sidedata_helpers=sidedata_helpers,
1580 sidedata_helpers=sidedata_helpers,
1559 )
1581 )
1560
1582
1561 yield fname, deltas
1583 yield fname, deltas
1562
1584
1563 progress.complete()
1585 progress.complete()
1564
1586
1565
1587
1566 def _makecg1packer(
1588 def _makecg1packer(
1567 repo,
1589 repo,
1568 oldmatcher,
1590 oldmatcher,
1569 matcher,
1591 matcher,
1570 bundlecaps,
1592 bundlecaps,
1571 ellipses=False,
1593 ellipses=False,
1572 shallow=False,
1594 shallow=False,
1573 ellipsisroots=None,
1595 ellipsisroots=None,
1574 fullnodes=None,
1596 fullnodes=None,
1575 remote_sidedata=None,
1597 remote_sidedata=None,
1576 ):
1598 ):
1577 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1599 builddeltaheader = lambda d: _CHANGEGROUPV1_DELTA_HEADER.pack(
1578 d.node, d.p1node, d.p2node, d.linknode
1600 d.node, d.p1node, d.p2node, d.linknode
1579 )
1601 )
1580
1602
1581 return cgpacker(
1603 return cgpacker(
1582 repo,
1604 repo,
1583 oldmatcher,
1605 oldmatcher,
1584 matcher,
1606 matcher,
1585 b'01',
1607 b'01',
1586 builddeltaheader=builddeltaheader,
1608 builddeltaheader=builddeltaheader,
1587 manifestsend=b'',
1609 manifestsend=b'',
1588 forcedeltaparentprev=True,
1610 forcedeltaparentprev=True,
1589 bundlecaps=bundlecaps,
1611 bundlecaps=bundlecaps,
1590 ellipses=ellipses,
1612 ellipses=ellipses,
1591 shallow=shallow,
1613 shallow=shallow,
1592 ellipsisroots=ellipsisroots,
1614 ellipsisroots=ellipsisroots,
1593 fullnodes=fullnodes,
1615 fullnodes=fullnodes,
1594 )
1616 )
1595
1617
1596
1618
1597 def _makecg2packer(
1619 def _makecg2packer(
1598 repo,
1620 repo,
1599 oldmatcher,
1621 oldmatcher,
1600 matcher,
1622 matcher,
1601 bundlecaps,
1623 bundlecaps,
1602 ellipses=False,
1624 ellipses=False,
1603 shallow=False,
1625 shallow=False,
1604 ellipsisroots=None,
1626 ellipsisroots=None,
1605 fullnodes=None,
1627 fullnodes=None,
1606 remote_sidedata=None,
1628 remote_sidedata=None,
1607 ):
1629 ):
1608 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1630 builddeltaheader = lambda d: _CHANGEGROUPV2_DELTA_HEADER.pack(
1609 d.node, d.p1node, d.p2node, d.basenode, d.linknode
1631 d.node, d.p1node, d.p2node, d.basenode, d.linknode
1610 )
1632 )
1611
1633
1612 return cgpacker(
1634 return cgpacker(
1613 repo,
1635 repo,
1614 oldmatcher,
1636 oldmatcher,
1615 matcher,
1637 matcher,
1616 b'02',
1638 b'02',
1617 builddeltaheader=builddeltaheader,
1639 builddeltaheader=builddeltaheader,
1618 manifestsend=b'',
1640 manifestsend=b'',
1619 bundlecaps=bundlecaps,
1641 bundlecaps=bundlecaps,
1620 ellipses=ellipses,
1642 ellipses=ellipses,
1621 shallow=shallow,
1643 shallow=shallow,
1622 ellipsisroots=ellipsisroots,
1644 ellipsisroots=ellipsisroots,
1623 fullnodes=fullnodes,
1645 fullnodes=fullnodes,
1624 )
1646 )
1625
1647
1626
1648
1627 def _makecg3packer(
1649 def _makecg3packer(
1628 repo,
1650 repo,
1629 oldmatcher,
1651 oldmatcher,
1630 matcher,
1652 matcher,
1631 bundlecaps,
1653 bundlecaps,
1632 ellipses=False,
1654 ellipses=False,
1633 shallow=False,
1655 shallow=False,
1634 ellipsisroots=None,
1656 ellipsisroots=None,
1635 fullnodes=None,
1657 fullnodes=None,
1636 remote_sidedata=None,
1658 remote_sidedata=None,
1637 ):
1659 ):
1638 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1660 builddeltaheader = lambda d: _CHANGEGROUPV3_DELTA_HEADER.pack(
1639 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags
1661 d.node, d.p1node, d.p2node, d.basenode, d.linknode, d.flags
1640 )
1662 )
1641
1663
1642 return cgpacker(
1664 return cgpacker(
1643 repo,
1665 repo,
1644 oldmatcher,
1666 oldmatcher,
1645 matcher,
1667 matcher,
1646 b'03',
1668 b'03',
1647 builddeltaheader=builddeltaheader,
1669 builddeltaheader=builddeltaheader,
1648 manifestsend=closechunk(),
1670 manifestsend=closechunk(),
1649 bundlecaps=bundlecaps,
1671 bundlecaps=bundlecaps,
1650 ellipses=ellipses,
1672 ellipses=ellipses,
1651 shallow=shallow,
1673 shallow=shallow,
1652 ellipsisroots=ellipsisroots,
1674 ellipsisroots=ellipsisroots,
1653 fullnodes=fullnodes,
1675 fullnodes=fullnodes,
1654 )
1676 )
1655
1677
1656
1678
1657 def _makecg4packer(
1679 def _makecg4packer(
1658 repo,
1680 repo,
1659 oldmatcher,
1681 oldmatcher,
1660 matcher,
1682 matcher,
1661 bundlecaps,
1683 bundlecaps,
1662 ellipses=False,
1684 ellipses=False,
1663 shallow=False,
1685 shallow=False,
1664 ellipsisroots=None,
1686 ellipsisroots=None,
1665 fullnodes=None,
1687 fullnodes=None,
1666 remote_sidedata=None,
1688 remote_sidedata=None,
1667 ):
1689 ):
1668 # Sidedata is in a separate chunk from the delta to differentiate
1690 # Sidedata is in a separate chunk from the delta to differentiate
1669 # "raw delta" and sidedata.
1691 # "raw delta" and sidedata.
1670 def builddeltaheader(d):
1692 def builddeltaheader(d):
1671 return _CHANGEGROUPV4_DELTA_HEADER.pack(
1693 return _CHANGEGROUPV4_DELTA_HEADER.pack(
1672 d.protocol_flags,
1694 d.protocol_flags,
1673 d.node,
1695 d.node,
1674 d.p1node,
1696 d.p1node,
1675 d.p2node,
1697 d.p2node,
1676 d.basenode,
1698 d.basenode,
1677 d.linknode,
1699 d.linknode,
1678 d.flags,
1700 d.flags,
1679 )
1701 )
1680
1702
1681 return cgpacker(
1703 return cgpacker(
1682 repo,
1704 repo,
1683 oldmatcher,
1705 oldmatcher,
1684 matcher,
1706 matcher,
1685 b'04',
1707 b'04',
1686 builddeltaheader=builddeltaheader,
1708 builddeltaheader=builddeltaheader,
1687 manifestsend=closechunk(),
1709 manifestsend=closechunk(),
1688 bundlecaps=bundlecaps,
1710 bundlecaps=bundlecaps,
1689 ellipses=ellipses,
1711 ellipses=ellipses,
1690 shallow=shallow,
1712 shallow=shallow,
1691 ellipsisroots=ellipsisroots,
1713 ellipsisroots=ellipsisroots,
1692 fullnodes=fullnodes,
1714 fullnodes=fullnodes,
1693 remote_sidedata=remote_sidedata,
1715 remote_sidedata=remote_sidedata,
1694 )
1716 )
1695
1717
1696
1718
1697 _packermap = {
1719 _packermap = {
1698 b'01': (_makecg1packer, cg1unpacker),
1720 b'01': (_makecg1packer, cg1unpacker),
1699 # cg2 adds support for exchanging generaldelta
1721 # cg2 adds support for exchanging generaldelta
1700 b'02': (_makecg2packer, cg2unpacker),
1722 b'02': (_makecg2packer, cg2unpacker),
1701 # cg3 adds support for exchanging revlog flags and treemanifests
1723 # cg3 adds support for exchanging revlog flags and treemanifests
1702 b'03': (_makecg3packer, cg3unpacker),
1724 b'03': (_makecg3packer, cg3unpacker),
1703 # ch4 adds support for exchanging sidedata
1725 # ch4 adds support for exchanging sidedata
1704 b'04': (_makecg4packer, cg4unpacker),
1726 b'04': (_makecg4packer, cg4unpacker),
1705 }
1727 }
1706
1728
1707
1729
1708 def allsupportedversions(repo):
1730 def allsupportedversions(repo):
1709 versions = set(_packermap.keys())
1731 versions = set(_packermap.keys())
1710 needv03 = False
1732 needv03 = False
1711 if (
1733 if (
1712 repo.ui.configbool(b'experimental', b'changegroup3')
1734 repo.ui.configbool(b'experimental', b'changegroup3')
1713 or repo.ui.configbool(b'experimental', b'treemanifest')
1735 or repo.ui.configbool(b'experimental', b'treemanifest')
1714 or scmutil.istreemanifest(repo)
1736 or scmutil.istreemanifest(repo)
1715 ):
1737 ):
1716 # we keep version 03 because we need to to exchange treemanifest data
1738 # we keep version 03 because we need to to exchange treemanifest data
1717 #
1739 #
1718 # we also keep vresion 01 and 02, because it is possible for repo to
1740 # we also keep vresion 01 and 02, because it is possible for repo to
1719 # contains both normal and tree manifest at the same time. so using
1741 # contains both normal and tree manifest at the same time. so using
1720 # older version to pull data is viable
1742 # older version to pull data is viable
1721 #
1743 #
1722 # (or even to push subset of history)
1744 # (or even to push subset of history)
1723 needv03 = True
1745 needv03 = True
1724 if not needv03:
1746 if not needv03:
1725 versions.discard(b'03')
1747 versions.discard(b'03')
1726 want_v4 = (
1748 want_v4 = (
1727 repo.ui.configbool(b'experimental', b'changegroup4')
1749 repo.ui.configbool(b'experimental', b'changegroup4')
1728 or requirements.REVLOGV2_REQUIREMENT in repo.requirements
1750 or requirements.REVLOGV2_REQUIREMENT in repo.requirements
1729 or requirements.CHANGELOGV2_REQUIREMENT in repo.requirements
1751 or requirements.CHANGELOGV2_REQUIREMENT in repo.requirements
1730 )
1752 )
1731 if not want_v4:
1753 if not want_v4:
1732 versions.discard(b'04')
1754 versions.discard(b'04')
1733 return versions
1755 return versions
1734
1756
1735
1757
1736 # Changegroup versions that can be applied to the repo
1758 # Changegroup versions that can be applied to the repo
1737 def supportedincomingversions(repo):
1759 def supportedincomingversions(repo):
1738 return allsupportedversions(repo)
1760 return allsupportedversions(repo)
1739
1761
1740
1762
1741 # Changegroup versions that can be created from the repo
1763 # Changegroup versions that can be created from the repo
1742 def supportedoutgoingversions(repo):
1764 def supportedoutgoingversions(repo):
1743 versions = allsupportedversions(repo)
1765 versions = allsupportedversions(repo)
1744 if scmutil.istreemanifest(repo):
1766 if scmutil.istreemanifest(repo):
1745 # Versions 01 and 02 support only flat manifests and it's just too
1767 # Versions 01 and 02 support only flat manifests and it's just too
1746 # expensive to convert between the flat manifest and tree manifest on
1768 # expensive to convert between the flat manifest and tree manifest on
1747 # the fly. Since tree manifests are hashed differently, all of history
1769 # the fly. Since tree manifests are hashed differently, all of history
1748 # would have to be converted. Instead, we simply don't even pretend to
1770 # would have to be converted. Instead, we simply don't even pretend to
1749 # support versions 01 and 02.
1771 # support versions 01 and 02.
1750 versions.discard(b'01')
1772 versions.discard(b'01')
1751 versions.discard(b'02')
1773 versions.discard(b'02')
1752 if requirements.NARROW_REQUIREMENT in repo.requirements:
1774 if requirements.NARROW_REQUIREMENT in repo.requirements:
1753 # Versions 01 and 02 don't support revlog flags, and we need to
1775 # Versions 01 and 02 don't support revlog flags, and we need to
1754 # support that for stripping and unbundling to work.
1776 # support that for stripping and unbundling to work.
1755 versions.discard(b'01')
1777 versions.discard(b'01')
1756 versions.discard(b'02')
1778 versions.discard(b'02')
1757 if LFS_REQUIREMENT in repo.requirements:
1779 if LFS_REQUIREMENT in repo.requirements:
1758 # Versions 01 and 02 don't support revlog flags, and we need to
1780 # Versions 01 and 02 don't support revlog flags, and we need to
1759 # mark LFS entries with REVIDX_EXTSTORED.
1781 # mark LFS entries with REVIDX_EXTSTORED.
1760 versions.discard(b'01')
1782 versions.discard(b'01')
1761 versions.discard(b'02')
1783 versions.discard(b'02')
1762
1784
1763 return versions
1785 return versions
1764
1786
1765
1787
1766 def localversion(repo):
1788 def localversion(repo):
1767 # Finds the best version to use for bundles that are meant to be used
1789 # Finds the best version to use for bundles that are meant to be used
1768 # locally, such as those from strip and shelve, and temporary bundles.
1790 # locally, such as those from strip and shelve, and temporary bundles.
1769 return max(supportedoutgoingversions(repo))
1791 return max(supportedoutgoingversions(repo))
1770
1792
1771
1793
1772 def safeversion(repo):
1794 def safeversion(repo):
1773 # Finds the smallest version that it's safe to assume clients of the repo
1795 # Finds the smallest version that it's safe to assume clients of the repo
1774 # will support. For example, all hg versions that support generaldelta also
1796 # will support. For example, all hg versions that support generaldelta also
1775 # support changegroup 02.
1797 # support changegroup 02.
1776 versions = supportedoutgoingversions(repo)
1798 versions = supportedoutgoingversions(repo)
1777 if requirements.GENERALDELTA_REQUIREMENT in repo.requirements:
1799 if requirements.GENERALDELTA_REQUIREMENT in repo.requirements:
1778 versions.discard(b'01')
1800 versions.discard(b'01')
1779 assert versions
1801 assert versions
1780 return min(versions)
1802 return min(versions)
1781
1803
1782
1804
1783 def getbundler(
1805 def getbundler(
1784 version,
1806 version,
1785 repo,
1807 repo,
1786 bundlecaps=None,
1808 bundlecaps=None,
1787 oldmatcher=None,
1809 oldmatcher=None,
1788 matcher=None,
1810 matcher=None,
1789 ellipses=False,
1811 ellipses=False,
1790 shallow=False,
1812 shallow=False,
1791 ellipsisroots=None,
1813 ellipsisroots=None,
1792 fullnodes=None,
1814 fullnodes=None,
1793 remote_sidedata=None,
1815 remote_sidedata=None,
1794 ):
1816 ):
1795 assert version in supportedoutgoingversions(repo)
1817 assert version in supportedoutgoingversions(repo)
1796
1818
1797 if matcher is None:
1819 if matcher is None:
1798 matcher = matchmod.always()
1820 matcher = matchmod.always()
1799 if oldmatcher is None:
1821 if oldmatcher is None:
1800 oldmatcher = matchmod.never()
1822 oldmatcher = matchmod.never()
1801
1823
1802 if version == b'01' and not matcher.always():
1824 if version == b'01' and not matcher.always():
1803 raise error.ProgrammingError(
1825 raise error.ProgrammingError(
1804 b'version 01 changegroups do not support sparse file matchers'
1826 b'version 01 changegroups do not support sparse file matchers'
1805 )
1827 )
1806
1828
1807 if ellipses and version in (b'01', b'02'):
1829 if ellipses and version in (b'01', b'02'):
1808 raise error.Abort(
1830 raise error.Abort(
1809 _(
1831 _(
1810 b'ellipsis nodes require at least cg3 on client and server, '
1832 b'ellipsis nodes require at least cg3 on client and server, '
1811 b'but negotiated version %s'
1833 b'but negotiated version %s'
1812 )
1834 )
1813 % version
1835 % version
1814 )
1836 )
1815
1837
1816 # Requested files could include files not in the local store. So
1838 # Requested files could include files not in the local store. So
1817 # filter those out.
1839 # filter those out.
1818 matcher = repo.narrowmatch(matcher)
1840 matcher = repo.narrowmatch(matcher)
1819
1841
1820 fn = _packermap[version][0]
1842 fn = _packermap[version][0]
1821 return fn(
1843 return fn(
1822 repo,
1844 repo,
1823 oldmatcher,
1845 oldmatcher,
1824 matcher,
1846 matcher,
1825 bundlecaps,
1847 bundlecaps,
1826 ellipses=ellipses,
1848 ellipses=ellipses,
1827 shallow=shallow,
1849 shallow=shallow,
1828 ellipsisroots=ellipsisroots,
1850 ellipsisroots=ellipsisroots,
1829 fullnodes=fullnodes,
1851 fullnodes=fullnodes,
1830 remote_sidedata=remote_sidedata,
1852 remote_sidedata=remote_sidedata,
1831 )
1853 )
1832
1854
1833
1855
1834 def getunbundler(version, fh, alg, extras=None):
1856 def getunbundler(version, fh, alg, extras=None):
1835 return _packermap[version][1](fh, alg, extras=extras)
1857 return _packermap[version][1](fh, alg, extras=extras)
1836
1858
1837
1859
1838 def _changegroupinfo(repo, nodes, source):
1860 def _changegroupinfo(repo, nodes, source):
1839 if repo.ui.verbose or source == b'bundle':
1861 if repo.ui.verbose or source == b'bundle':
1840 repo.ui.status(_(b"%d changesets found\n") % len(nodes))
1862 repo.ui.status(_(b"%d changesets found\n") % len(nodes))
1841 if repo.ui.debugflag:
1863 if repo.ui.debugflag:
1842 repo.ui.debug(b"list of changesets:\n")
1864 repo.ui.debug(b"list of changesets:\n")
1843 for node in nodes:
1865 for node in nodes:
1844 repo.ui.debug(b"%s\n" % hex(node))
1866 repo.ui.debug(b"%s\n" % hex(node))
1845
1867
1846
1868
1847 def makechangegroup(
1869 def makechangegroup(
1848 repo, outgoing, version, source, fastpath=False, bundlecaps=None
1870 repo, outgoing, version, source, fastpath=False, bundlecaps=None
1849 ):
1871 ):
1850 cgstream = makestream(
1872 cgstream = makestream(
1851 repo,
1873 repo,
1852 outgoing,
1874 outgoing,
1853 version,
1875 version,
1854 source,
1876 source,
1855 fastpath=fastpath,
1877 fastpath=fastpath,
1856 bundlecaps=bundlecaps,
1878 bundlecaps=bundlecaps,
1857 )
1879 )
1858 return getunbundler(
1880 return getunbundler(
1859 version,
1881 version,
1860 util.chunkbuffer(cgstream),
1882 util.chunkbuffer(cgstream),
1861 None,
1883 None,
1862 {b'clcount': len(outgoing.missing)},
1884 {b'clcount': len(outgoing.missing)},
1863 )
1885 )
1864
1886
1865
1887
1866 def makestream(
1888 def makestream(
1867 repo,
1889 repo,
1868 outgoing,
1890 outgoing,
1869 version,
1891 version,
1870 source,
1892 source,
1871 fastpath=False,
1893 fastpath=False,
1872 bundlecaps=None,
1894 bundlecaps=None,
1873 matcher=None,
1895 matcher=None,
1874 remote_sidedata=None,
1896 remote_sidedata=None,
1875 ):
1897 ):
1876 bundler = getbundler(
1898 bundler = getbundler(
1877 version,
1899 version,
1878 repo,
1900 repo,
1879 bundlecaps=bundlecaps,
1901 bundlecaps=bundlecaps,
1880 matcher=matcher,
1902 matcher=matcher,
1881 remote_sidedata=remote_sidedata,
1903 remote_sidedata=remote_sidedata,
1882 )
1904 )
1883
1905
1884 repo = repo.unfiltered()
1906 repo = repo.unfiltered()
1885 commonrevs = outgoing.common
1907 commonrevs = outgoing.common
1886 csets = outgoing.missing
1908 csets = outgoing.missing
1887 heads = outgoing.ancestorsof
1909 heads = outgoing.ancestorsof
1888 # We go through the fast path if we get told to, or if all (unfiltered
1910 # We go through the fast path if we get told to, or if all (unfiltered
1889 # heads have been requested (since we then know there all linkrevs will
1911 # heads have been requested (since we then know there all linkrevs will
1890 # be pulled by the client).
1912 # be pulled by the client).
1891 heads.sort()
1913 heads.sort()
1892 fastpathlinkrev = fastpath or (
1914 fastpathlinkrev = fastpath or (
1893 repo.filtername is None and heads == sorted(repo.heads())
1915 repo.filtername is None and heads == sorted(repo.heads())
1894 )
1916 )
1895
1917
1896 repo.hook(b'preoutgoing', throw=True, source=source)
1918 repo.hook(b'preoutgoing', throw=True, source=source)
1897 _changegroupinfo(repo, csets, source)
1919 _changegroupinfo(repo, csets, source)
1898 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1920 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
1899
1921
1900
1922
1901 def _addchangegroupfiles(
1923 def _addchangegroupfiles(
1902 repo,
1924 repo,
1903 source,
1925 source,
1904 revmap,
1926 revmap,
1905 trp,
1927 trp,
1906 expectedfiles,
1928 expectedfiles,
1907 needfiles,
1929 needfiles,
1908 addrevisioncb=None,
1930 addrevisioncb=None,
1909 ):
1931 ):
1910 revisions = 0
1932 revisions = 0
1911 files = 0
1933 files = 0
1912 progress = repo.ui.makeprogress(
1934 progress = repo.ui.makeprogress(
1913 _(b'files'), unit=_(b'files'), total=expectedfiles
1935 _(b'files'), unit=_(b'files'), total=expectedfiles
1914 )
1936 )
1915 for chunkdata in iter(source.filelogheader, {}):
1937 for chunkdata in iter(source.filelogheader, {}):
1916 files += 1
1938 files += 1
1917 f = chunkdata[b"filename"]
1939 f = chunkdata[b"filename"]
1918 repo.ui.debug(b"adding %s revisions\n" % f)
1940 repo.ui.debug(b"adding %s revisions\n" % f)
1919 progress.increment()
1941 progress.increment()
1920 fl = repo.file(f)
1942 fl = repo.file(f)
1921 o = len(fl)
1943 o = len(fl)
1922 try:
1944 try:
1923 deltas = source.deltaiter()
1945 deltas = source.deltaiter()
1924 added = fl.addgroup(
1946 added = fl.addgroup(
1925 deltas,
1947 deltas,
1926 revmap,
1948 revmap,
1927 trp,
1949 trp,
1928 addrevisioncb=addrevisioncb,
1950 addrevisioncb=addrevisioncb,
1929 )
1951 )
1930 if not added:
1952 if not added:
1931 raise error.Abort(_(b"received file revlog group is empty"))
1953 raise error.Abort(_(b"received file revlog group is empty"))
1932 except error.CensoredBaseError as e:
1954 except error.CensoredBaseError as e:
1933 raise error.Abort(_(b"received delta base is censored: %s") % e)
1955 raise error.Abort(_(b"received delta base is censored: %s") % e)
1934 revisions += len(fl) - o
1956 revisions += len(fl) - o
1935 if f in needfiles:
1957 if f in needfiles:
1936 needs = needfiles[f]
1958 needs = needfiles[f]
1937 for new in pycompat.xrange(o, len(fl)):
1959 for new in pycompat.xrange(o, len(fl)):
1938 n = fl.node(new)
1960 n = fl.node(new)
1939 if n in needs:
1961 if n in needs:
1940 needs.remove(n)
1962 needs.remove(n)
1941 else:
1963 else:
1942 raise error.Abort(_(b"received spurious file revlog entry"))
1964 raise error.Abort(_(b"received spurious file revlog entry"))
1943 if not needs:
1965 if not needs:
1944 del needfiles[f]
1966 del needfiles[f]
1945 progress.complete()
1967 progress.complete()
1946
1968
1947 for f, needs in pycompat.iteritems(needfiles):
1969 for f, needs in pycompat.iteritems(needfiles):
1948 fl = repo.file(f)
1970 fl = repo.file(f)
1949 for n in needs:
1971 for n in needs:
1950 try:
1972 try:
1951 fl.rev(n)
1973 fl.rev(n)
1952 except error.LookupError:
1974 except error.LookupError:
1953 raise error.Abort(
1975 raise error.Abort(
1954 _(b'missing file data for %s:%s - run hg verify')
1976 _(b'missing file data for %s:%s - run hg verify')
1955 % (f, hex(n))
1977 % (f, hex(n))
1956 )
1978 )
1957
1979
1958 return revisions, files
1980 return revisions, files
General Comments 0
You need to be logged in to leave comments. Login now