/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: Jelmer Vernooij
  • Date: 2009-02-25 14:36:59 UTC
  • mfrom: (4048 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4049.
  • Revision ID: jelmer@samba.org-20090225143659-vx6cbqtmyicuzfyf
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
from bzrlib import (
23
23
    branch,
 
24
    bzrdir,
24
25
    debug,
25
26
    errors,
26
27
    graph,
27
28
    lockdir,
 
29
    pack,
28
30
    repository,
29
31
    revision,
30
32
    symbol_versioning,
41
43
from bzrlib.smart import client, vfs
42
44
from bzrlib.revision import ensure_null, NULL_REVISION
43
45
from bzrlib.trace import mutter, note, warning
 
46
from bzrlib.util import bencode
 
47
from bzrlib.versionedfile import record_to_fulltext_bytes
44
48
 
45
49
 
46
50
class _RpcHelper(object):
51
55
            return self._client.call(method, *args)
52
56
        except errors.ErrorFromSmartServer, err:
53
57
            self._translate_error(err, **err_context)
54
 
        
 
58
 
55
59
    def _call_expecting_body(self, method, *args, **err_context):
56
60
        try:
57
61
            return self._client.call_expecting_body(method, *args)
58
62
        except errors.ErrorFromSmartServer, err:
59
63
            self._translate_error(err, **err_context)
60
 
        
 
64
 
61
65
    def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
62
66
                                             **err_context):
63
67
        try:
65
69
                method, args, body_bytes)
66
70
        except errors.ErrorFromSmartServer, err:
67
71
            self._translate_error(err, **err_context)
68
 
        
 
72
 
 
73
 
 
74
def response_tuple_to_repo_format(response):
 
75
    """Convert a response tuple describing a repository format to a format."""
 
76
    format = RemoteRepositoryFormat()
 
77
    format.rich_root_data = (response[0] == 'yes')
 
78
    format.supports_tree_reference = (response[1] == 'yes')
 
79
    format.supports_external_lookups = (response[2] == 'yes')
 
80
    format._network_name = response[3]
 
81
    return format
 
82
 
 
83
 
69
84
# Note: RemoteBzrDirFormat is in bzrdir.py
70
85
 
71
86
class RemoteBzrDir(BzrDir, _RpcHelper):
72
87
    """Control directory on a remote server, accessed via bzr:// or similar."""
73
88
 
74
 
    def __init__(self, transport, _client=None):
 
89
    def __init__(self, transport, format, _client=None):
75
90
        """Construct a RemoteBzrDir.
76
91
 
77
92
        :param _client: Private parameter for testing. Disables probing and the
78
93
            use of a real bzrdir.
79
94
        """
80
 
        BzrDir.__init__(self, transport, RemoteBzrDirFormat())
 
95
        BzrDir.__init__(self, transport, format)
81
96
        # this object holds a delegated bzrdir that uses file-level operations
82
97
        # to talk to the other side
83
98
        self._real_bzrdir = None
 
99
        # 1-shot cache for the call pattern 'create_branch; open_branch' - see
 
100
        # create_branch for details.
 
101
        self._next_open_branch_result = None
84
102
 
85
103
        if _client is None:
86
104
            medium = transport.get_smart_medium()
108
126
    def _translate_error(self, err, **context):
109
127
        _translate_error(err, bzrdir=self, **context)
110
128
 
 
129
    def break_lock(self):
 
130
        # Prevent aliasing problems in the next_open_branch_result cache.
 
131
        # See create_branch for rationale.
 
132
        self._next_open_branch_result = None
 
133
        return BzrDir.break_lock(self)
 
134
 
111
135
    def cloning_metadir(self, stacked=False):
112
136
        self._ensure_real()
113
137
        return self._real_bzrdir.cloning_metadir(stacked)
114
138
 
115
139
    def create_repository(self, shared=False):
116
 
        self._ensure_real()
117
 
        self._real_bzrdir.create_repository(shared=shared)
118
 
        return self.open_repository()
 
140
        # as per meta1 formats - just delegate to the format object which may
 
141
        # be parameterised.
 
142
        result = self._format.repository_format.initialize(self, shared)
 
143
        if not isinstance(result, RemoteRepository):
 
144
            return self.open_repository()
 
145
        else:
 
146
            return result
119
147
 
120
148
    def destroy_repository(self):
121
149
        """See BzrDir.destroy_repository"""
123
151
        self._real_bzrdir.destroy_repository()
124
152
 
125
153
    def create_branch(self):
126
 
        self._ensure_real()
127
 
        real_branch = self._real_bzrdir.create_branch()
128
 
        return RemoteBranch(self, self.find_repository(), real_branch)
 
154
        # as per meta1 formats - just delegate to the format object which may
 
155
        # be parameterised.
 
156
        real_branch = self._format.get_branch_format().initialize(self)
 
157
        if not isinstance(real_branch, RemoteBranch):
 
158
            result = RemoteBranch(self, self.find_repository(), real_branch)
 
159
        else:
 
160
            result = real_branch
 
161
        # BzrDir.clone_on_transport() uses the result of create_branch but does
 
