/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Martin Pool
  • Date: 2008-02-06 00:41:04 UTC
  • mfrom: (3215 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3219.
  • Revision ID: mbp@sourcefrog.net-20080206004104-mxtn32habuhjq6b8
Merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
from bzrlib import (
23
23
    branch,
 
24
    debug,
24
25
    errors,
 
26
    graph,
25
27
    lockdir,
26
28
    repository,
27
29
    revision,
28
30
)
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 (
38
40
    deprecated_method,
39
41
    zero_ninetyone,
40
42
    )
41
 
from bzrlib.trace import note
 
43
from bzrlib.revision import NULL_REVISION
 
44
from bzrlib.trace import mutter, note
42
45
 
43
46
# Note: RemoteBzrDirFormat is in bzrdir.py
44
47
 
127
130
        else:
128
131
            raise errors.UnexpectedSmartServerResponse(response)
129
132
 
 
133
    def _get_tree_branch(self):
 
134
        """See BzrDir._get_tree_branch()."""
 
135
        return None, self.open_branch()
 
136
 
130
137
    def open_branch(self, _unsupported=False):
131
138
        assert _unsupported == False, 'unsupported flag support not implemented yet.'
132
139
        reference_url = self.get_branch_reference()
263
270
        self._lock_token = None
264
271
        self._lock_count = 0
265
272
        self._leave_lock = False
 
273
        # A cache of looked up revision parent data; reset at unlock time.
 
274
        self._parents_map = None
266
275
        # For tests:
267
276
        # These depend on the actual remote format, so force them off for
268
277
        # maximum compatibility. XXX: In future these should depend on the
367
376
 
368
377
    def has_revision(self, revision_id):
369
378
        """See Repository.has_revision()."""
370
 
        if revision_id is None:
 
379
        if revision_id == NULL_REVISION:
371
380
            # The null revision is always present.
372
381
            return True
373
382
        path = self.bzrdir._path_for_remote_call(self._client)
375
384
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
376
385
        return response[0] == 'yes'
377
386
 
 
387
    def has_revisions(self, revision_ids):
 
388
        """See Repository.has_revisions()."""
 
389
        result = set()
 
390
        for revision_id in revision_ids:
 
391
            if self.has_revision(revision_id):
 
392
                result.add(revision_id)
 
393
        return result
 
394
 
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)
381
398
        
382
399
    def get_graph(self, other_repository=None):
383
400
        """Return the graph for this repository format"""
384
 
        self._ensure_real()
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)
386
408
 
387
409
    def gather_stats(self, revid=None, committers=None):
388
410
        """See Repository.gather_stats()."""
453
475
        if not self._lock_mode:
454
476
            self._lock_mode = 'r'
455
477
            self._lock_count = 1
 
478
            self._parents_map = {}
456
479
            if self._real_repository is not None:
457
480
                self._real_repository.lock_read()
458
481
        else:
490
513
                self._leave_lock = False
491
514
            self._lock_mode = 'w'
492
515
            self._lock_count = 1
 
516
            self._parents_map = {}
493
517
        elif self._lock_mode == 'r':
494
518
            raise errors.ReadOnlyError(self)
495
519
        else:
549
573
        self._lock_count -= 1
550
574
        if self._lock_count > 0:
551
575
            return
 
576
        self._parents_map = None
552
577
        old_mode = self._lock_mode
553
578
        self._lock_mode = None
554
579
        try:
628
653
                committer=committer, revprops=revprops, revision_id=revision_id)
629
654
        return builder
630
655
 
631
 
    @needs_write_lock
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)
635
659
 
636
 
    @needs_write_lock
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."""
672
695
        return False
673
696
 
 
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)
 
708
        return result
 
709
 
 
710
    @needs_read_lock
 
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.
 
713
        
 
714
        These are returned in topological order.
 
715
 
 
716
        revision_id: only return revision ids included by revision_id.
 
717
        """
 
718
        return repository.InterRepository.get(
 
719
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
 
720
 
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)
719
766
 
 
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)),
 
777
                        len(parent_map))
 
778
            self._parents_map.update(parent_map)
 
779
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
 
780
 
 
781
    def _response_is_unknown_method(self, response, verb):
 
782
        """Return True if response is an unknonwn method response to verb.
 
783
        
 
784
        :param response: The response from a smart client call_expecting_body
 
785
            call.
 
786
        :param verb: The verb used in that call.
 
787
        :return: True if an unknown method was encountered.
 
788
        """
 
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()
 
796
           return True
 
797
        return False
 
798
 
 
799
    def _get_parent_map(self, keys):
 
800
        """Helper for get_parent_map that performs the RPC."""
 
801
        keys = set(keys)
 
802
        if NULL_REVISION in keys:
 
803
            keys.discard(NULL_REVISION)
 
804
            found_parents = {NULL_REVISION:()}
 
805
            if not keys:
 
806
                return found_parents
 
807
        else:
 
808
            found_parents = {}
 
809
        path = self.bzrdir._path_for_remote_call(self._client)
 
810
        for key in keys:
 
811
            assert type(key) is str
 
812
        verb = 'Repository.get_parent_map'
 
813
        response = self._client.call_expecting_body(
 
814
            verb, path, *keys)
 
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()
 
827
            if coded == '':
 
828
                # no revisions found
 
829
                return {}
 
830
            lines = coded.split('\n')
 
831
            revision_graph = {}
 
832
            for line in lines:
 
833
                d = tuple(line.split())
 
834
                if len(d) > 1:
 
835
                    revision_graph[d[0]] = d[1:]
 
836
                else:
 
837
                    # No parents - so give the Graph result (NULL_REVISION,).
 
838
                    revision_graph[d[0]] = (NULL_REVISION,)
 
839
            return revision_graph
 
840
 
720
841
    @needs_read_lock
721
842
    def get_signature_text(self, revision_id):
722
843
        self._ensure_real()
781
902
        from bzrlib import osutils
782
903
        import tarfile
783
904
        import tempfile
784
 
        from StringIO import StringIO
785
905
        # TODO: Maybe a progress bar while streaming the tarball?
786
906
        note("Copying repository content as tarball...")
787
907
        tar_file = self._get_tarball('bz2')
853
973
        self._ensure_real()
854
974
        return self._real_repository.has_signature_for_revision_id(revision_id)
855
975
 
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)
 
986
 
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)
869
1000
        else:
870
1001
            raise errors.UnexpectedSmartServerResponse(response)
871
1002
 
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():
876
 
            try:
877
 
                # These records should have only one name, and that name
878
 
                # should be a one-element tuple.
879
 
                [name_tuple] = record_names
880
 
            except ValueError:
881
 
                raise errors.SmartProtocolError(
882
 
                    'Repository data stream had invalid record name %r'
883
 
                    % (record_names,))
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'
 
1015
                        % (record_names,))
 
1016
                name_tuple = record_names[0]
 
1017
                yield name_tuple, record_bytes
885
1018
 
886
1019
    def insert_data_stream(self, stream):
887
1020
        self._ensure_real()
906
1039
        return self._real_repository._check_for_inconsistent_revision_parents()
907
1040
 
908
1041
    def _make_parents_provider(self):
909
 
        self._ensure_real()
910
 
        return self._real_repository._make_parents_provider()
 
1042
        return self
911
1043
 
912
1044
 
913
1045
class RemoteBranchLockableFiles(LockableFiles):