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