162
        # not return it to its callers; we save approximately 8% of our round
 
163
        # trips by handing the branch we created back to the first caller to
 
164
        # open_branch rather than probing anew. Long term we need a API in
 
165
        # bzrdir that doesn't discard result objects (like result_branch).
 
166
        # RBC 20090225
 
167
        self._next_open_branch_result = result
 
168
        return result
129
169
 
130
170
    def destroy_branch(self):
131
171
        """See BzrDir.destroy_branch"""
132
172
        self._ensure_real()
133
173
        self._real_bzrdir.destroy_branch()
 
174
        self._next_open_branch_result = None
134
175
 
135
176
    def create_workingtree(self, revision_id=None, from_branch=None):
136
177
        raise errors.NotLocalUrl(self.transport.base)
164
205
    def open_branch(self, _unsupported=False):
165
206
        if _unsupported:
166
207
            raise NotImplementedError('unsupported flag support not implemented yet.')
 
208
        if self._next_open_branch_result is not None:
 
209
            # See create_branch for details.
 
210
            result = self._next_open_branch_result
 
211
            self._next_open_branch_result = None
 
212
            return result
167
213
        reference_url = self.get_branch_reference()
168
214
        if reference_url is None:
169
215
            # branch at this location.
172
218
            # a branch reference, use the existing BranchReference logic.
173
219
            format = BranchReferenceFormat()
174
220
            return format.open(self, _found=True, location=reference_url)
175
 
                
 
221
 
176
222
    def open_repository(self):
177
223
        path = self._path_for_remote_call(self._client)
178
224
        verb = 'BzrDir.find_repositoryV2'
197
243
            format.supports_external_lookups = (response[4] == 'yes')
198
244
            # Used to support creating a real format instance when needed.
199
245
            format._creating_bzrdir = self
200
 
            return RemoteRepository(self, format)
 
246
            remote_repo = RemoteRepository(self, format)
 
247
            format._creating_repo = remote_repo
 
248
            return remote_repo
201
249
        else:
202
250
            raise errors.NoRepositoryPresent(self)
203
251
 
257
305
    the attributes rich_root_data and supports_tree_reference are set
258
306
    on a per instance basis, and are not set (and should not be) at
259
307
    the class level.
 
308
 
 
309
    :ivar _custom_format: If set, a specific concrete repository format that
 
310
        will be used when initializing a repository with this
 
311
        RemoteRepositoryFormat.
 
312
    :ivar _creating_repo: If set, the repository object that this
 
313
        RemoteRepositoryFormat was created for: it can be called into
 
314
        to obtain data like the network name.
260
315
    """
261
316
 
262
317
    _matchingbzrdir = RemoteBzrDirFormat()
263
318
 
264
 
    def initialize(self, a_bzrdir, shared=False):
265
 
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
319
    def __init__(self):
 
320
        repository.RepositoryFormat.__init__(self)
 
321
        self._custom_format = None
 
322
        self._network_name = None
 
323
        self._creating_bzrdir = None
 
324
 
 
325
    def _vfs_initialize(self, a_bzrdir, shared):
 
326
        """Helper for common code in initialize."""
 
327
        if self._custom_format:
 
328
            # Custom format requested
 
329
            result = self._custom_format.initialize(a_bzrdir, shared=shared)
 
330
        elif self._creating_bzrdir is not None:
 
331
            # Use the format that the repository we were created to back
 
332
            # has.
266
333
            prior_repo = self._creating_bzrdir.open_repository()
267
334
            prior_repo._ensure_real()
268
 
            return prior_repo._real_repository._format.initialize(
 
335
            result = prior_repo._real_repository._format.initialize(
269
336
                a_bzrdir, shared=shared)
270
 
        return a_bzrdir.create_repository(shared=shared)
271
 
    
 
337
        else:
 
338
            # assume that a_bzr is a RemoteBzrDir but the smart server didn't
 
339
            # support remote initialization.
 
340
            # We delegate to a real object at this point (as RemoteBzrDir
 
341
            # delegate to the repository format which would lead to infinite
 
342
            # recursion if we just called a_bzrdir.create_repository.
 
343
            a_bzrdir._ensure_real()
 
344
            result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
 
345
        if not isinstance(result, RemoteRepository):
 
346
            return self.open(a_bzrdir)
 
347
        else:
 
348
            return result
 
349
 
 
350
    def initialize(self, a_bzrdir, shared=False):
 
351
        # Being asked to create on a non RemoteBzrDir:
 
352
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
353
            return self._vfs_initialize(a_bzrdir, shared)
 
354
        medium = a_bzrdir._client._medium
 
355
        if medium._is_remote_before((1, 13)):
 
356
            return self._vfs_initialize(a_bzrdir, shared)
 
357
        # Creating on a remote bzr dir.
 
358
        # 1) get the network name to use.
 
359
        if self._custom_format:
 
360
            network_name = self._custom_format.network_name()
 
361
        else:
 
362
            # Select the current bzrlib default and ask for that.
 
363
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
364
            reference_format = reference_bzrdir_format.repository_format
 
365
            network_name = reference_format.network_name()
 
366
        # 2) try direct creation via RPC
 
367
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
368
        verb = 'BzrDir.create_repository'
 
369
        if shared:
 
370
            shared_str = 'True'
 
371
        else:
 
372
            shared_str = 'False'
 
373
        try:
 
374
            response = a_bzrdir._call(verb, path, network_name, shared_str)
 
375
        except errors.UnknownSmartMethod:
 
376
            # Fallback - use vfs methods
 
377
            return self._vfs_initialize(a_bzrdir, shared)
 
378
        else:
 
379
            # Turn the response into a RemoteRepository object.
 
380
            format = response_tuple_to_repo_format(response[1:])
 
381
            # Used to support creating a real format instance when needed.
 
382
            format._creating_bzrdir = a_bzrdir
 
383
            remote_repo = RemoteRepository(a_bzrdir, format)
 
384
            format._creating_repo = remote_repo
 
385
            return remote_repo
 
386
 
272
387
    def open(self, a_bzrdir):
273
388
        if not isinstance(a_bzrdir, RemoteBzrDir):
274
389
            raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
289
404
            raise errors.BadConversionTarget(
290
405
                'Does not support nested trees', target_format)
291
406
 
 
407
    def network_name(self):
 
408
        if self._network_name:
 
409
            return self._network_name
 
410
        self._creating_repo._ensure_real()
 
411
        return self._creating_repo._real_repository._format.network_name()
 
412
 
 
413
    @property
 
414
    def _serializer(self):
 
415
        if self._custom_format is not None:
 
416
            return self._custom_format._serializer
 
417
        elif self._network_name is not None:
 
418
            self._custom_format = repository.network_format_registry.get(
 
419
                self._network_name)
 
420
            return self._custom_format._serializer
 
421
        else:
 
422
            # We should only be getting asked for the serializer for
 
423
            # RemoteRepositoryFormat objects when the RemoteRepositoryFormat object
 
424
            # is a concrete instance for a RemoteRepository. In this case we know
 
425
            # the creating_repo and can use it to supply the serializer.
 
426
            self._creating_repo._ensure_real()
 
427
            return self._creating_repo._real_repository._format._serializer
 
428
 
292
429
 
293
430
class RemoteRepository(_RpcHelper):
294
431
    """Repository accessed over rpc.
