~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-01-03 18:09:01 UTC
  • mfrom: (3159.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20080103180901-w987y1ftqoh02qbm
(vila) Fix #179368 by keeping the current range hint on
        ShortReadvErrors

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
from cStringIO import StringIO
21
21
 
22
 
from bzrlib import branch, errors, lockdir, repository
 
22
from bzrlib import (
 
23
    branch,
 
24
    errors,
 
25
    lockdir,
 
26
    repository,
 
27
    revision,
 
28
)
23
29
from bzrlib.branch import Branch, BranchReferenceFormat
24
30
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
25
31
from bzrlib.config import BranchConfig, TreeConfig
26
32
from bzrlib.decorators import needs_read_lock, needs_write_lock
27
33
from bzrlib.errors import NoSuchRevision
28
34
from bzrlib.lockable_files import LockableFiles
29
 
from bzrlib.revision import NULL_REVISION
 
35
from bzrlib.pack import ContainerReader
30
36
from bzrlib.smart import client, vfs
 
37
from bzrlib.symbol_versioning import (
 
38
    deprecated_method,
 
39
    zero_ninetyone,
 
40
    )
31
41
from bzrlib.trace import note
32
42
 
33
43
# Note: RemoteBzrDirFormat is in bzrdir.py
47
57
        self._real_bzrdir = None
48
58
 
49
59
        if _client is None:
50
 
            self._medium = transport.get_smart_client()
51
 
            self._client = client._SmartClient(self._medium)
 
60
            self._shared_medium = transport.get_shared_medium()
 
61
            self._client = client._SmartClient(self._shared_medium)
52
62
        else:
53
63
            self._client = _client
54
 
            self._medium = None
 
64
            self._shared_medium = None
55
65
            return
56
66
 
57
67
        path = self._path_for_remote_call(self._client)
75
85
        self._real_bzrdir.create_repository(shared=shared)
76
86
        return self.open_repository()
77
87
 
 
88
    def destroy_repository(self):
 
89
        """See BzrDir.destroy_repository"""
 
90
        self._ensure_real()
 
91
        self._real_bzrdir.destroy_repository()
 
92
 
78
93
    def create_branch(self):
79
94
        self._ensure_real()
80
95
        real_branch = self._real_bzrdir.create_branch()
81
96
        return RemoteBranch(self, self.find_repository(), real_branch)
82
97
 
83
 
    def create_workingtree(self, revision_id=None):
 
98
    def destroy_branch(self):
 
99
        """See BzrDir.destroy_branch"""
 
100
        self._ensure_real()
 
101
        self._real_bzrdir.destroy_branch()
 
102
 
 
103
    def create_workingtree(self, revision_id=None, from_branch=None):
84
104
        raise errors.NotLocalUrl(self.transport.base)
85
105
 
86
106
    def find_branch_format(self):
105
125
        elif response == ('nobranch',):
106
126
            raise errors.NotBranchError(path=self.root_transport.base)
107
127
        else:
108
 
            assert False, 'unexpected response code %r' % (response,)
 
128
            raise errors.UnexpectedSmartServerResponse(response)
109
129
 
110
130
    def open_branch(self, _unsupported=False):
111
131
        assert _unsupported == False, 'unsupported flag support not implemented yet.'
177
197
    Instances of this repository are represented by RemoteRepository
178
198
    instances.
179
199
 
180
 
    The RemoteRepositoryFormat is parameterised during construction
 
200
    The RemoteRepositoryFormat is parameterized during construction
181
201
    to reflect the capabilities of the real, remote format. Specifically
182
202
    the attributes rich_root_data and supports_tree_reference are set
183
203
    on a per instance basis, and are not set (and should not be) at
235
255
            self._real_repository = None
236
256
        self.bzrdir = remote_bzrdir
237
257
        if _client is None:
238
 
            self._client = client._SmartClient(self.bzrdir._medium)
 
258
            self._client = client._SmartClient(self.bzrdir._shared_medium)
239
259
        else:
240
260
            self._client = _client
241
261
        self._format = format
243
263
        self._lock_token = None
244
264
        self._lock_count = 0
245
265
        self._leave_lock = False
 
266
        # For tests:
 
267
        # These depend on the actual remote format, so force them off for
 
268
        # maximum compatibility. XXX: In future these should depend on the
 
269
        # remote repository instance, but this is irrelevant until we perform
 
270
        # reconcile via an RPC call.
 
271
        self._reconcile_does_inventory_gc = False
 
272
        self._reconcile_fixes_text_parents = False
 
273
        self._reconcile_backsup_inventory = False
 
274
        self.base = self.bzrdir.transport.base
 
275
 
 
276
    def __str__(self):
 
277
        return "%s(%s)" % (self.__class__.__name__, self.base)
 
278
 
 
279
    __repr__ = __str__
 
280
 
 
281
    def abort_write_group(self):
 
282
        """Complete a write group on the decorated repository.
 
283
        
 
284
        Smart methods peform operations in a single step so this api
 
285
        is not really applicable except as a compatibility thunk
 
286
        for older plugins that don't use e.g. the CommitBuilder
 
287
        facility.
 
288
        """
 
289
        self._ensure_real()
 
290
        return self._real_repository.abort_write_group()
 
291
 
 
292
    def commit_write_group(self):
 
293
        """Complete a write group on the decorated repository.
 
294
        
 
295
        Smart methods peform operations in a single step so this api
 
296
        is not really applicable except as a compatibility thunk
 
297
        for older plugins that don't use e.g. the CommitBuilder
 
298
        facility.
 
299
        """
 
300
        self._ensure_real()
 
301
        return self._real_repository.commit_write_group()
246
302
 
247
303
    def _ensure_real(self):
248
304
        """Ensure that there is a _real_repository set.
254
310
            #self._real_repository = self.bzrdir._real_bzrdir.open_repository()
255
311
            self._set_real_repository(self.bzrdir._real_bzrdir.open_repository())
256
312
 
 
313
    def find_text_key_references(self):
 
314
        """Find the text key references within the repository.
 
315
 
 
316
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
 
317
        revision_ids. Each altered file-ids has the exact revision_ids that
 
318
        altered it listed explicitly.
 
319
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
 
320
            to whether they were referred to by the inventory of the
 
321
            revision_id that they contain. The inventory texts from all present
 
322
            revision ids are assessed to generate this report.
 
323
        """
 
324
        self._ensure_real()
 
325
        return self._real_repository.find_text_key_references()
 
326
 
 
327
    def _generate_text_key_index(self):
 
328
        """Generate a new text key index for the repository.
 
329
 
 
330
        This is an expensive function that will take considerable time to run.
 
331
 
 
332
        :return: A dict mapping (file_id, revision_id) tuples to a list of
 
333
            parents, also (file_id, revision_id) tuples.
 
334
        """
 
335
        self._ensure_real()
 
336
        return self._real_repository._generate_text_key_index()
 
337
 
257
338
    def get_revision_graph(self, revision_id=None):
258
339
        """See Repository.get_revision_graph()."""
259
340
        if revision_id is None:
260
341
            revision_id = ''
261
 
        elif revision_id == NULL_REVISION:
 
342
        elif revision.is_null(revision_id):
262
343
            return {}
263
344
 
264
345
        path = self.bzrdir._path_for_remote_call(self._client)
275
356
            lines = coded.split('\n')
276
357
            revision_graph = {}
277
358
            for line in lines:
278
 
                d = list(line.split())
 
359
                d = tuple(line.split())
279
360
                revision_graph[d[0]] = d[1:]
280
361
                
281
362
            return revision_graph
294
375
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
295
376
        return response[0] == 'yes'
296
377
 
 
378
    def has_same_location(self, other):
 
379
        return (self.__class__ == other.__class__ and
 
380
                self.bzrdir.transport.base == other.bzrdir.transport.base)
 
381
        
 
382
    def get_graph(self, other_repository=None):
 
383
        """Return the graph for this repository format"""
 
384
        self._ensure_real()
 
385
        return self._real_repository.get_graph(other_repository)
 
386
 
297
387
    def gather_stats(self, revid=None, committers=None):
298
388
        """See Repository.gather_stats()."""
299
389
        path = self.bzrdir._path_for_remote_call(self._client)
300
 
        if revid in (None, NULL_REVISION):
 
390
        # revid can be None to indicate no revisions, not just NULL_REVISION
 
391
        if revid is None or revision.is_null(revid):
301
392
            fmt_revid = ''
302
393
        else:
303
394
            fmt_revid = revid
324
415
 
325
416
        return result
326
417
 
 
418
    def find_branches(self, using=False):
 
419
        """See Repository.find_branches()."""
 
420
        # should be an API call to the server.
 
421
        self._ensure_real()
 
422
        return self._real_repository.find_branches(using=using)
 
423
 
327
424
    def get_physical_lock_status(self):
328
425
        """See Repository.get_physical_lock_status()."""
329
 
        return False
 
426
        # should be an API call to the server.
 
427
        self._ensure_real()
 
428
        return self._real_repository.get_physical_lock_status()
 
429
 
 
430
    def is_in_write_group(self):
 
431
        """Return True if there is an open write group.
 
432
 
 
433
        write groups are only applicable locally for the smart server..
 
434
        """
 
435
        if self._real_repository:
 
436
            return self._real_repository.is_in_write_group()
 
437
 
 
438
    def is_locked(self):
 
439
        return self._lock_count >= 1
330
440
 
331
441
    def is_shared(self):
332
442
        """See Repository.is_shared()."""
335
445
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
336
446
        return response[0] == 'yes'
337
447
 
 
448
    def is_write_locked(self):
 
449
        return self._lock_mode == 'w'
 
450
 
338
451
    def lock_read(self):
339
452
        # wrong eventually - want a local lock cache context
340
453
        if not self._lock_mode:
357
470
            raise errors.LockContention('(remote lock)')
358
471
        elif response[0] == 'UnlockableTransport':
359
472
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
 
473
        elif response[0] == 'LockFailed':
 
474
            raise errors.LockFailed(response[1], response[2])
360
475
        else:
361
 
            assert False, 'unexpected response code %s' % (response,)
 
476
            raise errors.UnexpectedSmartServerResponse(response)
362
477
 
363
478
    def lock_write(self, token=None):
364
479
        if not self._lock_mode:
365
480
            self._lock_token = self._remote_lock_write(token)
366
 
            assert self._lock_token, 'Remote server did not return a token!'
 
481
            # if self._lock_token is None, then this is something like packs or
 
482
            # svn where we don't get to lock the repo, or a weave style repository
 
483
            # where we cannot lock it over the wire and attempts to do so will
 
484
            # fail.
367
485
            if self._real_repository is not None:
368
486
                self._real_repository.lock_write(token=self._lock_token)
369
487
            if token is not None:
376
494
            raise errors.ReadOnlyError(self)
377
495
        else:
378
496
            self._lock_count += 1
379
 
        return self._lock_token
 
497
        return self._lock_token or None
380
498
 
381
499
    def leave_lock_in_place(self):
 
500
        if not self._lock_token:
 
501
            raise NotImplementedError(self.leave_lock_in_place)
382
502
        self._leave_lock = True
383
503
 
384
504
    def dont_leave_lock_in_place(self):
 
505
        if not self._lock_token:
 
506
            raise NotImplementedError(self.dont_leave_lock_in_place)
385
507
        self._leave_lock = False
386
508
 
387
509
    def _set_real_repository(self, repository):
399
521
        elif self._lock_mode == 'r':
400
522
            self._real_repository.lock_read()
401
523
 
 
524
    def start_write_group(self):
 
525
        """Start a write group on the decorated repository.
 
526
        
 
527
        Smart methods peform operations in a single step so this api
 
528
        is not really applicable except as a compatibility thunk
 
529
        for older plugins that don't use e.g. the CommitBuilder
 
530
        facility.
 
531
        """
 
532
        self._ensure_real()
 
533
        return self._real_repository.start_write_group()
 
534
 
402
535
    def _unlock(self, token):
403
536
        path = self.bzrdir._path_for_remote_call(self._client)
 
537
        if not token:
 
538
            # with no token the remote repository is not persistently locked.
 
539
            return
404
540
        response = self._client.call('Repository.unlock', path, token)
405
541
        if response == ('ok',):
406
542
            return
407
543
        elif response[0] == 'TokenMismatch':
408
544
            raise errors.TokenMismatch(token, '(remote token)')
409
545
        else:
410
 
            assert False, 'unexpected response code %s' % (response,)
 
546
            raise errors.UnexpectedSmartServerResponse(response)
411
547
 
412
548
    def unlock(self):
413
549
        self._lock_count -= 1
414
 
        if not self._lock_count:
415
 
            mode = self._lock_mode
416
 
            self._lock_mode = None
 
550
        if self._lock_count > 0:
 
551
            return
 
552
        old_mode = self._lock_mode
 
553
        self._lock_mode = None
 
554
        try:
 
555
            # The real repository is responsible at present for raising an
 
556
            # exception if it's in an unfinished write group.  However, it
 
557
            # normally will *not* actually remove the lock from disk - that's
 
558
            # done by the server on receiving the Repository.unlock call.
 
559
            # This is just to let the _real_repository stay up to date.
417
560
            if self._real_repository is not None:
418
561
                self._real_repository.unlock()
419
 
            if mode != 'w':
 
562
        finally:
 
563
            # The rpc-level lock should be released even if there was a
 
564
            # problem releasing the vfs-based lock.
 
565
            if old_mode == 'w':
420
566
                # Only write-locked repositories need to make a remote method
421
567
                # call to perfom the unlock.
422
 
                return
423
 
            assert self._lock_token, 'Locked, but no token!'
424
 
            token = self._lock_token
425
 
            self._lock_token = None
426
 
            if not self._leave_lock:
427
 
                self._unlock(token)
 
568
                old_token = self._lock_token
 
569
                self._lock_token = None
 
570
                if not self._leave_lock:
 
571
                    self._unlock(old_token)
428
572
 
429
573
    def break_lock(self):
430
574
        # should hand off to the network
432
576
        return self._real_repository.break_lock()
433
577
 
434
578
    def _get_tarball(self, compression):
435
 
        """Return a TemporaryFile containing a repository tarball"""
 
579
        """Return a TemporaryFile containing a repository tarball.
 
580
        
 
581
        Returns None if the server does not support sending tarballs.
 
582
        """
436
583
        import tempfile
437
584
        path = self.bzrdir._path_for_remote_call(self._client)
438
585
        response, protocol = self._client.call_expecting_body(
439
586
            'Repository.tarball', path, compression)
440
 
        assert response[0] in ('ok', 'failure'), \
441
 
            'unexpected response code %s' % (response,)
442
587
        if response[0] == 'ok':
443
588
            # Extract the tarball and return it
444
589
            t = tempfile.NamedTemporaryFile()
446
591
            t.write(protocol.read_body_bytes())
447
592
            t.seek(0)
448
593
            return t
449
 
        else:
450
 
            raise errors.SmartServerError(error_code=response)
 
594
        if (response == ('error', "Generic bzr smart protocol error: "
 
595
                "bad request 'Repository.tarball'") or
 
596
              response == ('error', "Generic bzr smart protocol error: "
 
597
                "bad request u'Repository.tarball'")):
 
598
            protocol.cancel_read_body()
 
599
            return None
 
600
        raise errors.UnexpectedSmartServerResponse(response)
451
601
 
452
602
    def sprout(self, to_bzrdir, revision_id=None):
453
603
        # TODO: Option to control what format is created?
454
 
        to_repo = to_bzrdir.create_repository()
455
 
        self._copy_repository_tarball(to_repo, revision_id)
456
 
        return to_repo
 
604
        self._ensure_real()
 
605
        dest_repo = self._real_repository._format.initialize(to_bzrdir,
 
606
                                                             shared=False)
 
607
        dest_repo.fetch(self, revision_id=revision_id)
 
608
        return dest_repo
457
609
 
458
610
    ### These methods are just thin shims to the VFS object for now.
459
611
 
461
613
        self._ensure_real()
462
614
        return self._real_repository.revision_tree(revision_id)
463
615
 
 
616
    def get_serializer_format(self):
 
617
        self._ensure_real()
 
618
        return self._real_repository.get_serializer_format()
 
619
 
464
620
    def get_commit_builder(self, branch, parents, config, timestamp=None,
465
621
                           timezone=None, committer=None, revprops=None,
466
622
                           revision_id=None):
470
626
        builder = self._real_repository.get_commit_builder(branch, parents,
471
627
                config, timestamp=timestamp, timezone=timezone,
472
628
                committer=committer, revprops=revprops, revision_id=revision_id)
473
 
        # Make the builder use this RemoteRepository rather than the real one.
474
 
        builder.repository = self
475
629
        return builder
476
630
 
477
631
    @needs_write_lock
514
668
        return False
515
669
 
516
670
    def fetch(self, source, revision_id=None, pb=None):
 
671
        if self.has_same_location(source):
 
672
            # check that last_revision is in 'from' and then return a
 
673
            # no-operation.
 
674
            if (revision_id is not None and
 
675
                not revision.is_null(revision_id)):
 
676
                self.get_revision(revision_id)
 
677
            return 0, []
517
678
        self._ensure_real()
518
679
        return self._real_repository.fetch(
519
680
            source, revision_id=revision_id, pb=pb)
520
681
 
 
682
    def create_bundle(self, target, base, fileobj, format=None):
 
683
        self._ensure_real()
 
684
        self._real_repository.create_bundle(target, base, fileobj, format)
 
685
 
521
686
    @property
522
687
    def control_weaves(self):
523
688
        self._ensure_real()
524
689
        return self._real_repository.control_weaves
525
690
 
526
691
    @needs_read_lock
527
 
    def get_ancestry(self, revision_id):
 
692
    def get_ancestry(self, revision_id, topo_sorted=True):
528
693
        self._ensure_real()
529
 
        return self._real_repository.get_ancestry(revision_id)
 
694
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
530
695
 
531
696
    @needs_read_lock
532
697
    def get_inventory_weave(self):
537
702
        self._ensure_real()
538
703
        return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
539
704
 
 
705
    def _get_versioned_file_checker(self, revisions, revision_versions_cache):
 
706
        self._ensure_real()
 
707
        return self._real_repository._get_versioned_file_checker(
 
708
            revisions, revision_versions_cache)
 
709
        
 
710
    def iter_files_bytes(self, desired_files):
 
711
        """See Repository.iter_file_bytes.
 
712
        """
 
713
        self._ensure_real()
 
714
        return self._real_repository.iter_files_bytes(desired_files)
 
715
 
540
716
    @needs_read_lock
541
717
    def get_signature_text(self, revision_id):
542
718
        self._ensure_real()
586
762
        return self._real_repository.get_revision_reconcile(revision_id)
587
763
 
588
764
    @needs_read_lock
589
 
    def check(self, revision_ids):
 
765
    def check(self, revision_ids=None):
590
766
        self._ensure_real()
591
 
        return self._real_repository.check(revision_ids)
 
767
        return self._real_repository.check(revision_ids=revision_ids)
592
768
 
593
769
    def copy_content_into(self, destination, revision_id=None):
594
770
        self._ensure_real()
595
771
        return self._real_repository.copy_content_into(
596
772
            destination, revision_id=revision_id)
597
773
 
598
 
    def _copy_repository_tarball(self, destination, revision_id=None):
 
774
    def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
599
775
        # get a tarball of the remote repository, and copy from that into the
600
776
        # destination
601
777
        from bzrlib import osutils
605
781
        # TODO: Maybe a progress bar while streaming the tarball?
606
782
        note("Copying repository content as tarball...")
607
783
        tar_file = self._get_tarball('bz2')
 
784
        if tar_file is None:
 
785
            return None
 
786
        destination = to_bzrdir.create_repository()
608
787
        try:
609
788
            tar = tarfile.open('repository', fileobj=tar_file,
610
789
                mode='r|bz2')
618
797
                osutils.rmtree(tmpdir)
619
798
        finally:
620
799
            tar_file.close()
621
 
        # TODO: if the server doesn't support this operation, maybe do it the
622
 
        # slow way using the _real_repository?
623
 
        #
 
800
        return destination
624
801
        # TODO: Suggestion from john: using external tar is much faster than
625
802
        # python's tarfile library, but it may not work on windows.
626
803
 
 
804
    @needs_write_lock
 
805
    def pack(self):
 
806
        """Compress the data within the repository.
 
807
 
 
808
        This is not currently implemented within the smart server.
 
809
        """
 
810
        self._ensure_real()
 
811
        return self._real_repository.pack()
 
812
 
627
813
    def set_make_working_trees(self, new_value):
628
814
        raise NotImplementedError(self.set_make_working_trees)
629
815
 
655
841
        return self._real_repository.store_revision_signature(
656
842
            gpg_strategy, plaintext, revision_id)
657
843
 
 
844
    def add_signature_text(self, revision_id, signature):
 
845
        self._ensure_real()
 
846
        return self._real_repository.add_signature_text(revision_id, signature)
 
847
 
658
848
    def has_signature_for_revision_id(self, revision_id):
659
849
        self._ensure_real()
660
850
        return self._real_repository.has_signature_for_revision_id(revision_id)
661
851
 
 
852
    def get_data_stream(self, revision_ids):
 
853
        path = self.bzrdir._path_for_remote_call(self._client)
 
854
        response, protocol = self._client.call_expecting_body(
 
855
            'Repository.stream_knit_data_for_revisions', path, *revision_ids)
 
856
        if response == ('ok',):
 
857
            return self._deserialise_stream(protocol)
 
858
        elif (response == ('error', "Generic bzr smart protocol error: "
 
859
                "bad request 'Repository.stream_knit_data_for_revisions'") or
 
860
              response == ('error', "Generic bzr smart protocol error: "
 
861
                "bad request u'Repository.stream_knit_data_for_revisions'")):
 
862
            protocol.cancel_read_body()
 
863
            self._ensure_real()
 
864
            return self._real_repository.get_data_stream(revision_ids)
 
865
        else:
 
866
            raise errors.UnexpectedSmartServerResponse(response)
 
867
 
 
868
    def _deserialise_stream(self, protocol):
 
869
        buffer = StringIO(protocol.read_body_bytes())
 
870
        reader = ContainerReader(buffer)
 
871
        for record_names, read_bytes in reader.iter_records():
 
872
            try:
 
873
                # These records should have only one name, and that name
 
874
                # should be a one-element tuple.
 
875
                [name_tuple] = record_names
 
876
            except ValueError:
 
877
                raise errors.SmartProtocolError(
 
878
                    'Repository data stream had invalid record name %r'
 
879
                    % (record_names,))
 
880
            yield name_tuple, read_bytes(None)
 
881
 
 
882
    def insert_data_stream(self, stream):
 
883
        self._ensure_real()
 
884
        self._real_repository.insert_data_stream(stream)
 
885
 
 
886
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
 
887
        self._ensure_real()
 
888
        return self._real_repository.item_keys_introduced_by(revision_ids,
 
889
            _files_pb=_files_pb)
 
890
 
 
891
    def revision_graph_can_have_wrong_parents(self):
 
892
        # The answer depends on the remote repo format.
 
893
        self._ensure_real()
 
894
        return self._real_repository.revision_graph_can_have_wrong_parents()
 
895
 
 
896
    def _find_inconsistent_revision_parents(self):
 
897
        self._ensure_real()
 
898
        return self._real_repository._find_inconsistent_revision_parents()
 
899
 
 
900
    def _check_for_inconsistent_revision_parents(self):
 
901
        self._ensure_real()
 
902
        return self._real_repository._check_for_inconsistent_revision_parents()
 
903
 
 
904
    def _make_parents_provider(self):
 
905
        self._ensure_real()
 
906
        return self._real_repository._make_parents_provider()
 
907
 
662
908
 
663
909
class RemoteBranchLockableFiles(LockableFiles):
664
910
    """A 'LockableFiles' implementation that talks to a smart server.
718
964
        assert isinstance(a_bzrdir, RemoteBzrDir)
719
965
        return a_bzrdir.create_branch()
720
966
 
 
967
    def supports_tags(self):
 
968
        # Remote branches might support tags, but we won't know until we
 
969
        # access the real remote branch.
 
970
        return True
 
971
 
721
972
 
722
973
class RemoteBranch(branch.Branch):
723
974
    """Branch stored on a server accessed by HPSS RPC.
736
987
        # We intentionally don't call the parent class's __init__, because it
737
988
        # will try to assign to self.tags, which is a property in this subclass.
738
989
        # And the parent's __init__ doesn't do much anyway.
 
990
        self._revision_id_to_revno_cache = None
739
991
        self._revision_history_cache = None
740
992
        self.bzrdir = remote_bzrdir
741
993
        if _client is not None:
742
994
            self._client = _client
743
995
        else:
744
 
            self._client = client._SmartClient(self.bzrdir._medium)
 
996
            self._client = client._SmartClient(self.bzrdir._shared_medium)
745
997
        self.repository = remote_repository
746
998
        if real_branch is not None:
747
999
            self._real_branch = real_branch
827
1079
            self.repository.unlock()
828
1080
        path = self.bzrdir._path_for_remote_call(self._client)
829
1081
        response = self._client.call('Branch.lock_write', path, branch_token,
830
 
                                     repo_token)
 
1082
                                     repo_token or '')
831
1083
        if response[0] == 'ok':
832
1084
            ok, branch_token, repo_token = response
833
1085
            return branch_token, repo_token
839
1091
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
840
1092
        elif response[0] == 'ReadOnlyError':
841
1093
            raise errors.ReadOnlyError(self)
 
1094
        elif response[0] == 'LockFailed':
 
1095
            raise errors.LockFailed(response[1], response[2])
842
1096
        else:
843
 
            assert False, 'unexpected response code %r' % (response,)
 
1097
            raise errors.UnexpectedSmartServerResponse(response)
844
1098
            
845
1099
    def lock_write(self, token=None):
846
1100
        if not self._lock_mode:
878
1132
                if token != self._lock_token:
879
1133
                    raise errors.TokenMismatch(token, self._lock_token)
880
1134
            self._lock_count += 1
881
 
        return self._lock_token
 
1135
        return self._lock_token or None
882
1136
 
883
1137
    def _unlock(self, branch_token, repo_token):
884
1138
        path = self.bzrdir._path_for_remote_call(self._client)
885
1139
        response = self._client.call('Branch.unlock', path, branch_token,
886
 
                                     repo_token)
 
1140
                                     repo_token or '')
887
1141
        if response == ('ok',):
888
1142
            return
889
1143
        elif response[0] == 'TokenMismatch':
890
1144
            raise errors.TokenMismatch(
891
1145
                str((branch_token, repo_token)), '(remote tokens)')
892
1146
        else:
893
 
            assert False, 'unexpected response code %s' % (response,)
 
1147
            raise errors.UnexpectedSmartServerResponse(response)
894
1148
 
895
1149
    def unlock(self):
896
1150
        self._lock_count -= 1
899
1153
            mode = self._lock_mode
900
1154
            self._lock_mode = None
901
1155
            if self._real_branch is not None:
902
 
                if not self._leave_lock:
 
1156
                if (not self._leave_lock and mode == 'w' and
 
1157
                    self._repo_lock_token):
903
1158
                    # If this RemoteBranch will remove the physical lock for the
904
1159
                    # repository, make sure the _real_branch doesn't do it
905
1160
                    # first.  (Because the _real_branch's repository is set to
923
1178
        return self._real_branch.break_lock()
924
1179
 
925
1180
    def leave_lock_in_place(self):
 
1181
        if not self._lock_token:
 
1182
            raise NotImplementedError(self.leave_lock_in_place)
926
1183
        self._leave_lock = True
927
1184
 
928
1185
    def dont_leave_lock_in_place(self):
 
1186
        if not self._lock_token:
 
1187
            raise NotImplementedError(self.dont_leave_lock_in_place)
929
1188
        self._leave_lock = False
930
1189
 
931
1190
    def last_revision_info(self):
985
1244
        # format, because RemoteBranches can't be created at arbitrary URLs.
986
1245
        # XXX: if to_bzrdir is a RemoteBranch, this should perhaps do
987
1246
        # to_bzrdir.create_branch...
988
 
        result = branch.BranchFormat.get_default_format().initialize(to_bzrdir)
 
1247
        self._ensure_real()
 
1248
        result = self._real_branch._format.initialize(to_bzrdir)
989
1249
        self.copy_content_into(result, revision_id=revision_id)
990
1250
        result.set_parent(self.bzrdir.root_transport.base)
991
1251
        return result
992
1252
 
993
1253
    @needs_write_lock
994
 
    def append_revision(self, *revision_ids):
995
 
        self._ensure_real()
996
 
        return self._real_branch.append_revision(*revision_ids)
997
 
 
998
 
    @needs_write_lock
999
1254
    def pull(self, source, overwrite=False, stop_revision=None,
1000
1255
             **kwargs):
1001
1256
        # FIXME: This asks the real branch to run the hooks, which means
1038
1293
        self._ensure_real()
1039
1294
        return self._real_branch.set_push_location(location)
1040
1295
 
1041
 
    def update_revisions(self, other, stop_revision=None):
 
1296
    def update_revisions(self, other, stop_revision=None, overwrite=False):
1042
1297
        self._ensure_real()
1043
1298
        return self._real_branch.update_revisions(
1044
 
            other, stop_revision=stop_revision)
 
1299
            other, stop_revision=stop_revision, overwrite=overwrite)
1045
1300
 
1046
1301
 
1047
1302
class RemoteBranchConfig(BranchConfig):