22
22
from bzrlib import (
29
from bzrlib.branch import Branch, BranchReferenceFormat
31
from bzrlib.branch import BranchReferenceFormat
30
32
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
31
33
from bzrlib.config import BranchConfig, TreeConfig
32
34
from bzrlib.decorators import needs_read_lock, needs_write_lock
33
35
from bzrlib.errors import NoSuchRevision
34
36
from bzrlib.lockable_files import LockableFiles
35
from bzrlib.pack import ContainerReader
37
from bzrlib.pack import ContainerPushParser
36
38
from bzrlib.smart import client, vfs
37
39
from bzrlib.symbol_versioning import (
41
from bzrlib.trace import note
43
from bzrlib.revision import NULL_REVISION
44
from bzrlib.trace import mutter, note
43
46
# Note: RemoteBzrDirFormat is in bzrdir.py
375
384
assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
376
385
return response[0] == 'yes'
387
def has_revisions(self, revision_ids):
388
"""See Repository.has_revisions()."""
390
for revision_id in revision_ids:
391
if self.has_revision(revision_id):
392
result.add(revision_id)
378
395
def has_same_location(self, other):
379
396
return (self.__class__ == other.__class__ and
380
397
self.bzrdir.transport.base == other.bzrdir.transport.base)
382
399
def get_graph(self, other_repository=None):
383
400
"""Return the graph for this repository format"""
385
return self._real_repository.get_graph(other_repository)
401
parents_provider = self
402
if (other_repository is not None and
403
other_repository.bzrdir.transport.base !=
404
self.bzrdir.transport.base):
405
parents_provider = graph._StackedParentsProvider(
406
[parents_provider, other_repository._make_parents_provider()])
407
return graph.Graph(parents_provider)
387
409
def gather_stats(self, revid=None, committers=None):
388
410
"""See Repository.gather_stats()."""
628
653
committer=committer, revprops=revprops, revision_id=revision_id)
632
656
def add_inventory(self, revid, inv, parents):
633
657
self._ensure_real()
634
658
return self._real_repository.add_inventory(revid, inv, parents)
637
660
def add_revision(self, rev_id, rev, inv=None, config=None):
638
661
self._ensure_real()
639
662
return self._real_repository.add_revision(
671
694
"""RemoteRepositories never create working trees by default."""
697
def revision_ids_to_search_result(self, result_set):
698
"""Convert a set of revision ids to a graph SearchResult."""
699
result_parents = set()
700
for parents in self.get_graph().get_parent_map(
701
result_set).itervalues():
702
result_parents.update(parents)
703
included_keys = result_set.intersection(result_parents)
704
start_keys = result_set.difference(included_keys)
705
exclude_keys = result_parents.difference(result_set)
706
result = graph.SearchResult(start_keys, exclude_keys,
707
len(result_set), result_set)
711
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
712
"""Return the revision ids that other has that this does not.
714
These are returned in topological order.
716
revision_id: only return revision ids included by revision_id.
718
return repository.InterRepository.get(
719
other, self).search_missing_revision_ids(revision_id, find_ghosts)
674
721
def fetch(self, source, revision_id=None, pb=None):
675
722
if self.has_same_location(source):
676
723
# check that last_revision is in 'from' and then return a
717
764
self._ensure_real()
718
765
return self._real_repository.iter_files_bytes(desired_files)
767
def get_parent_map(self, keys):
768
"""See bzrlib.Graph.get_parent_map()."""
769
# Hack to build up the caching logic.
770
ancestry = self._parents_map
771
missing_revisions = set(key for key in keys if key not in ancestry)
772
if missing_revisions:
773
parent_map = self._get_parent_map(missing_revisions)
774
if 'hpss' in debug.debug_flags:
775
mutter('retransmitted revisions: %d of %d',
776
len(set(self._parents_map).intersection(parent_map)),
778
self._parents_map.update(parent_map)
779
return dict((k, ancestry[k]) for k in keys if k in ancestry)
781
def _response_is_unknown_method(self, response, verb):
782
"""Return True if response is an unknonwn method response to verb.
784
:param response: The response from a smart client call_expecting_body
786
:param verb: The verb used in that call.
787
:return: True if an unknown method was encountered.
789
# This might live better on
790
# bzrlib.smart.protocol.SmartClientRequestProtocolOne
791
if (response[0] == ('error', "Generic bzr smart protocol error: "
792
"bad request '%s'" % verb) or
793
response[0] == ('error', "Generic bzr smart protocol error: "
794
"bad request u'%s'" % verb)):
795
response[1].cancel_read_body()
799
def _get_parent_map(self, keys):
800
"""Helper for get_parent_map that performs the RPC."""
802
if NULL_REVISION in keys:
803
keys.discard(NULL_REVISION)
804
found_parents = {NULL_REVISION:()}
809
path = self.bzrdir._path_for_remote_call(self._client)
811
assert type(key) is str
812
verb = 'Repository.get_parent_map'
813
response = self._client.call_expecting_body(
815
if self._response_is_unknown_method(response, verb):
816
# Server that does not support this method, get the whole graph.
817
response = self._client.call_expecting_body(
818
'Repository.get_revision_graph', path, '')
819
if response[0][0] not in ['ok', 'nosuchrevision']:
820
reponse[1].cancel_read_body()
821
raise errors.UnexpectedSmartServerResponse(response[0])
822
elif response[0][0] not in ['ok']:
823
reponse[1].cancel_read_body()
824
raise errors.UnexpectedSmartServerResponse(response[0])
825
if response[0][0] == 'ok':
826
coded = response[1].read_body_bytes()
830
lines = coded.split('\n')
833
d = tuple(line.split())
835
revision_graph[d[0]] = d[1:]
837
# No parents - so give the Graph result (NULL_REVISION,).
838
revision_graph[d[0]] = (NULL_REVISION,)
839
return revision_graph
721
842
def get_signature_text(self, revision_id):
722
843
self._ensure_real()
853
973
self._ensure_real()
854
974
return self._real_repository.has_signature_for_revision_id(revision_id)
856
def get_data_stream(self, revision_ids):
976
def get_data_stream_for_search(self, search):
977
REQUEST_NAME = 'Repository.stream_revisions_chunked'
857
978
path = self.bzrdir._path_for_remote_call(self._client)
858
response, protocol = self._client.call_expecting_body(
859
'Repository.stream_knit_data_for_revisions', path, *revision_ids)
979
recipe = search.get_recipe()
980
start_keys = ' '.join(recipe[0])
981
stop_keys = ' '.join(recipe[1])
982
count = str(recipe[2])
983
body = '\n'.join((start_keys, stop_keys, count))
984
response, protocol = self._client.call_with_body_bytes_expecting_body(
985
REQUEST_NAME, (path,), body)
860
987
if response == ('ok',):
861
988
return self._deserialise_stream(protocol)
989
if response == ('NoSuchRevision', ):
990
# We cannot easily identify the revision that is missing in this
991
# situation without doing much more network IO. For now, bail.
992
raise NoSuchRevision(self, "unknown")
862
993
elif (response == ('error', "Generic bzr smart protocol error: "
863
"bad request 'Repository.stream_knit_data_for_revisions'") or
994
"bad request '%s'" % REQUEST_NAME) or
864
995
response == ('error', "Generic bzr smart protocol error: "
865
"bad request u'Repository.stream_knit_data_for_revisions'")):
996
"bad request u'%s'" % REQUEST_NAME)):
866
997
protocol.cancel_read_body()
867
998
self._ensure_real()
868
return self._real_repository.get_data_stream(revision_ids)
999
return self._real_repository.get_data_stream_for_search(search)
870
1001
raise errors.UnexpectedSmartServerResponse(response)
872
1003
def _deserialise_stream(self, protocol):
873
buffer = StringIO(protocol.read_body_bytes())
874
reader = ContainerReader(buffer)
875
for record_names, read_bytes in reader.iter_records():
877
# These records should have only one name, and that name
878
# should be a one-element tuple.
879
[name_tuple] = record_names
881
raise errors.SmartProtocolError(
882
'Repository data stream had invalid record name %r'
884
yield name_tuple, read_bytes(None)
1004
stream = protocol.read_streamed_body()
1005
container_parser = ContainerPushParser()
1006
for bytes in stream:
1007
container_parser.accept_bytes(bytes)
1008
records = container_parser.read_pending_records()
1009
for record_names, record_bytes in records:
1010
if len(record_names) != 1:
1011
# These records should have only one name, and that name
1012
# should be a one-element tuple.
1013
raise errors.SmartProtocolError(
1014
'Repository data stream had invalid record name %r'
1016
name_tuple = record_names[0]
1017
yield name_tuple, record_bytes
886
1019
def insert_data_stream(self, stream):
887
1020
self._ensure_real()