299
436
 
300
437
    def __init__(self, remote_bzrdir, format, real_repository=None, _client=None):
301
438
        """Create a RemoteRepository instance.
302
 
        
 
439
 
303
440
        :param remote_bzrdir: The bzrdir hosting this repository.
304
441
        :param format: The RemoteFormat object to use.
305
442
        :param real_repository: If not None, a local implementation of the
344
481
 
345
482
    def abort_write_group(self, suppress_errors=False):
346
483
        """Complete a write group on the decorated repository.
347
 
        
 
484
 
348
485
        Smart methods peform operations in a single step so this api
349
486
        is not really applicable except as a compatibility thunk
350
487
        for older plugins that don't use e.g. the CommitBuilder
358
495
 
359
496
    def commit_write_group(self):
360
497
        """Complete a write group on the decorated repository.
361
 
        
 
498
 
362
499
        Smart methods peform operations in a single step so this api
363
500
        is not really applicable except as a compatibility thunk
364
501
        for older plugins that don't use e.g. the CommitBuilder
367
504
        self._ensure_real()
368
505
        return self._real_repository.commit_write_group()
369
506
 
 
507
    def resume_write_group(self, tokens):
 
508
        self._ensure_real()
 
509
        return self._real_repository.resume_write_group(tokens)
 
510
 
 
511
    def suspend_write_group(self):
 
512
        self._ensure_real()
 
513
        return self._real_repository.suspend_write_group()
 
514
 
370
515
    def _ensure_real(self):
371
516
        """Ensure that there is a _real_repository set.
372
517
 
432
577
        for line in lines:
433
578
            d = tuple(line.split())
434
579
            revision_graph[d[0]] = d[1:]
435
 
            
 
580
 
436
581
        return revision_graph
437
582
 
 
583
    def _get_sink(self):
 
584
        """See Repository._get_sink()."""
 
585
        return RemoteStreamSink(self)
 
586
 
438
587
    def has_revision(self, revision_id):
439
588
        """See Repository.has_revision()."""
440
589
        if revision_id == NULL_REVISION:
619
768
 
620
769
    def start_write_group(self):
621
770
        """Start a write group on the decorated repository.
622
 
        
 
771
 
623
772
        Smart methods peform operations in a single step so this api
624
773
        is not really applicable except as a compatibility thunk
625
774
        for older plugins that don't use e.g. the CommitBuilder
642
791
            raise errors.UnexpectedSmartServerResponse(response)
643
792
 
644
793
    def unlock(self):
 
794
        if not self._lock_count:
 
795
            raise errors.LockNotHeld(self)
645
796
        self._lock_count -= 1
646
797
        if self._lock_count > 0:
647
798
            return
674
825
 
675
826
    def _get_tarball(self, compression):
676
827
        """Return a TemporaryFile containing a repository tarball.
677
 
        
 
