29
from bzrlib.branch import BranchReferenceFormat
28
from bzrlib.branch import Branch, BranchReferenceFormat
30
29
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
31
30
from bzrlib.config import BranchConfig, TreeConfig
32
31
from bzrlib.decorators import needs_read_lock, needs_write_lock
33
32
from bzrlib.errors import NoSuchRevision
34
33
from bzrlib.lockable_files import LockableFiles
35
from bzrlib.pack import ContainerPushParser
34
from bzrlib.revision import NULL_REVISION
36
35
from bzrlib.smart import client, vfs
37
from bzrlib.symbol_versioning import (
41
from bzrlib.revision import NULL_REVISION
42
36
from bzrlib.trace import note
44
38
# Note: RemoteBzrDirFormat is in bzrdir.py
86
80
self._real_bzrdir.create_repository(shared=shared)
87
81
return self.open_repository()
89
def destroy_repository(self):
90
"""See BzrDir.destroy_repository"""
92
self._real_bzrdir.destroy_repository()
94
83
def create_branch(self):
95
84
self._ensure_real()
96
85
real_branch = self._real_bzrdir.create_branch()
97
86
return RemoteBranch(self, self.find_repository(), real_branch)
99
def destroy_branch(self):
100
"""See BzrDir.destroy_branch"""
102
self._real_bzrdir.destroy_branch()
104
def create_workingtree(self, revision_id=None, from_branch=None):
88
def create_workingtree(self, revision_id=None):
105
89
raise errors.NotLocalUrl(self.transport.base)
107
91
def find_branch_format(self):
264
248
self._lock_token = None
265
249
self._lock_count = 0
266
250
self._leave_lock = False
268
# These depend on the actual remote format, so force them off for
269
# maximum compatibility. XXX: In future these should depend on the
270
# remote repository instance, but this is irrelevant until we perform
271
# reconcile via an RPC call.
272
self._reconcile_does_inventory_gc = False
273
self._reconcile_fixes_text_parents = False
274
self._reconcile_backsup_inventory = False
275
self.base = self.bzrdir.transport.base
278
return "%s(%s)" % (self.__class__.__name__, self.base)
282
def abort_write_group(self):
283
"""Complete a write group on the decorated repository.
285
Smart methods peform operations in a single step so this api
286
is not really applicable except as a compatibility thunk
287
for older plugins that don't use e.g. the CommitBuilder
291
return self._real_repository.abort_write_group()
293
def commit_write_group(self):
294
"""Complete a write group on the decorated repository.
296
Smart methods peform operations in a single step so this api
297
is not really applicable except as a compatibility thunk
298
for older plugins that don't use e.g. the CommitBuilder
302
return self._real_repository.commit_write_group()
304
252
def _ensure_real(self):
305
253
"""Ensure that there is a _real_repository set.
311
259
#self._real_repository = self.bzrdir._real_bzrdir.open_repository()
312
260
self._set_real_repository(self.bzrdir._real_bzrdir.open_repository())
314
def find_text_key_references(self):
315
"""Find the text key references within the repository.
317
:return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
318
revision_ids. Each altered file-ids has the exact revision_ids that
319
altered it listed explicitly.
320
:return: A dictionary mapping text keys ((fileid, revision_id) tuples)
321
to whether they were referred to by the inventory of the
322
revision_id that they contain. The inventory texts from all present
323
revision ids are assessed to generate this report.
326
return self._real_repository.find_text_key_references()
328
def _generate_text_key_index(self):
329
"""Generate a new text key index for the repository.
331
This is an expensive function that will take considerable time to run.
333
:return: A dict mapping (file_id, revision_id) tuples to a list of
334
parents, also (file_id, revision_id) tuples.
337
return self._real_repository._generate_text_key_index()
339
262
def get_revision_graph(self, revision_id=None):
340
263
"""See Repository.get_revision_graph()."""
341
264
if revision_id is None:
343
elif revision.is_null(revision_id):
266
elif revision_id == NULL_REVISION:
346
269
path = self.bzrdir._path_for_remote_call(self._client)
376
299
assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
377
300
return response[0] == 'yes'
379
def has_revisions(self, revision_ids):
380
"""See Repository.has_revisions()."""
382
for revision_id in revision_ids:
383
if self.has_revision(revision_id):
384
result.add(revision_id)
387
def has_same_location(self, other):
388
return (self.__class__ == other.__class__ and
389
self.bzrdir.transport.base == other.bzrdir.transport.base)
391
302
def get_graph(self, other_repository=None):
392
303
"""Return the graph for this repository format"""
394
304
return self._real_repository.get_graph(other_repository)
396
306
def gather_stats(self, revid=None, committers=None):
397
307
"""See Repository.gather_stats()."""
398
308
path = self.bzrdir._path_for_remote_call(self._client)
399
# revid can be None to indicate no revisions, not just NULL_REVISION
400
if revid is None or revision.is_null(revid):
309
if revid in (None, NULL_REVISION):
403
312
fmt_revid = revid
427
def find_branches(self, using=False):
428
"""See Repository.find_branches()."""
429
# should be an API call to the server.
431
return self._real_repository.find_branches(using=using)
433
336
def get_physical_lock_status(self):
434
337
"""See Repository.get_physical_lock_status()."""
435
# should be an API call to the server.
437
return self._real_repository.get_physical_lock_status()
439
def is_in_write_group(self):
440
"""Return True if there is an open write group.
442
write groups are only applicable locally for the smart server..
444
if self._real_repository:
445
return self._real_repository.is_in_write_group()
448
return self._lock_count >= 1
450
340
def is_shared(self):
451
341
"""See Repository.is_shared()."""
479
366
raise errors.LockContention('(remote lock)')
480
367
elif response[0] == 'UnlockableTransport':
481
368
raise errors.UnlockableTransport(self.bzrdir.root_transport)
482
elif response[0] == 'LockFailed':
483
raise errors.LockFailed(response[1], response[2])
485
370
raise errors.UnexpectedSmartServerResponse(response)
487
372
def lock_write(self, token=None):
488
373
if not self._lock_mode:
489
374
self._lock_token = self._remote_lock_write(token)
490
# if self._lock_token is None, then this is something like packs or
491
# svn where we don't get to lock the repo, or a weave style repository
492
# where we cannot lock it over the wire and attempts to do so will
375
assert self._lock_token, 'Remote server did not return a token!'
494
376
if self._real_repository is not None:
495
377
self._real_repository.lock_write(token=self._lock_token)
496
378
if token is not None:
530
408
elif self._lock_mode == 'r':
531
409
self._real_repository.lock_read()
533
def start_write_group(self):
534
"""Start a write group on the decorated repository.
536
Smart methods peform operations in a single step so this api
537
is not really applicable except as a compatibility thunk
538
for older plugins that don't use e.g. the CommitBuilder
542
return self._real_repository.start_write_group()
544
411
def _unlock(self, token):
545
412
path = self.bzrdir._path_for_remote_call(self._client)
547
# with no token the remote repository is not persistently locked.
549
413
response = self._client.call('Repository.unlock', path, token)
550
414
if response == ('ok',):
557
421
def unlock(self):
558
422
self._lock_count -= 1
559
if self._lock_count > 0:
561
old_mode = self._lock_mode
562
self._lock_mode = None
564
# The real repository is responsible at present for raising an
565
# exception if it's in an unfinished write group. However, it
566
# normally will *not* actually remove the lock from disk - that's
567
# done by the server on receiving the Repository.unlock call.
568
# This is just to let the _real_repository stay up to date.
423
if not self._lock_count:
424
mode = self._lock_mode
425
self._lock_mode = None
569
426
if self._real_repository is not None:
570
427
self._real_repository.unlock()
572
# The rpc-level lock should be released even if there was a
573
# problem releasing the vfs-based lock.
575
429
# Only write-locked repositories need to make a remote method
576
430
# call to perfom the unlock.
577
old_token = self._lock_token
578
self._lock_token = None
579
if not self._leave_lock:
580
self._unlock(old_token)
432
assert self._lock_token, 'Locked, but no token!'
433
token = self._lock_token
434
self._lock_token = None
435
if not self._leave_lock:
582
438
def break_lock(self):
583
439
# should hand off to the network
585
441
return self._real_repository.break_lock()
587
443
def _get_tarball(self, compression):
588
"""Return a TemporaryFile containing a repository tarball.
590
Returns None if the server does not support sending tarballs.
444
"""Return a TemporaryFile containing a repository tarball"""
593
446
path = self.bzrdir._path_for_remote_call(self._client)
594
447
response, protocol = self._client.call_expecting_body(
595
448
'Repository.tarball', path, compression)
449
assert response[0] in ('ok', 'failure'), \
450
'unexpected response code %s' % (response,)
596
451
if response[0] == 'ok':
597
452
# Extract the tarball and return it
598
453
t = tempfile.NamedTemporaryFile()
600
455
t.write(protocol.read_body_bytes())
603
if (response == ('error', "Generic bzr smart protocol error: "
604
"bad request 'Repository.tarball'") or
605
response == ('error', "Generic bzr smart protocol error: "
606
"bad request u'Repository.tarball'")):
607
protocol.cancel_read_body()
609
raise errors.UnexpectedSmartServerResponse(response)
459
raise errors.SmartServerError(error_code=response)
611
461
def sprout(self, to_bzrdir, revision_id=None):
612
462
# TODO: Option to control what format is created?
614
dest_repo = self._real_repository._format.initialize(to_bzrdir,
616
dest_repo.fetch(self, revision_id=revision_id)
463
to_repo = to_bzrdir.create_repository()
464
self._copy_repository_tarball(to_repo, revision_id)
619
467
### These methods are just thin shims to the VFS object for now.
622
470
self._ensure_real()
623
471
return self._real_repository.revision_tree(revision_id)
625
def get_serializer_format(self):
627
return self._real_repository.get_serializer_format()
629
473
def get_commit_builder(self, branch, parents, config, timestamp=None,
630
474
timezone=None, committer=None, revprops=None,
631
475
revision_id=None):
635
479
builder = self._real_repository.get_commit_builder(branch, parents,
636
480
config, timestamp=timestamp, timezone=timezone,
637
481
committer=committer, revprops=revprops, revision_id=revision_id)
482
# Make the builder use this RemoteRepository rather than the real one.
483
builder.repository = self
640
486
@needs_write_lock
683
525
def fetch(self, source, revision_id=None, pb=None):
684
if self.has_same_location(source):
685
# check that last_revision is in 'from' and then return a
687
if (revision_id is not None and
688
not revision.is_null(revision_id)):
689
self.get_revision(revision_id)
691
526
self._ensure_real()
692
527
return self._real_repository.fetch(
693
528
source, revision_id=revision_id, pb=pb)
695
def create_bundle(self, target, base, fileobj, format=None):
697
self._real_repository.create_bundle(target, base, fileobj, format)
700
531
def control_weaves(self):
701
532
self._ensure_real()
715
546
self._ensure_real()
716
547
return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
718
def _get_versioned_file_checker(self, revisions, revision_versions_cache):
720
return self._real_repository._get_versioned_file_checker(
721
revisions, revision_versions_cache)
723
def iter_files_bytes(self, desired_files):
724
"""See Repository.iter_file_bytes.
727
return self._real_repository.iter_files_bytes(desired_files)
730
550
def get_signature_text(self, revision_id):
731
551
self._ensure_real()
775
595
return self._real_repository.get_revision_reconcile(revision_id)
778
def check(self, revision_ids=None):
598
def check(self, revision_ids):
779
599
self._ensure_real()
780
return self._real_repository.check(revision_ids=revision_ids)
600
return self._real_repository.check(revision_ids)
782
602
def copy_content_into(self, destination, revision_id=None):
783
603
self._ensure_real()
784
604
return self._real_repository.copy_content_into(
785
605
destination, revision_id=revision_id)
787
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
607
def _copy_repository_tarball(self, destination, revision_id=None):
788
608
# get a tarball of the remote repository, and copy from that into the
790
610
from bzrlib import osutils
613
from StringIO import StringIO
793
614
# TODO: Maybe a progress bar while streaming the tarball?
794
615
note("Copying repository content as tarball...")
795
616
tar_file = self._get_tarball('bz2')
798
destination = to_bzrdir.create_repository()
800
618
tar = tarfile.open('repository', fileobj=tar_file,
809
627
osutils.rmtree(tmpdir)
630
# TODO: if the server doesn't support this operation, maybe do it the
631
# slow way using the _real_repository?
813
633
# TODO: Suggestion from john: using external tar is much faster than
814
634
# python's tarfile library, but it may not work on windows.
818
"""Compress the data within the repository.
820
This is not currently implemented within the smart server.
823
return self._real_repository.pack()
825
636
def set_make_working_trees(self, new_value):
826
637
raise NotImplementedError(self.set_make_working_trees)
853
664
return self._real_repository.store_revision_signature(
854
665
gpg_strategy, plaintext, revision_id)
856
def add_signature_text(self, revision_id, signature):
858
return self._real_repository.add_signature_text(revision_id, signature)
860
667
def has_signature_for_revision_id(self, revision_id):
861
668
self._ensure_real()
862
669
return self._real_repository.has_signature_for_revision_id(revision_id)
864
def get_data_stream(self, revision_ids):
865
REQUEST_NAME = 'Repository.stream_revisions_chunked'
866
path = self.bzrdir._path_for_remote_call(self._client)
867
response, protocol = self._client.call_expecting_body(
868
REQUEST_NAME, path, *revision_ids)
870
if response == ('ok',):
871
return self._deserialise_stream(protocol)
872
elif (response == ('error', "Generic bzr smart protocol error: "
873
"bad request '%s'" % REQUEST_NAME) or
874
response == ('error', "Generic bzr smart protocol error: "
875
"bad request u'%s'" % REQUEST_NAME)):
876
protocol.cancel_read_body()
878
return self._real_repository.get_data_stream(revision_ids)
880
raise errors.UnexpectedSmartServerResponse(response)
882
def _deserialise_stream(self, protocol):
883
stream = protocol.read_streamed_body()
884
container_parser = ContainerPushParser()
886
container_parser.accept_bytes(bytes)
887
records = container_parser.read_pending_records()
888
for record_names, record_bytes in records:
889
if len(record_names) != 1:
890
# These records should have only one name, and that name
891
# should be a one-element tuple.
892
raise errors.SmartProtocolError(
893
'Repository data stream had invalid record name %r'
895
name_tuple = record_names[0]
896
yield name_tuple, record_bytes
898
def insert_data_stream(self, stream):
900
self._real_repository.insert_data_stream(stream)
902
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
904
return self._real_repository.item_keys_introduced_by(revision_ids,
907
def revision_graph_can_have_wrong_parents(self):
908
# The answer depends on the remote repo format.
910
return self._real_repository.revision_graph_can_have_wrong_parents()
912
def _find_inconsistent_revision_parents(self):
914
return self._real_repository._find_inconsistent_revision_parents()
916
def _check_for_inconsistent_revision_parents(self):
918
return self._real_repository._check_for_inconsistent_revision_parents()
920
def _make_parents_provider(self):
922
return self._real_repository._make_parents_provider()
925
672
class RemoteBranchLockableFiles(LockableFiles):
926
673
"""A 'LockableFiles' implementation that talks to a smart server.
1003
745
# We intentionally don't call the parent class's __init__, because it
1004
746
# will try to assign to self.tags, which is a property in this subclass.
1005
747
# And the parent's __init__ doesn't do much anyway.
1006
self._revision_id_to_revno_cache = None
1007
748
self._revision_history_cache = None
1008
749
self.bzrdir = remote_bzrdir
1009
750
if _client is not None:
1010
751
self._client = _client
1012
self._client = client._SmartClient(self.bzrdir._shared_medium)
753
self._client = client._SmartClient(self.bzrdir._medium)
1013
754
self.repository = remote_repository
1014
755
if real_branch is not None:
1015
756
self._real_branch = real_branch
1148
887
if token != self._lock_token:
1149
888
raise errors.TokenMismatch(token, self._lock_token)
1150
889
self._lock_count += 1
1151
return self._lock_token or None
890
return self._lock_token
1153
892
def _unlock(self, branch_token, repo_token):
1154
893
path = self.bzrdir._path_for_remote_call(self._client)
1155
894
response = self._client.call('Branch.unlock', path, branch_token,
1157
896
if response == ('ok',):
1159
898
elif response[0] == 'TokenMismatch':
1260
994
# format, because RemoteBranches can't be created at arbitrary URLs.
1261
995
# XXX: if to_bzrdir is a RemoteBranch, this should perhaps do
1262
996
# to_bzrdir.create_branch...
1264
result = self._real_branch._format.initialize(to_bzrdir)
997
result = branch.BranchFormat.get_default_format().initialize(to_bzrdir)
1265
998
self.copy_content_into(result, revision_id=revision_id)
1266
999
result.set_parent(self.bzrdir.root_transport.base)
1269
1002
@needs_write_lock
1003
def append_revision(self, *revision_ids):
1005
return self._real_branch.append_revision(*revision_ids)
1270
1008
def pull(self, source, overwrite=False, stop_revision=None,
1272
1010
# FIXME: This asks the real branch to run the hooks, which means
1309
1047
self._ensure_real()
1310
1048
return self._real_branch.set_push_location(location)
1312
def update_revisions(self, other, stop_revision=None, overwrite=False):
1050
def update_revisions(self, other, stop_revision=None):
1313
1051
self._ensure_real()
1314
1052
return self._real_branch.update_revisions(
1315
other, stop_revision=stop_revision, overwrite=overwrite)
1053
other, stop_revision=stop_revision)
1318
1056
class RemoteBranchConfig(BranchConfig):