~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-06-06 13:56:24 UTC
  • mfrom: (3477.1.2 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080606135624-1ambbf8pct52xfh8
(jam) Move bzrlib.tests.test_diff.UnicodeFilename into
        bzrlib.tests.UnicodeFilenameFeature

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
# across to run on the server.
19
19
 
20
20
import bz2
 
21
from cStringIO import StringIO
21
22
 
22
23
from bzrlib import (
23
24
    branch,
28
29
    repository,
29
30
    revision,
30
31
    symbol_versioning,
31
 
    urlutils,
32
32
)
33
33
from bzrlib.branch import BranchReferenceFormat
34
34
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
 
35
from bzrlib.config import BranchConfig, TreeConfig
35
36
from bzrlib.decorators import needs_read_lock, needs_write_lock
36
37
from bzrlib.errors import (
37
38
    NoSuchRevision,
38
39
    SmartProtocolError,
39
40
    )
40
41
from bzrlib.lockable_files import LockableFiles
 
42
from bzrlib.pack import ContainerPushParser
41
43
from bzrlib.smart import client, vfs
 
44
from bzrlib.symbol_versioning import (
 
45
    deprecated_in,
 
46
    deprecated_method,
 
47
    )
42
48
from bzrlib.revision import ensure_null, NULL_REVISION
43
49
from bzrlib.trace import mutter, note, warning
44
50
 
82
88
            self._real_bzrdir = BzrDir.open_from_transport(
83
89
                self.root_transport, _server_formats=False)
84
90
 
85
 
    def cloning_metadir(self, stacked=False):
86
 
        self._ensure_real()
87
 
        return self._real_bzrdir.cloning_metadir(stacked)
88
 
 
89
 
    def _translate_error(self, err, **context):
90
 
        _translate_error(err, bzrdir=self, **context)
91
 
        
92
91
    def create_repository(self, shared=False):
93
92
        self._ensure_real()
94
93
        self._real_bzrdir.create_repository(shared=shared)
126
125
        try:
127
126
            response = self._client.call('BzrDir.open_branch', path)
128
127
        except errors.ErrorFromSmartServer, err:
129
 
            self._translate_error(err)
 
128
            if err.error_tuple == ('nobranch',):
 
129
                raise errors.NotBranchError(path=self.root_transport.base)
 
130
            raise
130
131
        if response[0] == 'ok':
131
132
            if response[1] == '':
132
133
                # branch at this location.
163
164
                verb = 'BzrDir.find_repository'
164
165
                response = self._client.call(verb, path)
165
166
        except errors.ErrorFromSmartServer, err:
166
 
            self._translate_error(err)
 
167
            if err.error_verb == 'norepository':
 
168
                raise errors.NoRepositoryPresent(self)
 
169
            raise
167
170
        if response[0] != 'ok':
168
171
            raise errors.UnexpectedSmartServerResponse(response)
169
172
        if verb == 'BzrDir.find_repository':
178
181
            format.supports_tree_reference = (response[3] == 'yes')
179
182
            # No wire format to check this yet.
180
183
            format.supports_external_lookups = (response[4] == 'yes')
181
 
            # Used to support creating a real format instance when needed.
182
 
            format._creating_bzrdir = self
183
184
            return RemoteRepository(self, format)
184
185
        else:
185
186
            raise errors.NoRepositoryPresent(self)
215
216
        """Upgrading of remote bzrdirs is not supported yet."""
216
217
        return False
217
218
 
218
 
    def clone(self, url, revision_id=None, force_new_repo=False,
219
 
              preserve_stacking=False):
 
219
    def clone(self, url, revision_id=None, force_new_repo=False):
220
220
        self._ensure_real()
221
221
        return self._real_bzrdir.clone(url, revision_id=revision_id,
222
 
            force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
223
 
 
224
 
    def get_config(self):
225
 
        self._ensure_real()
226
 
        return self._real_bzrdir.get_config()
 
222
            force_new_repo=force_new_repo)
227
223
 
228
224
 
229
225
class RemoteRepositoryFormat(repository.RepositoryFormat):
239
235
    the class level.
240
236
    """
241
237
 
242
 
    _matchingbzrdir = RemoteBzrDirFormat()
 
238
    _matchingbzrdir = RemoteBzrDirFormat
243
239
 
244
240
    def initialize(self, a_bzrdir, shared=False):
245
241
        if not isinstance(a_bzrdir, RemoteBzrDir):
246
 
            prior_repo = self._creating_bzrdir.open_repository()
247
 
            prior_repo._ensure_real()
248
 
            return prior_repo._real_repository._format.initialize(
249
 
                a_bzrdir, shared=shared)
 
242
            raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
250
243
        return a_bzrdir.create_repository(shared=shared)
251
244
    
252
245
    def open(self, a_bzrdir):
315
308
        self._reconcile_fixes_text_parents = False
316
309
        self._reconcile_backsup_inventory = False
317
310
        self.base = self.bzrdir.transport.base
318
 
        # Additional places to query for data.
319
 
        self._fallback_repositories = []
320
311
 
321
312
    def __str__(self):
322
313
        return "%s(%s)" % (self.__class__.__name__, self.base)
350
341
 
351
342
        Used before calls to self._real_repository.
352
343
        """
353
 
        if self._real_repository is None:
 
344
        if not self._real_repository:
354
345
            self.bzrdir._ensure_real()
355
 
            self._set_real_repository(
356
 
                self.bzrdir._real_bzrdir.open_repository())
357
 
 
358
 
    def _translate_error(self, err, **context):
359
 
        self.bzrdir._translate_error(err, repository=self, **context)
 
346
            #self._real_repository = self.bzrdir._real_bzrdir.open_repository()
 
347
            self._set_real_repository(self.bzrdir._real_bzrdir.open_repository())
360
348
 
361
349
    def find_text_key_references(self):
362
350
        """Find the text key references within the repository.
400
388
            response = self._client.call_expecting_body(
401
389
                'Repository.get_revision_graph', path, revision_id)
402
390
        except errors.ErrorFromSmartServer, err:
403
 
            self._translate_error(err)
 
391
            if err.error_verb == 'nosuchrevision':
 
392
                raise NoSuchRevision(self, revision_id)
 
393
            raise
404
394
        response_tuple, response_handler = response
405
395
        if response_tuple[0] != 'ok':
406
396
            raise errors.UnexpectedSmartServerResponse(response_tuple)
426
416
            'Repository.has_revision', path, revision_id)
427
417
        if response[0] not in ('yes', 'no'):
428
418
            raise errors.UnexpectedSmartServerResponse(response)
429
 
        if response[0] == 'yes':
430
 
            return True
431
 
        for fallback_repo in self._fallback_repositories:
432
 
            if fallback_repo.has_revision(revision_id):
433
 
                return True
434
 
        return False
 
419
        return response[0] == 'yes'
435
420
 
436
421
    def has_revisions(self, revision_ids):
437
422
        """See Repository.has_revisions()."""
438
 
        # FIXME: This does many roundtrips, particularly when there are
439
 
        # fallback repositories.  -- mbp 20080905
440
423
        result = set()
441
424
        for revision_id in revision_ids:
442
425
            if self.has_revision(revision_id):
542
525
        try:
543
526
            response = self._client.call('Repository.lock_write', path, token)
544
527
        except errors.ErrorFromSmartServer, err:
545
 
            self._translate_error(err, token=token)
 
528
            if err.error_verb == 'LockContention':
 
529
                raise errors.LockContention('(remote lock)')
 
530
            elif err.error_verb == 'UnlockableTransport':
 
531
                raise errors.UnlockableTransport(self.bzrdir.root_transport)
 
532
            elif err.error_verb == 'LockFailed':
 
533
                raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
534
            raise
546
535
 
547
536
        if response[0] == 'ok':
548
537
            ok, token = response
550
539
        else:
551
540
            raise errors.UnexpectedSmartServerResponse(response)
552
541
 
553
 
    def lock_write(self, token=None, _skip_rpc=False):
 
542
    def lock_write(self, token=None):
554
543
        if not self._lock_mode:
555
 
            if _skip_rpc:
556
 
                if self._lock_token is not None:
557
 
                    if token != self._lock_token:
558
 
                        raise errors.TokenMismatch(token, self._lock_token)
559
 
                self._lock_token = token
560
 
            else:
561
 
                self._lock_token = self._remote_lock_write(token)
 
544
            self._lock_token = self._remote_lock_write(token)
562
545
            # if self._lock_token is None, then this is something like packs or
563
546
            # svn where we don't get to lock the repo, or a weave style repository
564
547
            # where we cannot lock it over the wire and attempts to do so will
596
579
        :param repository: The repository to fallback to for non-hpss
597
580
            implemented operations.
598
581
        """
599
 
        if self._real_repository is not None:
600
 
            raise AssertionError('_real_repository is already set')
601
582
        if isinstance(repository, RemoteRepository):
602
583
            raise AssertionError()
603
584
        self._real_repository = repository
604
 
        for fb in self._fallback_repositories:
605
 
            self._real_repository.add_fallback_repository(fb)
606
585
        if self._lock_mode == 'w':
607
586
            # if we are already locked, the real repository must be able to
608
587
            # acquire the lock with our token.
629
608
        try:
630
609
            response = self._client.call('Repository.unlock', path, token)
631
610
        except errors.ErrorFromSmartServer, err:
632
 
            self._translate_error(err, token=token)
 
611
            if err.error_verb == 'TokenMismatch':
 
612
                raise errors.TokenMismatch(token, '(remote token)')
 
613
            raise
633
614
        if response == ('ok',):
634
615
            return
635
616
        else:
714
695
        # FIXME: It ought to be possible to call this without immediately
715
696
        # triggering _ensure_real.  For now it's the easiest thing to do.
716
697
        self._ensure_real()
717
 
        real_repo = self._real_repository
718
 
        builder = real_repo.get_commit_builder(branch, parents,
 
698
        builder = self._real_repository.get_commit_builder(branch, parents,
719
699
                config, timestamp=timestamp, timezone=timezone,
720
700
                committer=committer, revprops=revprops, revision_id=revision_id)
721
701
        return builder
722
702
 
723
 
    def add_fallback_repository(self, repository):
724
 
        """Add a repository to use for looking up data not held locally.
725
 
        
726
 
        :param repository: A repository.
727
 
        """
728
 
        # XXX: At the moment the RemoteRepository will allow fallbacks
729
 
        # unconditionally - however, a _real_repository will usually exist,
730
 
        # and may raise an error if it's not accommodated by the underlying
731
 
        # format.  Eventually we should check when opening the repository
732
 
        # whether it's willing to allow them or not.
733
 
        #
734
 
        # We need to accumulate additional repositories here, to pass them in
735
 
        # on various RPC's.
736
 
        self._fallback_repositories.append(repository)
737
 
        # They are also seen by the fallback repository.  If it doesn't exist
738
 
        # yet they'll be added then.  This implicitly copies them.
739
 
        self._ensure_real()
740
 
 
741
703
    def add_inventory(self, revid, inv, parents):
742
704
        self._ensure_real()
743
705
        return self._real_repository.add_inventory(revid, inv, parents)
761
723
        self._ensure_real()
762
724
        return self._real_repository.get_revision(revision_id)
763
725
 
 
726
    @property
 
727
    def weave_store(self):
 
728
        self._ensure_real()
 
729
        return self._real_repository.weave_store
 
730
 
764
731
    def get_transaction(self):
765
732
        self._ensure_real()
766
733
        return self._real_repository.get_transaction()
799
766
        return repository.InterRepository.get(
800
767
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
801
768
 
802
 
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False):
 
769
    def fetch(self, source, revision_id=None, pb=None):
803
770
        if self.has_same_location(source):
804
771
            # check that last_revision is in 'from' and then return a
805
772
            # no-operation.
807
774
                not revision.is_null(revision_id)):
808
775
                self.get_revision(revision_id)
809
776
            return 0, []
810
 
        inter = repository.InterRepository.get(source, self)
811
 
        try:
812
 
            return inter.fetch(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts)
813
 
 
814
 
        except NotImplementedError:
815
 
            raise errors.IncompatibleRepositories(source, self)
 
777
        self._ensure_real()
 
778
        return self._real_repository.fetch(
 
779
            source, revision_id=revision_id, pb=pb)
816
780
 
817
781
    def create_bundle(self, target, base, fileobj, format=None):
818
782
        self._ensure_real()
819
783
        self._real_repository.create_bundle(target, base, fileobj, format)
820
784
 
 
785
    @property
 
786
    def control_weaves(self):
 
787
        self._ensure_real()
 
788
        return self._real_repository.control_weaves
 
789
 
821
790
    @needs_read_lock
822
791
    def get_ancestry(self, revision_id, topo_sorted=True):
823
792
        self._ensure_real()
824
793
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
825
794
 
 
795
    @needs_read_lock
 
796
    def get_inventory_weave(self):
 
797
        self._ensure_real()
 
798
        return self._real_repository.get_inventory_weave()
 
799
 
826
800
    def fileids_altered_by_revision_ids(self, revision_ids):
827
801
        self._ensure_real()
828
802
        return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
838
812
        self._ensure_real()
839
813
        return self._real_repository.iter_files_bytes(desired_files)
840
814
 
841
 
    @property
842
 
    def _fetch_order(self):
843
 
        """Decorate the real repository for now.
844
 
 
845
 
        In the long term getting this back from the remote repository as part
846
 
        of open would be more efficient.
847
 
        """
848
 
        self._ensure_real()
849
 
        return self._real_repository._fetch_order
850
 
 
851
 
    @property
852
 
    def _fetch_uses_deltas(self):
853
 
        """Decorate the real repository for now.
854
 
 
855
 
        In the long term getting this back from the remote repository as part
856
 
        of open would be more efficient.
857
 
        """
858
 
        self._ensure_real()
859
 
        return self._real_repository._fetch_uses_deltas
860
 
 
861
 
    @property
862
 
    def _fetch_reconcile(self):
863
 
        """Decorate the real repository for now.
864
 
 
865
 
        In the long term getting this back from the remote repository as part
866
 
        of open would be more efficient.
867
 
        """
868
 
        self._ensure_real()
869
 
        return self._real_repository._fetch_reconcile
870
 
 
871
815
    def get_parent_map(self, keys):
872
816
        """See bzrlib.Graph.get_parent_map()."""
873
817
        # Hack to build up the caching logic.
896
840
    def _get_parent_map(self, keys):
897
841
        """Helper for get_parent_map that performs the RPC."""
898
842
        medium = self._client._medium
899
 
        if medium._is_remote_before((1, 2)):
 
843
        if not medium._remote_is_at_least_1_2:
900
844
            # We already found out that the server can't understand
901
845
            # Repository.get_parent_map requests, so just fetch the whole
902
846
            # graph.
975
919
            medium.disconnect()
976
920
            # To avoid having to disconnect repeatedly, we keep track of the
977
921
            # fact the server doesn't understand remote methods added in 1.2.
978
 
            medium._remember_remote_is_before((1, 2))
 
922
            medium._remote_is_at_least_1_2 = False
979
923
            return self.get_revision_graph(None)
980
924
        response_tuple, response_handler = response
981
925
        if response_tuple[0] not in ['ok']:
1061
1005
        # destination
1062
1006
        from bzrlib import osutils
1063
1007
        import tarfile
 
1008
        import tempfile
1064
1009
        # TODO: Maybe a progress bar while streaming the tarball?
1065
1010
        note("Copying repository content as tarball...")
1066
1011
        tar_file = self._get_tarball('bz2')
1070
1015
        try:
1071
1016
            tar = tarfile.open('repository', fileobj=tar_file,
1072
1017
                mode='r|bz2')
1073
 
            tmpdir = osutils.mkdtemp()
 
1018
            tmpdir = tempfile.mkdtemp()
1074
1019
            try:
1075
1020
                _extract_tar(tar, tmpdir)
1076
1021
                tmp_bzrdir = BzrDir.open(tmpdir)
1084
1029
        # TODO: Suggestion from john: using external tar is much faster than
1085
1030
        # python's tarfile library, but it may not work on windows.
1086
1031
 
1087
 
    @property
1088
 
    def inventories(self):
1089
 
        """Decorate the real repository for now.
1090
 
 
1091
 
        In the long term a full blown network facility is needed to
1092
 
        avoid creating a real repository object locally.
1093
 
        """
1094
 
        self._ensure_real()
1095
 
        return self._real_repository.inventories
1096
 
 
1097
1032
    @needs_write_lock
1098
1033
    def pack(self):
1099
1034
        """Compress the data within the repository.
1103
1038
        self._ensure_real()
1104
1039
        return self._real_repository.pack()
1105
1040
 
1106
 
    @property
1107
 
    def revisions(self):
1108
 
        """Decorate the real repository for now.
1109
 
 
1110
 
        In the short term this should become a real object to intercept graph
1111
 
        lookups.
1112
 
 
1113
 
        In the long term a full blown network facility is needed.
1114
 
        """
1115
 
        self._ensure_real()
1116
 
        return self._real_repository.revisions
1117
 
 
1118
1041
    def set_make_working_trees(self, new_value):
1119
1042
        self._ensure_real()
1120
1043
        self._real_repository.set_make_working_trees(new_value)
1121
1044
 
1122
 
    @property
1123
 
    def signatures(self):
1124
 
        """Decorate the real repository for now.
1125
 
 
1126
 
        In the long term a full blown network facility is needed to avoid
1127
 
        creating a real repository object locally.
1128
 
        """
1129
 
        self._ensure_real()
1130
 
        return self._real_repository.signatures
1131
 
 
1132
1045
    @needs_write_lock
1133
1046
    def sign_revision(self, revision_id, gpg_strategy):
1134
1047
        self._ensure_real()
1135
1048
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
1136
1049
 
1137
 
    @property
1138
 
    def texts(self):
1139
 
        """Decorate the real repository for now.
1140
 
 
1141
 
        In the long term a full blown network facility is needed to avoid
1142
 
        creating a real repository object locally.
1143
 
        """
1144
 
        self._ensure_real()
1145
 
        return self._real_repository.texts
1146
 
 
1147
1050
    @needs_read_lock
1148
1051
    def get_revisions(self, revision_ids):
1149
1052
        self._ensure_real()
1175
1078
        self._ensure_real()
1176
1079
        return self._real_repository.has_signature_for_revision_id(revision_id)
1177
1080
 
 
1081
    def get_data_stream_for_search(self, search):
 
1082
        medium = self._client._medium
 
1083
        if not medium._remote_is_at_least_1_2:
 
1084
            self._ensure_real()
 
1085
            return self._real_repository.get_data_stream_for_search(search)
 
1086
        REQUEST_NAME = 'Repository.stream_revisions_chunked'
 
1087
        path = self.bzrdir._path_for_remote_call(self._client)
 
1088
        body = self._serialise_search_recipe(search.get_recipe())
 
1089
        try:
 
1090
            result = self._client.call_with_body_bytes_expecting_body(
 
1091
                REQUEST_NAME, (path,), body)
 
1092
            response, protocol = result
 
1093
        except errors.UnknownSmartMethod:
 
1094
            # Server does not support this method, so fall back to VFS.
 
1095
            # Worse, we have to force a disconnection, because the server now
 
1096
            # doesn't realise it has a body on the wire to consume, so the
 
1097
            # only way to recover is to abandon the connection.
 
1098
            warning(
 
1099
                'Server is too old for streaming pull, reconnecting.  '
 
1100
                '(Upgrade the server to Bazaar 1.2 to avoid this)')
 
1101
            medium.disconnect()
 
1102
            # To avoid having to disconnect repeatedly, we keep track of the
 
1103
            # fact the server doesn't understand this remote method.
 
1104
            medium._remote_is_at_least_1_2 = False
 
1105
            self._ensure_real()
 
1106
            return self._real_repository.get_data_stream_for_search(search)
 
1107
 
 
1108
        if response == ('ok',):
 
1109
            return self._deserialise_stream(protocol)
 
1110
        if response == ('NoSuchRevision', ):
 
1111
            # We cannot easily identify the revision that is missing in this
 
1112
            # situation without doing much more network IO. For now, bail.
 
1113
            raise NoSuchRevision(self, "unknown")
 
1114
        else:
 
1115
            raise errors.UnexpectedSmartServerResponse(response)
 
1116
 
 
1117
    def _deserialise_stream(self, protocol):
 
1118
        stream = protocol.read_streamed_body()
 
1119
        container_parser = ContainerPushParser()
 
1120
        for bytes in stream:
 
1121
            container_parser.accept_bytes(bytes)
 
1122
            records = container_parser.read_pending_records()
 
1123
            for record_names, record_bytes in records:
 
1124
                if len(record_names) != 1:
 
1125
                    # These records should have only one name, and that name
 
1126
                    # should be a one-element tuple.
 
1127
                    raise errors.SmartProtocolError(
 
1128
                        'Repository data stream had invalid record name %r'
 
1129
                        % (record_names,))
 
1130
                name_tuple = record_names[0]
 
1131
                yield name_tuple, record_bytes
 
1132
 
 
1133
    def insert_data_stream(self, stream):
 
1134
        self._ensure_real()
 
1135
        self._real_repository.insert_data_stream(stream)
 
1136
 
1178
1137
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1179
1138
        self._ensure_real()
1180
1139
        return self._real_repository.item_keys_introduced_by(revision_ids,
1271
1230
        # And the parent's __init__ doesn't do much anyway.
1272
1231
        self._revision_id_to_revno_cache = None
1273
1232
        self._revision_history_cache = None
1274
 
        self._last_revision_info_cache = None
1275
1233
        self.bzrdir = remote_bzrdir
1276
1234
        if _client is not None:
1277
1235
            self._client = _client
1299
1257
        self._repo_lock_token = None
1300
1258
        self._lock_count = 0
1301
1259
        self._leave_lock = False
1302
 
        # The base class init is not called, so we duplicate this:
1303
 
        hooks = branch.Branch.hooks['open']
1304
 
        for hook in hooks:
1305
 
            hook(self)
1306
 
        self._setup_stacking()
1307
 
 
1308
 
    def _setup_stacking(self):
1309
 
        # configure stacking into the remote repository, by reading it from
1310
 
        # the vfs branch.
1311
 
        try:
1312
 
            fallback_url = self.get_stacked_on_url()
1313
 
        except (errors.NotStacked, errors.UnstackableBranchFormat,
1314
 
            errors.UnstackableRepositoryFormat), e:
1315
 
            return
1316
 
        # it's relative to this branch...
1317
 
        fallback_url = urlutils.join(self.base, fallback_url)
1318
 
        transports = [self.bzrdir.root_transport]
1319
 
        if self._real_branch is not None:
1320
 
            transports.append(self._real_branch._transport)
1321
 
        fallback_bzrdir = BzrDir.open(fallback_url, transports)
1322
 
        fallback_repo = fallback_bzrdir.open_repository()
1323
 
        self.repository.add_fallback_repository(fallback_repo)
1324
1260
 
1325
1261
    def _get_real_transport(self):
1326
1262
        # if we try vfs access, return the real branch's vfs transport
1345
1281
                    'to use vfs implementation')
1346
1282
            self.bzrdir._ensure_real()
1347
1283
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
1348
 
            if self.repository._real_repository is None:
1349
 
                # Give the remote repository the matching real repo.
1350
 
                real_repo = self._real_branch.repository
1351
 
                if isinstance(real_repo, RemoteRepository):
1352
 
                    real_repo._ensure_real()
1353
 
                    real_repo = real_repo._real_repository
1354
 
                self.repository._set_real_repository(real_repo)
1355
 
            # Give the real branch the remote repository to let fast-pathing
1356
 
            # happen.
 
1284
            # Give the remote repository the matching real repo.
 
1285
            real_repo = self._real_branch.repository
 
1286
            if isinstance(real_repo, RemoteRepository):
 
1287
                real_repo._ensure_real()
 
1288
                real_repo = real_repo._real_repository
 
1289
            self.repository._set_real_repository(real_repo)
 
1290
            # Give the branch the remote repository to let fast-pathing happen.
1357
1291
            self._real_branch.repository = self.repository
 
1292
            # XXX: deal with _lock_mode == 'w'
1358
1293
            if self._lock_mode == 'r':
1359
1294
                self._real_branch.lock_read()
1360
 
            elif self._lock_mode == 'w':
1361
 
                self._real_branch.lock_write(token=self._lock_token)
1362
 
 
1363
 
    def _translate_error(self, err, **context):
1364
 
        self.repository._translate_error(err, branch=self, **context)
1365
 
 
1366
 
    def _clear_cached_state(self):
1367
 
        super(RemoteBranch, self)._clear_cached_state()
1368
 
        if self._real_branch is not None:
1369
 
            self._real_branch._clear_cached_state()
1370
 
 
1371
 
    def _clear_cached_state_of_remote_branch_only(self):
1372
 
        """Like _clear_cached_state, but doesn't clear the cache of
1373
 
        self._real_branch.
1374
 
 
1375
 
        This is useful when falling back to calling a method of
1376
 
        self._real_branch that changes state.  In that case the underlying
1377
 
        branch changes, so we need to invalidate this RemoteBranch's cache of
1378
 
        it.  However, there's no need to invalidate the _real_branch's cache
1379
 
        too, in fact doing so might harm performance.
1380
 
        """
1381
 
        super(RemoteBranch, self)._clear_cached_state()
1382
 
        
 
1295
 
1383
1296
    @property
1384
1297
    def control_files(self):
1385
1298
        # Defer actually creating RemoteBranchLockableFiles until its needed,
1399
1312
        self._ensure_real()
1400
1313
        return self._real_branch.get_physical_lock_status()
1401
1314
 
1402
 
    def get_stacked_on_url(self):
1403
 
        """Get the URL this branch is stacked against.
1404
 
 
1405
 
        :raises NotStacked: If the branch is not stacked.
1406
 
        :raises UnstackableBranchFormat: If the branch does not support
1407
 
            stacking.
1408
 
        :raises UnstackableRepositoryFormat: If the repository does not support
1409
 
            stacking.
1410
 
        """
1411
 
        try:
1412
 
            response = self._client.call('Branch.get_stacked_on_url',
1413
 
                self._remote_path())
1414
 
            if response[0] != 'ok':
1415
 
                raise errors.UnexpectedSmartServerResponse(response)
1416
 
            return response[1]
1417
 
        except errors.ErrorFromSmartServer, err:
1418
 
            # there may not be a repository yet, so we can't call through
1419
 
            # its _translate_error
1420
 
            _translate_error(err, branch=self)
1421
 
        except errors.UnknownSmartMethod, err:
1422
 
            self._ensure_real()
1423
 
            return self._real_branch.get_stacked_on_url()
1424
 
 
1425
1315
    def lock_read(self):
1426
 
        self.repository.lock_read()
1427
1316
        if not self._lock_mode:
1428
1317
            self._lock_mode = 'r'
1429
1318
            self._lock_count = 1
1439
1328
            branch_token = token
1440
1329
            repo_token = self.repository.lock_write()
1441
1330
            self.repository.unlock()
 
1331
        path = self.bzrdir._path_for_remote_call(self._client)
1442
1332
        try:
1443
1333
            response = self._client.call(
1444
 
                'Branch.lock_write', self._remote_path(),
1445
 
                branch_token, repo_token or '')
 
1334
                'Branch.lock_write', path, branch_token, repo_token or '')
1446
1335
        except errors.ErrorFromSmartServer, err:
1447
 
            self._translate_error(err, token=token)
 
1336
            if err.error_verb == 'LockContention':
 
1337
                raise errors.LockContention('(remote lock)')
 
1338
            elif err.error_verb == 'TokenMismatch':
 
1339
                raise errors.TokenMismatch(token, '(remote token)')
 
1340
            elif err.error_verb == 'UnlockableTransport':
 
1341
                raise errors.UnlockableTransport(self.bzrdir.root_transport)
 
1342
            elif err.error_verb == 'ReadOnlyError':
 
1343
                raise errors.ReadOnlyError(self)
 
1344
            elif err.error_verb == 'LockFailed':
 
1345
                raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
1346
            raise
1448
1347
        if response[0] != 'ok':
1449
1348
            raise errors.UnexpectedSmartServerResponse(response)
1450
1349
        ok, branch_token, repo_token = response
1452
1351
            
1453
1352
    def lock_write(self, token=None):
1454
1353
        if not self._lock_mode:
1455
 
            # Lock the branch and repo in one remote call.
1456
1354
            remote_tokens = self._remote_lock_write(token)
1457
1355
            self._lock_token, self._repo_lock_token = remote_tokens
1458
1356
            if not self._lock_token:
1459
1357
                raise SmartProtocolError('Remote server did not return a token!')
1460
 
            # Tell the self.repository object that it is locked.
1461
 
            self.repository.lock_write(
1462
 
                self._repo_lock_token, _skip_rpc=True)
1463
 
 
 
1358
            # TODO: We really, really, really don't want to call _ensure_real
 
1359
            # here, but it's the easiest way to ensure coherency between the
 
1360
            # state of the RemoteBranch and RemoteRepository objects and the
 
1361
            # physical locks.  If we don't materialise the real objects here,
 
1362
            # then getting everything in the right state later is complex, so
 
1363
            # for now we just do it the lazy way.
 
1364
            #   -- Andrew Bennetts, 2007-02-22.
 
1365
            self._ensure_real()
1464
1366
            if self._real_branch is not None:
1465
 
                self._real_branch.lock_write(token=self._lock_token)
 
1367
                self._real_branch.repository.lock_write(
 
1368
                    token=self._repo_lock_token)
 
1369
                try:
 
1370
                    self._real_branch.lock_write(token=self._lock_token)
 
1371
                finally:
 
1372
                    self._real_branch.repository.unlock()
1466
1373
            if token is not None:
1467
1374
                self._leave_lock = True
1468
1375
            else:
 
1376
                # XXX: this case seems to be unreachable; token cannot be None.
1469
1377
                self._leave_lock = False
1470
1378
            self._lock_mode = 'w'
1471
1379
            self._lock_count = 1
1473
1381
            raise errors.ReadOnlyTransaction
1474
1382
        else:
1475
1383
            if token is not None:
1476
 
                # A token was given to lock_write, and we're relocking, so
1477
 
                # check that the given token actually matches the one we
1478
 
                # already have.
 
1384
                # A token was given to lock_write, and we're relocking, so check
 
1385
                # that the given token actually matches the one we already have.
1479
1386
                if token != self._lock_token:
1480
1387
                    raise errors.TokenMismatch(token, self._lock_token)
1481
1388
            self._lock_count += 1
1482
 
            # Re-lock the repository too.
1483
 
            self.repository.lock_write(self._repo_lock_token)
1484
1389
        return self._lock_token or None
1485
1390
 
1486
1391
    def _unlock(self, branch_token, repo_token):
 
1392
        path = self.bzrdir._path_for_remote_call(self._client)
1487
1393
        try:
1488
 
            response = self._client.call('Branch.unlock', self._remote_path(), branch_token,
 
1394
            response = self._client.call('Branch.unlock', path, branch_token,
1489
1395
                                         repo_token or '')
1490
1396
        except errors.ErrorFromSmartServer, err:
1491
 
            self._translate_error(err, token=str((branch_token, repo_token)))
 
1397
            if err.error_verb == 'TokenMismatch':
 
1398
                raise errors.TokenMismatch(
 
1399
                    str((branch_token, repo_token)), '(remote tokens)')
 
1400
            raise
1492
1401
        if response == ('ok',):
1493
1402
            return
1494
1403
        raise errors.UnexpectedSmartServerResponse(response)
1495
1404
 
1496
1405
    def unlock(self):
1497
 
        try:
1498
 
            self._lock_count -= 1
1499
 
            if not self._lock_count:
1500
 
                self._clear_cached_state()
1501
 
                mode = self._lock_mode
1502
 
                self._lock_mode = None
1503
 
                if self._real_branch is not None:
1504
 
                    if (not self._leave_lock and mode == 'w' and
1505
 
                        self._repo_lock_token):
1506
 
                        # If this RemoteBranch will remove the physical lock
1507
 
                        # for the repository, make sure the _real_branch
1508
 
                        # doesn't do it first.  (Because the _real_branch's
1509
 
                        # repository is set to be the RemoteRepository.)
1510
 
                        self._real_branch.repository.leave_lock_in_place()
1511
 
                    self._real_branch.unlock()
1512
 
                if mode != 'w':
1513
 
                    # Only write-locked branched need to make a remote method
1514
 
                    # call to perfom the unlock.
1515
 
                    return
1516
 
                if not self._lock_token:
1517
 
                    raise AssertionError('Locked, but no token!')
1518
 
                branch_token = self._lock_token
1519
 
                repo_token = self._repo_lock_token
1520
 
                self._lock_token = None
1521
 
                self._repo_lock_token = None
1522
 
                if not self._leave_lock:
1523
 
                    self._unlock(branch_token, repo_token)
1524
 
        finally:
1525
 
            self.repository.unlock()
 
1406
        self._lock_count -= 1
 
1407
        if not self._lock_count:
 
1408
            self._clear_cached_state()
 
1409
            mode = self._lock_mode
 
1410
            self._lock_mode = None
 
1411
            if self._real_branch is not None:
 
1412
                if (not self._leave_lock and mode == 'w' and
 
1413
                    self._repo_lock_token):
 
1414
                    # If this RemoteBranch will remove the physical lock for the
 
1415
                    # repository, make sure the _real_branch doesn't do it
 
1416
                    # first.  (Because the _real_branch's repository is set to
 
1417
                    # be the RemoteRepository.)
 
1418
                    self._real_branch.repository.leave_lock_in_place()
 
1419
                self._real_branch.unlock()
 
1420
            if mode != 'w':
 
1421
                # Only write-locked branched need to make a remote method call
 
1422
                # to perfom the unlock.
 
1423
                return
 
1424
            if not self._lock_token:
 
1425
                raise AssertionError('Locked, but no token!')
 
1426
            branch_token = self._lock_token
 
1427
            repo_token = self._repo_lock_token
 
1428
            self._lock_token = None
 
1429
            self._repo_lock_token = None
 
1430
            if not self._leave_lock:
 
1431
                self._unlock(branch_token, repo_token)
1526
1432
 
1527
1433
    def break_lock(self):
1528
1434
        self._ensure_real()
1538
1444
            raise NotImplementedError(self.dont_leave_lock_in_place)
1539
1445
        self._leave_lock = False
1540
1446
 
1541
 
    def _last_revision_info(self):
1542
 
        response = self._client.call('Branch.last_revision_info', self._remote_path())
 
1447
    def last_revision_info(self):
 
1448
        """See Branch.last_revision_info()."""
 
1449
        path = self.bzrdir._path_for_remote_call(self._client)
 
1450
        response = self._client.call('Branch.last_revision_info', path)
1543
1451
        if response[0] != 'ok':
1544
1452
            raise SmartProtocolError('unexpected response code %s' % (response,))
1545
1453
        revno = int(response[1])
1548
1456
 
1549
1457
    def _gen_revision_history(self):
1550
1458
        """See Branch._gen_revision_history()."""
 
1459
        path = self.bzrdir._path_for_remote_call(self._client)
1551
1460
        response_tuple, response_handler = self._client.call_expecting_body(
1552
 
            'Branch.revision_history', self._remote_path())
 
1461
            'Branch.revision_history', path)
1553
1462
        if response_tuple[0] != 'ok':
1554
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
1463
            raise UnexpectedSmartServerResponse(response_tuple)
1555
1464
        result = response_handler.read_body_bytes().split('\x00')
1556
1465
        if result == ['']:
1557
1466
            return []
1558
1467
        return result
1559
1468
 
1560
 
    def _remote_path(self):
1561
 
        return self.bzrdir._path_for_remote_call(self._client)
1562
 
 
1563
 
    def _set_last_revision_descendant(self, revision_id, other_branch,
1564
 
            allow_diverged=False, allow_overwrite_descendant=False):
1565
 
        try:
1566
 
            response = self._client.call('Branch.set_last_revision_ex',
1567
 
                self._remote_path(), self._lock_token, self._repo_lock_token, revision_id,
1568
 
                int(allow_diverged), int(allow_overwrite_descendant))
1569
 
        except errors.ErrorFromSmartServer, err:
1570
 
            self._translate_error(err, other_branch=other_branch)
1571
 
        self._clear_cached_state()
1572
 
        if len(response) != 3 and response[0] != 'ok':
1573
 
            raise errors.UnexpectedSmartServerResponse(response)
1574
 
        new_revno, new_revision_id = response[1:]
1575
 
        self._last_revision_info_cache = new_revno, new_revision_id
1576
 
        if self._real_branch is not None:
1577
 
            cache = new_revno, new_revision_id
1578
 
            self._real_branch._last_revision_info_cache = cache
1579
 
 
1580
 
    def _set_last_revision(self, revision_id):
1581
 
        self._clear_cached_state()
1582
 
        try:
1583
 
            response = self._client.call('Branch.set_last_revision',
1584
 
                self._remote_path(), self._lock_token, self._repo_lock_token, revision_id)
1585
 
        except errors.ErrorFromSmartServer, err:
1586
 
            self._translate_error(err)
1587
 
        if response != ('ok',):
1588
 
            raise errors.UnexpectedSmartServerResponse(response)
1589
 
 
1590
1469
    @needs_write_lock
1591
1470
    def set_revision_history(self, rev_history):
1592
1471
        # Send just the tip revision of the history; the server will generate
1593
1472
        # the full history from that.  If the revision doesn't exist in this
1594
1473
        # branch, NoSuchRevision will be raised.
 
1474
        path = self.bzrdir._path_for_remote_call(self._client)
1595
1475
        if rev_history == []:
1596
1476
            rev_id = 'null:'
1597
1477
        else:
1598
1478
            rev_id = rev_history[-1]
1599
 
        self._set_last_revision(rev_id)
 
1479
        self._clear_cached_state()
 
1480
        try:
 
1481
            response = self._client.call('Branch.set_last_revision',
 
1482
                path, self._lock_token, self._repo_lock_token, rev_id)
 
1483
        except errors.ErrorFromSmartServer, err:
 
1484
            if err.error_verb == 'NoSuchRevision':
 
1485
                raise NoSuchRevision(self, rev_id)
 
1486
            raise
 
1487
        if response != ('ok',):
 
1488
            raise errors.UnexpectedSmartServerResponse(response)
1600
1489
        self._cache_revision_history(rev_history)
1601
1490
 
1602
1491
    def get_parent(self):
1607
1496
        self._ensure_real()
1608
1497
        return self._real_branch.set_parent(url)
1609
1498
        
1610
 
    def set_stacked_on_url(self, stacked_location):
1611
 
        """Set the URL this branch is stacked against.
1612
 
 
1613
 
        :raises UnstackableBranchFormat: If the branch does not support
1614
 
            stacking.
1615
 
        :raises UnstackableRepositoryFormat: If the repository does not support
1616
 
            stacking.
1617
 
        """
1618
 
        self._ensure_real()
1619
 
        return self._real_branch.set_stacked_on_url(stacked_location)
1620
 
 
1621
1499
    def sprout(self, to_bzrdir, revision_id=None):
1622
1500
        # Like Branch.sprout, except that it sprouts a branch in the default
1623
1501
        # format, because RemoteBranches can't be created at arbitrary URLs.
1632
1510
    @needs_write_lock
1633
1511
    def pull(self, source, overwrite=False, stop_revision=None,
1634
1512
             **kwargs):
1635
 
        self._clear_cached_state_of_remote_branch_only()
 
1513
        # FIXME: This asks the real branch to run the hooks, which means
 
1514
        # they're called with the wrong target branch parameter. 
 
1515
        # The test suite specifically allows this at present but it should be
 
1516
        # fixed.  It should get a _override_hook_target branch,
 
1517
        # as push does.  -- mbp 20070405
1636
1518
        self._ensure_real()
1637
 
        return self._real_branch.pull(
 
1519
        self._real_branch.pull(
1638
1520
            source, overwrite=overwrite, stop_revision=stop_revision,
1639
 
            _override_hook_target=self, **kwargs)
 
1521
            **kwargs)
1640
1522
 
1641
1523
    @needs_read_lock
1642
1524
    def push(self, target, overwrite=False, stop_revision=None):
1648
1530
    def is_locked(self):
1649
1531
        return self._lock_count >= 1
1650
1532
 
1651
 
    @needs_read_lock
1652
 
    def revision_id_to_revno(self, revision_id):
1653
 
        self._ensure_real()
1654
 
        return self._real_branch.revision_id_to_revno(revision_id)
1655
 
 
1656
1533
    @needs_write_lock
1657
1534
    def set_last_revision_info(self, revno, revision_id):
1658
1535
        revision_id = ensure_null(revision_id)
 
1536
        path = self.bzrdir._path_for_remote_call(self._client)
1659
1537
        try:
1660
1538
            response = self._client.call('Branch.set_last_revision_info',
1661
 
                self._remote_path(), self._lock_token, self._repo_lock_token, str(revno), revision_id)
 
1539
                path, self._lock_token, self._repo_lock_token, str(revno), revision_id)
1662
1540
        except errors.UnknownSmartMethod:
1663
1541
            self._ensure_real()
1664
 
            self._clear_cached_state_of_remote_branch_only()
1665
 
            self._real_branch.set_last_revision_info(revno, revision_id)
1666
 
            self._last_revision_info_cache = revno, revision_id
1667
 
            return
 
1542
            self._clear_cached_state()
 
1543
            return self._real_branch.set_last_revision_info(revno, revision_id)
1668
1544
        except errors.ErrorFromSmartServer, err:
1669
 
            self._translate_error(err)
 
1545
            if err.error_verb == 'NoSuchRevision':
 
1546
                raise NoSuchRevision(self, err.error_args[0])
 
1547
            raise
1670
1548
        if response == ('ok',):
1671
1549
            self._clear_cached_state()
1672
 
            self._last_revision_info_cache = revno, revision_id
1673
 
            # Update the _real_branch's cache too.
1674
 
            if self._real_branch is not None:
1675
 
                cache = self._last_revision_info_cache
1676
 
                self._real_branch._last_revision_info_cache = cache
1677
1550
        else:
1678
1551
            raise errors.UnexpectedSmartServerResponse(response)
1679
1552
 
1680
 
    @needs_write_lock
1681
1553
    def generate_revision_history(self, revision_id, last_rev=None,
1682
1554
                                  other_branch=None):
1683
 
        medium = self._client._medium
1684
 
        if not medium._is_remote_before((1, 6)):
1685
 
            try:
1686
 
                self._set_last_revision_descendant(revision_id, other_branch,
1687
 
                    allow_diverged=True, allow_overwrite_descendant=True)
1688
 
                return
1689
 
            except errors.UnknownSmartMethod:
1690
 
                medium._remember_remote_is_before((1, 6))
1691
 
        self._clear_cached_state_of_remote_branch_only()
1692
1555
        self._ensure_real()
1693
 
        self._real_branch.generate_revision_history(
 
1556
        return self._real_branch.generate_revision_history(
1694
1557
            revision_id, last_rev=last_rev, other_branch=other_branch)
1695
1558
 
1696
1559
    @property
1702
1565
        self._ensure_real()
1703
1566
        return self._real_branch.set_push_location(location)
1704
1567
 
1705
 
    @needs_write_lock
1706
1568
    def update_revisions(self, other, stop_revision=None, overwrite=False,
1707
1569
                         graph=None):
1708
 
        """See Branch.update_revisions."""
1709
 
        other.lock_read()
1710
 
        try:
1711
 
            if stop_revision is None:
1712
 
                stop_revision = other.last_revision()
1713
 
                if revision.is_null(stop_revision):
1714
 
                    # if there are no commits, we're done.
1715
 
                    return
1716
 
            self.fetch(other, stop_revision)
1717
 
 
1718
 
            if overwrite:
1719
 
                # Just unconditionally set the new revision.  We don't care if
1720
 
                # the branches have diverged.
1721
 
                self._set_last_revision(stop_revision)
1722
 
            else:
1723
 
                medium = self._client._medium
1724
 
                if not medium._is_remote_before((1, 6)):
1725
 
                    try:
1726
 
                        self._set_last_revision_descendant(stop_revision, other)
1727
 
                        return
1728
 
                    except errors.UnknownSmartMethod:
1729
 
                        medium._remember_remote_is_before((1, 6))
1730
 
                # Fallback for pre-1.6 servers: check for divergence
1731
 
                # client-side, then do _set_last_revision.
1732
 
                last_rev = revision.ensure_null(self.last_revision())
1733
 
                if graph is None:
1734
 
                    graph = self.repository.get_graph()
1735
 
                if self._check_if_descendant_or_diverged(
1736
 
                        stop_revision, last_rev, graph, other):
1737
 
                    # stop_revision is a descendant of last_rev, but we aren't
1738
 
                    # overwriting, so we're done.
1739
 
                    return
1740
 
                self._set_last_revision(stop_revision)
1741
 
        finally:
1742
 
            other.unlock()
 
1570
        self._ensure_real()
 
1571
        return self._real_branch.update_revisions(
 
1572
            other, stop_revision=stop_revision, overwrite=overwrite,
 
1573
            graph=graph)
1743
1574
 
1744
1575
 
1745
1576
def _extract_tar(tar, to_dir):
1749
1580
    """
1750
1581
    for tarinfo in tar:
1751
1582
        tar.extract(tarinfo, to_dir)
1752
 
 
1753
 
 
1754
 
def _translate_error(err, **context):
1755
 
    """Translate an ErrorFromSmartServer into a more useful error.
1756
 
 
1757
 
    Possible context keys:
1758
 
      - branch
1759
 
      - repository
1760
 
      - bzrdir
1761
 
      - token
1762
 
      - other_branch
1763
 
 
1764
 
    If the error from the server doesn't match a known pattern, then
1765
 
    UnknownErrorFromSmartServer is raised.
1766
 
    """
1767
 
    def find(name):
1768
 
        try:
1769
 
            return context[name]
1770
 
        except KeyError, keyErr:
1771
 
            mutter('Missing key %r in context %r', keyErr.args[0], context)
1772
 
            raise err
1773
 
    if err.error_verb == 'NoSuchRevision':
1774
 
        raise NoSuchRevision(find('branch'), err.error_args[0])
1775
 
    elif err.error_verb == 'nosuchrevision':
1776
 
        raise NoSuchRevision(find('repository'), err.error_args[0])
1777
 
    elif err.error_tuple == ('nobranch',):
1778
 
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
1779
 
    elif err.error_verb == 'norepository':
1780
 
        raise errors.NoRepositoryPresent(find('bzrdir'))
1781
 
    elif err.error_verb == 'LockContention':
1782
 
        raise errors.LockContention('(remote lock)')
1783
 
    elif err.error_verb == 'UnlockableTransport':
1784
 
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
1785
 
    elif err.error_verb == 'LockFailed':
1786
 
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
1787
 
    elif err.error_verb == 'TokenMismatch':
1788
 
        raise errors.TokenMismatch(find('token'), '(remote token)')
1789
 
    elif err.error_verb == 'Diverged':
1790
 
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
1791
 
    elif err.error_verb == 'TipChangeRejected':
1792
 
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
1793
 
    elif err.error_verb == 'UnstackableBranchFormat':
1794
 
        raise errors.UnstackableBranchFormat(*err.error_args)
1795
 
    elif err.error_verb == 'UnstackableRepositoryFormat':
1796
 
        raise errors.UnstackableRepositoryFormat(*err.error_args)
1797
 
    elif err.error_verb == 'NotStacked':
1798
 
        raise errors.NotStacked(branch=find('branch'))
1799
 
    raise errors.UnknownErrorFromSmartServer(err)