828
 
678
829
        Returns None if the server does not support sending tarballs.
679
830
        """
680
831
        import tempfile
726
877
 
727
878
    def add_fallback_repository(self, repository):
728
879
        """Add a repository to use for looking up data not held locally.
729
 
        
 
880
 
730
881
        :param repository: A repository.
731
882
        """
732
883
        # XXX: At the moment the RemoteRepository will allow fallbacks
737
888
        #
738
889
        # We need to accumulate additional repositories here, to pass them in
739
890
        # on various RPC's.
 
891
        #
740
892
        self._fallback_repositories.append(repository)
741
 
        # They are also seen by the fallback repository.  If it doesn't exist
742
 
        # yet they'll be added then.  This implicitly copies them.
743
 
        self._ensure_real()
 
893
        # If self._real_repository was parameterised already (e.g. because a
 
894
        # _real_branch had its get_stacked_on_url method called), then the
 
895
        # repository to be added may already be in the _real_repositories list.
 
896
        if self._real_repository is not None:
 
897
            if repository not in self._real_repository._fallback_repositories:
 
898
                self._real_repository.add_fallback_repository(repository)
 
899
        else:
 
900
            # They are also seen by the fallback repository.  If it doesn't
 
901
            # exist yet they'll be added then.  This implicitly copies them.
 
902
            self._ensure_real()
744
903
 
745
904
    def add_inventory(self, revid, inv, parents):
746
905
        self._ensure_real()
801
960
    @needs_read_lock
802
961
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
803
962
        """Return the revision ids that other has that this does not.
804
 
        
 
963
 
805
964
        These are returned in topological order.
806
965
 
807
966
        revision_id: only return revision ids included by revision_id.
842
1001
        self._ensure_real()
843
1002
        return self._real_repository._get_versioned_file_checker(
844
1003
            revisions, revision_versions_cache)
845
 
        
 
1004
 
846
1005
    def iter_files_bytes(self, desired_files):
847
1006
        """See Repository.iter_file_bytes.
848
1007
        """
1011
1170
    def reconcile(self, other=None, thorough=False):
1012
1171
        self._ensure_real()
1013
1172
        return self._real_repository.reconcile(other=other, thorough=thorough)
1014
 
        
 
1173
 
1015
1174
    def all_revision_ids(self):
1016
1175
        self._ensure_real()
1017
1176
        return self._real_repository.all_revision_ids()
1018
 
    
 
1177
 
1019
1178
    @needs_read_lock
1020
1179
    def get_deltas_for_revisions(self, revisions):
1021
1180
        self._ensure_real()
1106
1265
        return self._real_repository.revisions
1107
1266
 
1108
1267
    def set_make_working_trees(self, new_value):
1109
 
        self._ensure_real()
1110
 
        self._real_repository.set_make_working_trees(new_value)
 
1268
        if new_value:
 
1269
            new_value_str = "True"
 
1270
        else:
 
1271
            new_value_str = "False"
 
1272
        path = self.bzrdir._path_for_remote_call(self._client)
 
1273
        try:
 
1274
            response = self._call(
 
1275
                'Repository.set_make_working_trees', path, new_value_str)
 
1276
        except errors.UnknownSmartMethod:
 
1277
            self._ensure_real()
 
1278
            self._real_repository.set_make_working_trees(new_value)
 
1279
        else:
 
1280
            if response[0] != 'ok':
 
1281
                raise errors.UnexpectedSmartServerResponse(response)
1111
1282
 
1112
1283
    @property
1113
1284
    def signatures(self):
1220
1391
            raise errors.UnexpectedSmartServerResponse(response)
1221
1392
 
1222
1393
 
 
1394
class RemoteStreamSink(repository.StreamSink):
 
1395
 
 
1396
    def __init__(self, target_repo):
 
1397
        repository.StreamSink.__init__(self, target_repo)
 
1398
 
 
1399
    def _insert_real(self, stream, src_format, resume_tokens):
 
1400
        self.target_repo._ensure_real()
 
1401
        sink = self.target_repo._real_repository._get_sink()
 
1402
        result = sink.insert_stream(stream, src_format, resume_tokens)
 
1403
        if not result:
 
1404
            self.target_repo.autopack()
 
1405
        return result
 
1406
 
 
1407
    def insert_stream(self, stream, src_format, resume_tokens):
 
1408
        repo = self.target_repo
 
1409
        client = repo._client
 
1410
        medium = client._medium
 
1411
        if medium._is_remote_before((1, 13)):
 
1412
            # No possible way this can work.
 
1413
            return self._insert_real(stream, src_format, resume_tokens)
 
1414
        path = repo.bzrdir._path_for_remote_call(client)
 
1415
        if not resume_tokens:
 
1416
            # XXX: Ugly but important for correctness, *will* be fixed during
 
1417
            # 1.13 cycle. Pushing a stream that is interrupted results in a
 
1418
            # fallback to the _real_repositories sink *with a partial stream*.
 
1419
            # Thats bad because we insert less data than bzr expected. To avoid
 
1420
            # this we do a trial push to make sure the verb is accessible, and
 
