##// END OF EJS Templates
streamclone: support for producing and consuming stream clone bundles...
Gregory Szorc -
r26755:bb0b955d default
parent child Browse files
Show More
@@ -7,6 +7,7 b''
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import struct
10 import time
11 import time
11
12
12 from .i18n import _
13 from .i18n import _
@@ -236,6 +237,61 b' def generatev1wireproto(repo):'
236 for chunk in it:
237 for chunk in it:
237 yield chunk
238 yield chunk
238
239
240 def generatebundlev1(repo, compression='UN'):
241 """Emit content for version 1 of a stream clone bundle.
242
243 The first 4 bytes of the output ("HGS1") denote this as stream clone
244 bundle version 1.
245
246 The next 2 bytes indicate the compression type. Only "UN" is currently
247 supported.
248
249 The next 16 bytes are two 64-bit big endian unsigned integers indicating
250 file count and byte count, respectively.
251
252 The next 2 bytes is a 16-bit big endian unsigned short declaring the length
253 of the requirements string, including a trailing \0. The following N bytes
254 are the requirements string, which is ASCII containing a comma-delimited
255 list of repo requirements that are needed to support the data.
256
257 The remaining content is the output of ``generatev1()`` (which may be
258 compressed in the future).
259
260 Returns a tuple of (requirements, data generator).
261 """
262 if compression != 'UN':
263 raise ValueError('we do not support the compression argument yet')
264
265 requirements = repo.requirements & repo.supportedformats
266 requires = ','.join(sorted(requirements))
267
268 def gen():
269 yield 'HGS1'
270 yield compression
271
272 filecount, bytecount, it = generatev1(repo)
273 repo.ui.status(_('writing %d bytes for %d files\n') %
274 (bytecount, filecount))
275
276 yield struct.pack('>QQ', filecount, bytecount)
277 yield struct.pack('>H', len(requires) + 1)
278 yield requires + '\0'
279
280 # This is where we'll add compression in the future.
281 assert compression == 'UN'
282
283 seen = 0
284 repo.ui.progress(_('bundle'), 0, total=bytecount)
285
286 for chunk in it:
287 seen += len(chunk)
288 repo.ui.progress(_('bundle'), seen, total=bytecount)
289 yield chunk
290
291 repo.ui.progress(_('bundle'), None)
292
293 return requirements, gen()
294
239 def consumev1(repo, fp, filecount, bytecount):
295 def consumev1(repo, fp, filecount, bytecount):
240 """Apply the contents from version 1 of a streaming clone file handle.
296 """Apply the contents from version 1 of a streaming clone file handle.
241
297
@@ -290,3 +346,47 b' def consumev1(repo, fp, filecount, bytec'
290 util.bytecount(bytecount / elapsed)))
346 util.bytecount(bytecount / elapsed)))
291 finally:
347 finally:
292 lock.release()
348 lock.release()
349
350 def applybundlev1(repo, fp):
351 """Apply the content from a stream clone bundle version 1.
352
353 We assume the 4 byte header has been read and validated and the file handle
354 is at the 2 byte compression identifier.
355 """
356 if len(repo):
357 raise error.Abort(_('cannot apply stream clone bundle on non-empty '
358 'repo'))
359
360 compression = fp.read(2)
361 if compression != 'UN':
362 raise error.Abort(_('only uncompressed stream clone bundles are '
363 'supported; got %s') % compression)
364
365 filecount, bytecount = struct.unpack('>QQ', fp.read(16))
366 requireslen = struct.unpack('>H', fp.read(2))[0]
367 requires = fp.read(requireslen)
368
369 if not requires.endswith('\0'):
370 raise error.Abort(_('malformed stream clone bundle: '
371 'requirements not properly encoded'))
372
373 requirements = set(requires.rstrip('\0').split(','))
374 missingreqs = requirements - repo.supportedformats
375 if missingreqs:
376 raise error.Abort(_('unable to apply stream clone: '
377 'unsupported format: %s') %
378 ', '.join(sorted(missingreqs)))
379
380 consumev1(repo, fp, filecount, bytecount)
381
382 class streamcloneapplier(object):
383 """Class to manage applying streaming clone bundles.
384
385 We need to wrap ``applybundlev1()`` in a dedicated type to enable bundle
386 readers to perform bundle type-specific functionality.
387 """
388 def __init__(self, fh):
389 self._fh = fh
390
391 def apply(self, repo):
392 return applybundlev1(repo, self._fh)
General Comments 0
You need to be logged in to leave comments. Login now