82
83
recreate_search trusts that clients will look for missing things
83
84
they expected and get it from elsewhere.
86
if search_bytes == 'everything':
87
return graph.EverythingResult(repository), None
85
88
lines = search_bytes.split('\n')
86
89
if lines[0] == 'ancestry-of':
180
184
repository.unlock()
182
def _do_repository_request(self, body_bytes):
183
repository = self._repository
184
revision_ids = set(self._revision_ids)
185
include_missing = 'include-missing:' in revision_ids
187
revision_ids.remove('include-missing:')
188
body_lines = body_bytes.split('\n')
189
search_result, error = self.recreate_search_from_recipe(
190
repository, body_lines)
191
if error is not None:
193
# TODO might be nice to start up the search again; but thats not
194
# written or tested yet.
195
client_seen_revs = set(search_result.get_keys())
196
# Always include the requested ids.
197
client_seen_revs.difference_update(revision_ids)
199
repo_graph = repository.get_graph()
186
def _expand_requested_revs(self, repo_graph, revision_ids, client_seen_revs,
187
include_missing, max_size=65536):
201
189
queried_revs = set()
190
estimator = estimate_compressed_size.ZLibEstimator(max_size)
203
191
next_revs = revision_ids
204
192
first_loop_done = False
227
215
# add parents to the result
228
216
result[encoded_id] = parents
229
217
# Approximate the serialized cost of this revision_id.
230
size_so_far += 2 + len(encoded_id) + sum(map(len, parents))
218
line = '%s %s\n' % (encoded_id, ' '.join(parents))
219
estimator.add_content(line)
231
220
# get all the directly asked for parents, and then flesh out to
232
221
# 64K (compressed) or so. We do one level of depth at a time to
233
222
# stay in sync with the client. The 250000 magic number is
234
223
# estimated compression ratio taken from bzr.dev itself.
235
if self.no_extra_results or (
236
first_loop_done and size_so_far > 250000):
224
if self.no_extra_results or (first_loop_done and estimator.full()):
225
trace.mutter('size: %d, z_size: %d'
226
% (estimator._uncompressed_size_added,
227
estimator._compressed_size_added))
237
228
next_revs = set()
239
230
# don't query things we've already queried
240
next_revs.difference_update(queried_revs)
231
next_revs = next_revs.difference(queried_revs)
241
232
first_loop_done = True
235
def _do_repository_request(self, body_bytes):
236
repository = self._repository
237
revision_ids = set(self._revision_ids)
238
include_missing = 'include-missing:' in revision_ids
240
revision_ids.remove('include-missing:')
241
body_lines = body_bytes.split('\n')
242
search_result, error = self.recreate_search_from_recipe(
243
repository, body_lines)
244
if error is not None:
246
# TODO might be nice to start up the search again; but thats not
247
# written or tested yet.
248
client_seen_revs = set(search_result.get_keys())
249
# Always include the requested ids.
250
client_seen_revs.difference_update(revision_ids)
252
repo_graph = repository.get_graph()
253
result = self._expand_requested_revs(repo_graph, revision_ids,
254
client_seen_revs, include_missing)
243
256
# sorting trivially puts lexographically similar revision ids together.
244
257
# Compression FTW.
245
259
for revision, parents in sorted(result.items()):
246
260
lines.append(' '.join((revision, ) + tuple(parents)))
320
334
:param repository: The repository to query in.
321
335
:param revision_id: The utf8 encoded revision_id to lookup.
322
:return: A smart server response of ('ok', ) if the revision is
336
:return: A smart server response of ('yes', ) if the revision is
337
present. ('no', ) if it is missing.
325
339
if repository.has_revision(revision_id):
326
340
return SuccessfulSmartServerResponse(('yes', ))
328
342
return SuccessfulSmartServerResponse(('no', ))
345
class SmartServerRequestHasSignatureForRevisionId(
346
SmartServerRepositoryRequest):
348
def do_repository_request(self, repository, revision_id):
349
"""Return ok if a signature is present for a revision.
351
Introduced in bzr 2.5.0.
353
:param repository: The repository to query in.
354
:param revision_id: The utf8 encoded revision_id to lookup.
355
:return: A smart server response of ('yes', ) if a
356
signature for the revision is present,
357
('no', ) if it is missing.
360
if repository.has_signature_for_revision_id(revision_id):
361
return SuccessfulSmartServerResponse(('yes', ))
363
return SuccessfulSmartServerResponse(('no', ))
364
except errors.NoSuchRevision:
365
return FailedSmartServerResponse(
366
('nosuchrevision', revision_id))
331
369
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
333
371
def do_repository_request(self, repository, revid, committers):
385
423
return SuccessfulSmartServerResponse(('no', ))
426
class SmartServerRepositoryMakeWorkingTrees(SmartServerRepositoryRequest):
428
def do_repository_request(self, repository):
429
"""Return the result of repository.make_working_trees().
431
Introduced in bzr 2.5.0.
433
:param repository: The repository to query in.
434
:return: A smart server response of ('yes', ) if the repository uses
435
working trees, and ('no', ) if it is not.
437
if repository.make_working_trees():
438
return SuccessfulSmartServerResponse(('yes', ))
440
return SuccessfulSmartServerResponse(('no', ))
388
443
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
390
445
def do_repository_request(self, repository, token=''):
395
token = repository.lock_write(token=token)
450
token = repository.lock_write(token=token).repository_token
396
451
except errors.LockContention, e:
397
452
return FailedSmartServerResponse(('LockContention',))
398
453
except errors.UnlockableTransport:
413
468
def do_repository_request(self, repository, to_network_name):
414
469
"""Get a stream for inserting into a to_format repository.
471
The request body is 'search_bytes', a description of the revisions
474
In 2.3 this verb added support for search_bytes == 'everything'. Older
475
implementations will respond with a BadSearch error, and clients should
476
catch this and fallback appropriately.
416
478
:param repository: The repository to stream from.
417
479
:param to_network_name: The network name of the format of the target
492
554
class SmartServerRepositoryGetStream_1_19(SmartServerRepositoryGetStream):
555
"""The same as Repository.get_stream, but will return stream CHK formats to
558
See SmartServerRepositoryGetStream._should_fake_unknown.
494
563
def _should_fake_unknown(self):
495
564
"""Returns False; we don't need to workaround bugs in 1.19+ clients."""
505
574
for record in substream:
506
575
if record.storage_kind in ('chunked', 'fulltext'):
507
576
serialised = record_to_fulltext_bytes(record)
508
elif record.storage_kind == 'inventory-delta':
509
serialised = record_to_inventory_delta_bytes(record)
510
577
elif record.storage_kind == 'absent':
511
578
raise ValueError("Absent factory for %s" % (record.key,))
544
611
:ivar first_bytes: The first bytes to give the next NetworkRecordStream.
547
def __init__(self, byte_stream):
614
def __init__(self, byte_stream, record_counter):
548
615
"""Create a _ByteStreamDecoder."""
549
616
self.stream_decoder = pack.ContainerPushParser()
550
617
self.current_type = None
551
618
self.first_bytes = None
552
619
self.byte_stream = byte_stream
620
self._record_counter = record_counter
554
623
def iter_stream_decoder(self):
555
624
"""Iterate the contents of the pack from stream_decoder."""
581
650
def record_stream(self):
582
651
"""Yield substream_type, substream from the byte stream."""
652
def wrap_and_count(pb, rc, substream):
653
"""Yield records from stream while showing progress."""
656
if self.current_type != 'revisions' and self.key_count != 0:
657
# As we know the number of revisions now (in self.key_count)
658
# we can setup and use record_counter (rc).
659
if not rc.is_initialized():
660
rc.setup(self.key_count, self.key_count)
661
for record in substream.read():
663
if rc.is_initialized() and counter == rc.STEP:
664
rc.increment(counter)
665
pb.update('Estimate', rc.current, rc.max)
667
if self.current_type == 'revisions':
668
# Total records is proportional to number of revs
669
# to fetch. With remote, we used self.key_count to
670
# track the number of revs. Once we have the revs
671
# counts in self.key_count, the progress bar changes
672
# from 'Estimating..' to 'Estimate' above.
674
if counter == rc.STEP:
675
pb.update('Estimating..', self.key_count)
583
680
self.seed_state()
681
pb = ui.ui_factory.nested_progress_bar()
682
rc = self._record_counter
584
683
# Make and consume sub generators, one per substream type:
585
684
while self.first_bytes is not None:
586
685
substream = NetworkRecordStream(self.iter_substream_bytes())
587
686
# after substream is fully consumed, self.current_type is set to
588
687
# the next type, and self.first_bytes is set to the matching bytes.
589
yield self.current_type, substream.read()
688
yield self.current_type, wrap_and_count(pb, rc, substream)
690
pb.update('Done', rc.max, rc.max)
591
693
def seed_state(self):
592
694
"""Prepare the _ByteStreamDecoder to decode from the pack stream."""
597
699
list(self.iter_substream_bytes())
600
def _byte_stream_to_stream(byte_stream):
702
def _byte_stream_to_stream(byte_stream, record_counter=None):
601
703
"""Convert a byte stream into a format and a stream.
603
705
:param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
604
706
:return: (RepositoryFormat, stream_generator)
606
decoder = _ByteStreamDecoder(byte_stream)
708
decoder = _ByteStreamDecoder(byte_stream, record_counter)
607
709
for bytes in byte_stream:
608
710
decoder.stream_decoder.accept_bytes(bytes)
609
711
for record in decoder.stream_decoder.read_pending_records(max=1):