1421
            # do not fallback when actually pushing the stream. A cleanup patch
 
1422
            # is going to look at rewinding/restarting the stream/partial
 
1423
            # buffering etc.
 
1424
            byte_stream = self._stream_to_byte_stream([], src_format)
 
1425
            try:
 
1426
                response = client.call_with_body_stream(
 
1427
                    ('Repository.insert_stream', path, ''), byte_stream)
 
1428
            except errors.UnknownSmartMethod:
 
1429
                medium._remember_remote_is_before((1,13))
 
1430
                return self._insert_real(stream, src_format, resume_tokens)
 
1431
        byte_stream = self._stream_to_byte_stream(stream, src_format)
 
1432
        resume_tokens = ' '.join(resume_tokens)
 
1433
        response = client.call_with_body_stream(
 
1434
            ('Repository.insert_stream', path, resume_tokens), byte_stream)
 
1435
        if response[0][0] not in ('ok', 'missing-basis'):
 
1436
            raise errors.UnexpectedSmartServerResponse(response)
 
1437
        if response[0][0] == 'missing-basis':
 
1438
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
 
1439
            resume_tokens = tokens
 
1440
            return resume_tokens, missing_keys
 
1441
        else:
 
1442
            if self.target_repo._real_repository is not None:
 
1443
                collection = getattr(self.target_repo._real_repository,
 
1444
                    '_pack_collection', None)
 
1445
                if collection is not None:
 
1446
                    collection.reload_pack_names()
 
1447
            return [], set()
 
1448
 
 
1449
    def _stream_to_byte_stream(self, stream, src_format):
 
1450
        bytes = []
 
1451
        pack_writer = pack.ContainerWriter(bytes.append)
 
1452
        pack_writer.begin()
 
1453
        pack_writer.add_bytes_record(src_format.network_name(), '')
 
1454
        adapters = {}
 
1455
        def get_adapter(adapter_key):
 
1456
            try:
 
1457
                return adapters[adapter_key]
 
1458
            except KeyError:
 
1459
                adapter_factory = adapter_registry.get(adapter_key)
 
1460
                adapter = adapter_factory(self)
 
1461
                adapters[adapter_key] = adapter
 
1462
                return adapter
 
1463
        for substream_type, substream in stream:
 
1464
            for record in substream:
 
1465
                if record.storage_kind in ('chunked', 'fulltext'):
 
1466
                    serialised = record_to_fulltext_bytes(record)
 
1467
                else:
 
1468
                    serialised = record.get_bytes_as(record.storage_kind)
 
1469
                pack_writer.add_bytes_record(serialised, [(substream_type,)])
 
1470
                for b in bytes:
 
1471
                    yield b
 
1472
                del bytes[:]
 
1473
        pack_writer.end()
 
1474
        for b in bytes:
 
1475
            yield b
 
1476
 
 
1477
 
1223
1478
class RemoteBranchLockableFiles(LockableFiles):
1224
1479
    """A 'LockableFiles' implementation that talks to a smart server.
1225
 
    
 
1480
 
1226
1481
    This is not a public interface class.
1227
1482
    """
1228
1483
 
1246
1501
        super(RemoteBranchFormat, self).__init__()
1247
1502
        self._matchingbzrdir = RemoteBzrDirFormat()
1248
1503
        self._matchingbzrdir.set_branch_format(self)
 
1504
        self._custom_format = None
1249
1505
 
1250
1506
    def __eq__(self, other):
1251
 
        return (isinstance(other, RemoteBranchFormat) and 
 
1507
        return (isinstance(other, RemoteBranchFormat) and
1252
1508
            self.__dict__ == other.__dict__)
1253
1509
 
1254
1510
    def get_format_description(self):
1255
1511
        return 'Remote BZR Branch'
1256
1512
 
1257
 
    def get_format_string(self):
1258
 
        return 'Remote BZR Branch'
 
1513
    def network_name(self):
 
1514
        return self._network_name
1259
1515
 
1260
1516
    def open(self, a_bzrdir):
1261
1517
        return a_bzrdir.open_branch()
1262
1518
 
 
1519
    def _vfs_initialize(self, a_bzrdir):
 
1520
        # Initialisation when using a local bzrdir object, or a non-vfs init
 
1521
        # method is not available on the server.
 
1522
        # self._custom_format is always set - the start of initialize ensures
 
1523
        # that.
 
1524
        if isinstance(a_bzrdir, RemoteBzrDir):
 
1525
            a_bzrdir._ensure_real()
 
1526
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
 
1527
        else:
 
1528
            # We assume the bzrdir is parameterised; it may not be.
 
1529
            result = self._custom_format.initialize(a_bzrdir)
 
1530
        if (isinstance(a_bzrdir, RemoteBzrDir) and
 
1531
            not isinstance(result, RemoteBranch)):
 
1532
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
 
1533
        return result
 
1534
 
1263
1535
    def initialize(self, a_bzrdir):
1264
 
        return a_bzrdir.create_branch()
 
1536
        # 1) get the network name to use.
 
1537
        if self._custom_format:
 
1538
            network_name = self._custom_format.network_name()
 
1539
        else:
 
1540
            # Select the current bzrlib default and ask for that.
 
1541
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
1542
            reference_format = reference_bzrdir_format.get_branch_format()
 
1543
            self._custom_format = reference_format
 
1544
            network_name = reference_format.network_name()
 
1545
        # Being asked to create on a non RemoteBzrDir:
 
1546
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
1547
            return self._vfs_initialize(a_bzrdir)
 
1548
        medium = a_bzrdir._client._medium
 
1549
        if medium._is_remote_before((1, 13)):
 
1550
            return self._vfs_initialize(a_bzrdir)
 
1551
        # Creating on a remote bzr dir.
 
1552
        # 2) try direct creation via RPC
 
1553
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
1554
        verb = 'BzrDir.create_branch'
 
1555
        try:
 
1556
            response = a_bzrdir._call(verb, path, network_name)
 
1557
        except errors.UnknownSmartMethod:
 
1558
            # Fallback - use vfs methods
 
1559
            return self._vfs_initialize(a_bzrdir)
 
1560
        if response[0] != 'ok':
 
1561
            raise errors.UnexpectedSmartServerResponse(response)
 
1562
        # Turn the response into a RemoteRepository object.
 
1563
        format = RemoteBranchFormat()
 
1564
        format._network_name = response[1]
 
1565
        repo_format = response_tuple_to_repo_format(response[3:])
 
1566
        if response[2] == '':
 
1567
            repo_bzrdir = a_bzrdir
 
1568
        else:
 
1569
            repo_bzrdir = RemoteBzrDir(
 
1570
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
 
1571
                a_bzrdir._client)
 
1572
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
1573
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
 
1574
            format=format, setup_stacking=False)
 
1575
        # XXX: We know this is a new branch, so it must have revno 0, revid
 
1576
        # NULL_REVISION. Creating the branch locked would make this be unable
 
1577
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
 
1578
        remote_branch._last_revision_info_cache = 0, NULL_REVISION
 
1579
        return remote_branch
1265
1580
 
1266
1581
    def supports_tags(self):
1267
1582
        # Remote branches might support tags, but we won't know until we
1276
1591
    """
1277
1592
 
1278
1593
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
1279
 
        _client=None):
 
1594
        _client=None, format=None, setup_stacking=True):
1280
1595
        """Create a RemoteBranch instance.
1281
1596
 
1282
1597
        :param real_branch: An optional local implementation of the branch
1283
1598
            format, usually accessing the data via the VFS.
1284
1599
        :param _client: Private parameter for testing.
 
1600
        :param format: A RemoteBranchFormat object, None to create one
 
1601
            automatically. If supplied it should have a network_name already
 
1602
            supplied.
 
1603
        :param setup_stacking: If True make an RPC call to determine the
 
1604
            stacked (or not) status of the branch. If False assume the branch
 
1605
            is not stacked.
1285
1606
        """
1286
1607
        # We intentionally don't call the parent class's __init__, because it
1287
1608
        # will try to assign to self.tags, which is a property in this subclass.
1310
1631
        else:
1311
1632
            self._real_branch = None
1312
1633
        # Fill out expected attributes of branch for bzrlib api users.
1313
 
        self._format = RemoteBranchFormat()
1314
1634
        self.base = self.bzrdir.root_transport.base
1315
1635
        self._control_files = None
1316
1636
        self._lock_mode = None
1318
1638
        self._repo_lock_token = None
1319
1639
        self._lock_count = 0
1320
1640
        self._leave_lock = False
 
1641
        # Setup a format: note that we cannot call _ensure_real until all the
 
1642
        # attributes above are set: This code cannot be moved higher up in this
 
1643
        # function.
 
1644
        if format is None:
 
1645
            self._format = RemoteBranchFormat()
 
1646
            if real_branch is not None:
 
1647
                self._format._network_name = \
 
1648
                    self._real_branch._format.network_name()
 
1649
            #else:
 
1650
            #    # XXX: Need to get this from BzrDir.open_branch's return value.
 
1651
            #    self._ensure_real()
 
1652
            #    self._format._network_name = \
 
1653
            #        self._real_branch._format.network_name()
 
1654
        else:
 
1655
            self._format = format
1321
1656
        # The base class init is not called, so we duplicate this:
1322
1657
        hooks = branch.Branch.hooks['open']
1323
1658
        for hook in hooks:
1324
1659
            hook(self)
1325
 
        self._setup_stacking()
 
1660
        if setup_stacking:
 
1661
            self._setup_stacking()
1326
1662
 
1327
1663
    def _setup_stacking(self):
1328
1664
        # configure stacking into the remote repository, by reading it from
1336
1672
        fallback_url = urlutils.join(self.base, fallback_url)
1337
1673
        transports = [self.bzrdir.root_transport]
1338
1674
        if self._real_branch is not None:
 
1675
            # The real repository is setup already:
1339
1676
            transports.append(self._real_branch._transport)
1340
 
        stacked_on = branch.Branch.open(fallback_url,
1341
 
                                        possible_transports=transports)
1342
 
        self.repository.add_fallback_repository(stacked_on.repository)
 
1677
            self.repository.add_fallback_repository(
 
1678
                self.repository._real_repository._fallback_repositories[0])
 
1679
        else:
 
1680
            stacked_on = branch.Branch.open(fallback_url,
 
1681
                                            possible_transports=transports)
 
1682
            self.repository.add_fallback_repository(stacked_on.repository)
1343
1683
 
1344
1684
    def _get_real_transport(self):
1345
1685
        # if we try vfs access, return the real branch's vfs transport
1398
1738
        too, in fact doing so might harm performance.
1399
1739
        """
1400
1740
        super(RemoteBranch, self)._clear_cached_state()
1401
 
        
 
1741
 
1402
1742
    @property
1403
1743
    def control_files(self):
1404
1744
        # Defer actually creating RemoteBranchLockableFiles until its needed,
1468
1808
            raise errors.UnexpectedSmartServerResponse(response)
1469
1809
        ok, branch_token, repo_token = response
1470
1810
        return branch_token, repo_token
1471
 
            
 
1811
 
1472
1812
    def lock_write(self, token=None):
1473
1813
        if not self._lock_mode:
1474
1814
            # Lock the branch and repo in one remote call.
1580
1920
 
1581
1921
    def _set_last_revision_descendant(self, revision_id, other_branch,
1582
1922
            allow_diverged=False, allow_overwrite_descendant=False):
 
1923
        # This performs additional work to meet the hook contract; while its
 
1924
        # undesirable, we have to synthesise the revno to call the hook, and
 
1925
        # not calling the hook is worse as it means changes can't be prevented.
 
1926
        # Having calculated this though, we can't just call into
 
1927
        # set_last_revision_info as a simple call, because there is a set_rh
 
1928
        # hook that some folk may still be using.
 
1929
        old_revno, old_revid = self.last_revision_info()
 
1930
        history = self._lefthand_history(revision_id)
 
1931
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
1583
1932
        err_context = {'other_branch': other_branch}
1584
1933
        response = self._call('Branch.set_last_revision_ex',
1585
1934
            self._remote_path(), self._lock_token, self._repo_lock_token,
1590
1939
            raise errors.UnexpectedSmartServerResponse(response)
1591
1940
        new_revno, new_revision_id = response[1:]
1592
1941
        self._last_revision_info_cache = new_revno, new_revision_id
 
1942
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1593
1943
        if self._real_branch is not None:
1594
1944
            cache = new_revno, new_revision_id
1595
1945
            self._real_branch._last_revision_info_cache = cache
1596
1946
 
1597
1947
    def _set_last_revision(self, revision_id):
 
1948
        old_revno, old_revid = self.last_revision_info()
 
1949
        # This performs additional work to meet the hook contract; while its
 
1950
        # undesirable, we have to synthesise the revno to call the hook, and
 
1951
        # not calling the hook is worse as it means changes can't be prevented.
 
1952
        # Having calculated this though, we can't just call into
 
1953
        # set_last_revision_info as a simple call, because there is a set_rh
 
1954
        # hook that some folk may still be using.
 
1955
        history = self._lefthand_history(revision_id)
 
1956
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
1598
1957
        self._clear_cached_state()
1599
1958
        response = self._call('Branch.set_last_revision',
1600
1959
            self._remote_path(), self._lock_token, self._repo_lock_token,
1601
1960
            revision_id)
1602
1961
        if response != ('ok',):
1603
1962
            raise errors.UnexpectedSmartServerResponse(response)
 
1963
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1604
1964
 
1605
1965
    @needs_write_lock
1606
1966
    def set_revision_history(self, rev_history):
1612
1972
        else:
1613
1973
            rev_id = rev_history[-1]
1614
1974
        self._set_last_revision(rev_id)
 
1975
        for hook in branch.Branch.hooks['set_rh']:
 
1976
            hook(self, rev_history)
1615
1977
        self._cache_revision_history(rev_history)
1616
1978
 
1617
1979
    def get_parent(self):
1618
1980
        self._ensure_real()
1619
1981
        return self._real_branch.get_parent()
1620
 
        
 
1982
 
 
1983
    def _get_parent_location(self):
 
1984
        # Used by tests, when checking normalisation of given vs stored paths.
 
1985
        self._ensure_real()
 
1986
        return self._real_branch._get_parent_location()
 
1987
 
1621
1988
    def set_parent(self, url):
1622
1989
        self._ensure_real()
1623
1990
        return self._real_branch.set_parent(url)
1624
 
        
 
1991
 
 
1992
    def _set_parent_location(self, url):
 
1993
        # Used by tests, to poke bad urls into branch configurations
 
1994
        if url is None:
 
1995
            self.set_parent(url)
 
1996
        else:
 
1997
            self._ensure_real()
 
1998
            return self._real_branch._set_parent_location(url)
 
1999
 
1625
2000
    def set_stacked_on_url(self, stacked_location):
1626
2001
        """Set the URL this branch is stacked against.
1627
2002
 
1633
2008
        self._ensure_real()
1634
2009
        return self._real_branch.set_stacked_on_url(stacked_location)
1635
2010
 
1636
 
    def sprout(self, to_bzrdir, revision_id=None):
1637
 
        branch_format = to_bzrdir._format._branch_format
1638
 
        if (branch_format is None or
1639
 
            isinstance(branch_format, RemoteBranchFormat)):
1640
 
            # The to_bzrdir specifies RemoteBranchFormat (or no format, which
1641
 
            # implies the same thing), but RemoteBranches can't be created at
1642
 
            # arbitrary URLs.  So create a branch in the same format as
1643
 
            # _real_branch instead.
1644
 
            # XXX: if to_bzrdir is a RemoteBzrDir, this should perhaps do
1645
 
            # to_bzrdir.create_branch to create a RemoteBranch after all...
1646
 
            self._ensure_real()
1647
 
            result = self._real_branch._format.initialize(to_bzrdir)
1648
 
            self.copy_content_into(result, revision_id=revision_id)
1649
 
            result.set_parent(self.bzrdir.root_transport.base)
1650
 
        else:
1651
 
            result = branch.Branch.sprout(
1652
 
                self, to_bzrdir, revision_id=revision_id)
1653
 
        return result
1654
 
 
1655
2011
    @needs_write_lock
1656
2012
    def pull(self, source, overwrite=False, stop_revision=None,
1657
2013
             **kwargs):
1678
2034
 
1679
2035
    @needs_write_lock
1680
2036
    def set_last_revision_info(self, revno, revision_id):
 
2037
        # XXX: These should be returned by the set_last_revision_info verb
 
2038
        old_revno, old_revid = self.last_revision_info()
 
2039
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
1681
2040
        revision_id = ensure_null(revision_id)
1682
2041
        try:
1683
2042
            response = self._call('Branch.set_last_revision_info',
1692
2051
        if response == ('ok',):
1693
2052
            self._clear_cached_state()
1694
2053
            self._last_revision_info_cache = revno, revision_id
 
2054
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1695
2055
            # Update the _real_branch's cache too.
1696
2056
            if self._real_branch is not None:
1697
2057
                cache = self._last_revision_info_cache
1704
2064
                                  other_branch=None):
1705
2065
        medium = self._client._medium
1706
2066
        if not medium._is_remote_before((1, 6)):
 
2067
            # Use a smart method for 1.6 and above servers
1707
2068
            try:
1708
2069
                self._set_last_revision_descendant(revision_id, other_branch,
1709
2070
                    allow_diverged=True, allow_overwrite_descendant=True)
1711
2072
            except errors.UnknownSmartMethod:
1712
2073
                medium._remember_remote_is_before((1, 6))
1713
2074
        self._clear_cached_state_of_remote_branch_only()
1714
 
        self._ensure_real()
1715
 
        self._real_branch.generate_revision_history(
1716
 
            revision_id, last_rev=last_rev, other_branch=other_branch)
 
2075
        self.set_revision_history(self._lefthand_history(revision_id,
 
2076
            last_rev=last_rev,other_branch=other_branch))
1717
2077
 
1718
2078
    @property
1719
2079
    def tags(self):
1724
2084
        self._ensure_real()
1725
2085
        return self._real_branch.set_push_location(location)
1726
2086
 
1727
 
    @needs_write_lock
1728
 
    def update_revisions(self, other, stop_revision=None, overwrite=False,
1729
 
                         graph=None):
1730
 
        """See Branch.update_revisions."""
1731
 
        other.lock_read()
1732
 
        try:
1733
 
            if stop_revision is None:
1734
 
                stop_revision = other.last_revision()
1735
 
                if revision.is_null(stop_revision):
1736
 
                    # if there are no commits, we're done.
1737
 
                    return
1738
 
            self.fetch(other, stop_revision)
1739
 
 
1740
 
            if overwrite:
1741
 
                # Just unconditionally set the new revision.  We don't care if
1742
 
                # the branches have diverged.
1743
 
                self._set_last_revision(stop_revision)
1744
 
            else:
1745
 
                medium = self._client._medium
1746
 
                if not medium._is_remote_before((1, 6)):
1747
 
                    try:
1748
 
                        self._set_last_revision_descendant(stop_revision, other)
1749
 
                        return
1750
 
                    except errors.UnknownSmartMethod:
1751
 
                        medium._remember_remote_is_before((1, 6))
1752
 
                # Fallback for pre-1.6 servers: check for divergence
1753
 
                # client-side, then do _set_last_revision.
1754
 
                last_rev = revision.ensure_null(self.last_revision())
1755
 
                if graph is None:
1756
 
                    graph = self.repository.get_graph()
1757
 
                if self._check_if_descendant_or_diverged(
1758
 
                        stop_revision, last_rev, graph, other):
1759
 
                    # stop_revision is a descendant of last_rev, but we aren't
1760
 
                    # overwriting, so we're done.
1761
 
                    return
1762
 
                self._set_last_revision(stop_revision)
1763
 
        finally:
1764
 
            other.unlock()
1765
 
 
1766
2087
 
1767
2088
def _extract_tar(tar, to_dir):
1768
2089
    """Extract all the contents of a tarfile object.