33
33
from bzrlib.branch import BranchReferenceFormat
34
34
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
35
from bzrlib.config import BranchConfig, TreeConfig
35
36
from bzrlib.decorators import needs_read_lock, needs_write_lock
36
37
from bzrlib.errors import (
38
39
SmartProtocolError,
40
41
from bzrlib.lockable_files import LockableFiles
42
from bzrlib.pack import ContainerPushParser
41
43
from bzrlib.smart import client, vfs
44
from bzrlib.symbol_versioning import (
42
48
from bzrlib.revision import ensure_null, NULL_REVISION
43
49
from bzrlib.trace import mutter, note, warning
82
88
self._real_bzrdir = BzrDir.open_from_transport(
83
89
self.root_transport, _server_formats=False)
85
def cloning_metadir(self, stacked=False):
87
return self._real_bzrdir.cloning_metadir(stacked)
89
def _translate_error(self, err, **context):
90
_translate_error(err, bzrdir=self, **context)
92
91
def create_repository(self, shared=False):
93
92
self._ensure_real()
94
93
self._real_bzrdir.create_repository(shared=shared)
215
216
"""Upgrading of remote bzrdirs is not supported yet."""
218
def clone(self, url, revision_id=None, force_new_repo=False,
219
preserve_stacking=False):
219
def clone(self, url, revision_id=None, force_new_repo=False):
220
220
self._ensure_real()
221
221
return self._real_bzrdir.clone(url, revision_id=revision_id,
222
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
224
def get_config(self):
226
return self._real_bzrdir.get_config()
222
force_new_repo=force_new_repo)
229
225
class RemoteRepositoryFormat(repository.RepositoryFormat):
242
_matchingbzrdir = RemoteBzrDirFormat()
238
_matchingbzrdir = RemoteBzrDirFormat
244
240
def initialize(self, a_bzrdir, shared=False):
245
241
if not isinstance(a_bzrdir, RemoteBzrDir):
246
prior_repo = self._creating_bzrdir.open_repository()
247
prior_repo._ensure_real()
248
return prior_repo._real_repository._format.initialize(
249
a_bzrdir, shared=shared)
242
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
250
243
return a_bzrdir.create_repository(shared=shared)
252
245
def open(self, a_bzrdir):
351
342
Used before calls to self._real_repository.
353
if self._real_repository is None:
344
if not self._real_repository:
354
345
self.bzrdir._ensure_real()
355
self._set_real_repository(
356
self.bzrdir._real_bzrdir.open_repository())
358
def _translate_error(self, err, **context):
359
self.bzrdir._translate_error(err, repository=self, **context)
346
#self._real_repository = self.bzrdir._real_bzrdir.open_repository()
347
self._set_real_repository(self.bzrdir._real_bzrdir.open_repository())
361
349
def find_text_key_references(self):
362
350
"""Find the text key references within the repository.
400
388
response = self._client.call_expecting_body(
401
389
'Repository.get_revision_graph', path, revision_id)
402
390
except errors.ErrorFromSmartServer, err:
403
self._translate_error(err)
391
if err.error_verb == 'nosuchrevision':
392
raise NoSuchRevision(self, revision_id)
404
394
response_tuple, response_handler = response
405
395
if response_tuple[0] != 'ok':
406
396
raise errors.UnexpectedSmartServerResponse(response_tuple)
426
416
'Repository.has_revision', path, revision_id)
427
417
if response[0] not in ('yes', 'no'):
428
418
raise errors.UnexpectedSmartServerResponse(response)
429
if response[0] == 'yes':
431
for fallback_repo in self._fallback_repositories:
432
if fallback_repo.has_revision(revision_id):
419
return response[0] == 'yes'
436
421
def has_revisions(self, revision_ids):
437
422
"""See Repository.has_revisions()."""
438
# FIXME: This does many roundtrips, particularly when there are
439
# fallback repositories. -- mbp 20080905
441
424
for revision_id in revision_ids:
442
425
if self.has_revision(revision_id):
543
526
response = self._client.call('Repository.lock_write', path, token)
544
527
except errors.ErrorFromSmartServer, err:
545
self._translate_error(err, token=token)
528
if err.error_verb == 'LockContention':
529
raise errors.LockContention('(remote lock)')
530
elif err.error_verb == 'UnlockableTransport':
531
raise errors.UnlockableTransport(self.bzrdir.root_transport)
532
elif err.error_verb == 'LockFailed':
533
raise errors.LockFailed(err.error_args[0], err.error_args[1])
547
536
if response[0] == 'ok':
548
537
ok, token = response
551
540
raise errors.UnexpectedSmartServerResponse(response)
553
def lock_write(self, token=None, _skip_rpc=False):
542
def lock_write(self, token=None):
554
543
if not self._lock_mode:
556
if self._lock_token is not None:
557
if token != self._lock_token:
558
raise errors.TokenMismatch(token, self._lock_token)
559
self._lock_token = token
561
self._lock_token = self._remote_lock_write(token)
544
self._lock_token = self._remote_lock_write(token)
562
545
# if self._lock_token is None, then this is something like packs or
563
546
# svn where we don't get to lock the repo, or a weave style repository
564
547
# where we cannot lock it over the wire and attempts to do so will
596
579
:param repository: The repository to fallback to for non-hpss
597
580
implemented operations.
599
if self._real_repository is not None:
600
raise AssertionError('_real_repository is already set')
601
582
if isinstance(repository, RemoteRepository):
602
583
raise AssertionError()
603
584
self._real_repository = repository
604
for fb in self._fallback_repositories:
605
self._real_repository.add_fallback_repository(fb)
606
585
if self._lock_mode == 'w':
607
586
# if we are already locked, the real repository must be able to
608
587
# acquire the lock with our token.
714
695
# FIXME: It ought to be possible to call this without immediately
715
696
# triggering _ensure_real. For now it's the easiest thing to do.
716
697
self._ensure_real()
717
real_repo = self._real_repository
718
builder = real_repo.get_commit_builder(branch, parents,
698
builder = self._real_repository.get_commit_builder(branch, parents,
719
699
config, timestamp=timestamp, timezone=timezone,
720
700
committer=committer, revprops=revprops, revision_id=revision_id)
723
def add_fallback_repository(self, repository):
724
"""Add a repository to use for looking up data not held locally.
726
:param repository: A repository.
728
# XXX: At the moment the RemoteRepository will allow fallbacks
729
# unconditionally - however, a _real_repository will usually exist,
730
# and may raise an error if it's not accommodated by the underlying
731
# format. Eventually we should check when opening the repository
732
# whether it's willing to allow them or not.
734
# We need to accumulate additional repositories here, to pass them in
736
self._fallback_repositories.append(repository)
737
# They are also seen by the fallback repository. If it doesn't exist
738
# yet they'll be added then. This implicitly copies them.
741
703
def add_inventory(self, revid, inv, parents):
742
704
self._ensure_real()
743
705
return self._real_repository.add_inventory(revid, inv, parents)
799
766
return repository.InterRepository.get(
800
767
other, self).search_missing_revision_ids(revision_id, find_ghosts)
802
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False):
769
def fetch(self, source, revision_id=None, pb=None):
803
770
if self.has_same_location(source):
804
771
# check that last_revision is in 'from' and then return a
807
774
not revision.is_null(revision_id)):
808
775
self.get_revision(revision_id)
810
inter = repository.InterRepository.get(source, self)
812
return inter.fetch(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts)
814
except NotImplementedError:
815
raise errors.IncompatibleRepositories(source, self)
778
return self._real_repository.fetch(
779
source, revision_id=revision_id, pb=pb)
817
781
def create_bundle(self, target, base, fileobj, format=None):
818
782
self._ensure_real()
819
783
self._real_repository.create_bundle(target, base, fileobj, format)
786
def control_weaves(self):
788
return self._real_repository.control_weaves
822
791
def get_ancestry(self, revision_id, topo_sorted=True):
823
792
self._ensure_real()
824
793
return self._real_repository.get_ancestry(revision_id, topo_sorted)
796
def get_inventory_weave(self):
798
return self._real_repository.get_inventory_weave()
826
800
def fileids_altered_by_revision_ids(self, revision_ids):
827
801
self._ensure_real()
828
802
return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
838
812
self._ensure_real()
839
813
return self._real_repository.iter_files_bytes(desired_files)
842
def _fetch_order(self):
843
"""Decorate the real repository for now.
845
In the long term getting this back from the remote repository as part
846
of open would be more efficient.
849
return self._real_repository._fetch_order
852
def _fetch_uses_deltas(self):
853
"""Decorate the real repository for now.
855
In the long term getting this back from the remote repository as part
856
of open would be more efficient.
859
return self._real_repository._fetch_uses_deltas
862
def _fetch_reconcile(self):
863
"""Decorate the real repository for now.
865
In the long term getting this back from the remote repository as part
866
of open would be more efficient.
869
return self._real_repository._fetch_reconcile
871
815
def get_parent_map(self, keys):
872
816
"""See bzrlib.Graph.get_parent_map()."""
873
817
# Hack to build up the caching logic.
1103
1038
self._ensure_real()
1104
1039
return self._real_repository.pack()
1107
def revisions(self):
1108
"""Decorate the real repository for now.
1110
In the short term this should become a real object to intercept graph
1113
In the long term a full blown network facility is needed.
1116
return self._real_repository.revisions
1118
1041
def set_make_working_trees(self, new_value):
1119
1042
self._ensure_real()
1120
1043
self._real_repository.set_make_working_trees(new_value)
1123
def signatures(self):
1124
"""Decorate the real repository for now.
1126
In the long term a full blown network facility is needed to avoid
1127
creating a real repository object locally.
1130
return self._real_repository.signatures
1132
1045
@needs_write_lock
1133
1046
def sign_revision(self, revision_id, gpg_strategy):
1134
1047
self._ensure_real()
1135
1048
return self._real_repository.sign_revision(revision_id, gpg_strategy)
1139
"""Decorate the real repository for now.
1141
In the long term a full blown network facility is needed to avoid
1142
creating a real repository object locally.
1145
return self._real_repository.texts
1147
1050
@needs_read_lock
1148
1051
def get_revisions(self, revision_ids):
1149
1052
self._ensure_real()
1175
1078
self._ensure_real()
1176
1079
return self._real_repository.has_signature_for_revision_id(revision_id)
1081
def get_data_stream_for_search(self, search):
1082
medium = self._client._medium
1083
if not medium._remote_is_at_least_1_2:
1085
return self._real_repository.get_data_stream_for_search(search)
1086
REQUEST_NAME = 'Repository.stream_revisions_chunked'
1087
path = self.bzrdir._path_for_remote_call(self._client)
1088
body = self._serialise_search_recipe(search.get_recipe())
1090
result = self._client.call_with_body_bytes_expecting_body(
1091
REQUEST_NAME, (path,), body)
1092
response, protocol = result
1093
except errors.UnknownSmartMethod:
1094
# Server does not support this method, so fall back to VFS.
1095
# Worse, we have to force a disconnection, because the server now
1096
# doesn't realise it has a body on the wire to consume, so the
1097
# only way to recover is to abandon the connection.
1099
'Server is too old for streaming pull, reconnecting. '
1100
'(Upgrade the server to Bazaar 1.2 to avoid this)')
1102
# To avoid having to disconnect repeatedly, we keep track of the
1103
# fact the server doesn't understand this remote method.
1104
medium._remote_is_at_least_1_2 = False
1106
return self._real_repository.get_data_stream_for_search(search)
1108
if response == ('ok',):
1109
return self._deserialise_stream(protocol)
1110
if response == ('NoSuchRevision', ):
1111
# We cannot easily identify the revision that is missing in this
1112
# situation without doing much more network IO. For now, bail.
1113
raise NoSuchRevision(self, "unknown")
1115
raise errors.UnexpectedSmartServerResponse(response)
1117
def _deserialise_stream(self, protocol):
1118
stream = protocol.read_streamed_body()
1119
container_parser = ContainerPushParser()
1120
for bytes in stream:
1121
container_parser.accept_bytes(bytes)
1122
records = container_parser.read_pending_records()
1123
for record_names, record_bytes in records:
1124
if len(record_names) != 1:
1125
# These records should have only one name, and that name
1126
# should be a one-element tuple.
1127
raise errors.SmartProtocolError(
1128
'Repository data stream had invalid record name %r'
1130
name_tuple = record_names[0]
1131
yield name_tuple, record_bytes
1133
def insert_data_stream(self, stream):
1135
self._real_repository.insert_data_stream(stream)
1178
1137
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1179
1138
self._ensure_real()
1180
1139
return self._real_repository.item_keys_introduced_by(revision_ids,
1299
1257
self._repo_lock_token = None
1300
1258
self._lock_count = 0
1301
1259
self._leave_lock = False
1302
# The base class init is not called, so we duplicate this:
1303
hooks = branch.Branch.hooks['open']
1306
self._setup_stacking()
1308
def _setup_stacking(self):
1309
# configure stacking into the remote repository, by reading it from
1312
fallback_url = self.get_stacked_on_url()
1313
except (errors.NotStacked, errors.UnstackableBranchFormat,
1314
errors.UnstackableRepositoryFormat), e:
1316
# it's relative to this branch...
1317
fallback_url = urlutils.join(self.base, fallback_url)
1318
transports = [self.bzrdir.root_transport]
1319
if self._real_branch is not None:
1320
transports.append(self._real_branch._transport)
1321
fallback_bzrdir = BzrDir.open(fallback_url, transports)
1322
fallback_repo = fallback_bzrdir.open_repository()
1323
self.repository.add_fallback_repository(fallback_repo)
1325
1261
def _get_real_transport(self):
1326
1262
# if we try vfs access, return the real branch's vfs transport
1345
1281
'to use vfs implementation')
1346
1282
self.bzrdir._ensure_real()
1347
1283
self._real_branch = self.bzrdir._real_bzrdir.open_branch()
1348
if self.repository._real_repository is None:
1349
# Give the remote repository the matching real repo.
1350
real_repo = self._real_branch.repository
1351
if isinstance(real_repo, RemoteRepository):
1352
real_repo._ensure_real()
1353
real_repo = real_repo._real_repository
1354
self.repository._set_real_repository(real_repo)
1355
# Give the real branch the remote repository to let fast-pathing
1284
# Give the remote repository the matching real repo.
1285
real_repo = self._real_branch.repository
1286
if isinstance(real_repo, RemoteRepository):
1287
real_repo._ensure_real()
1288
real_repo = real_repo._real_repository
1289
self.repository._set_real_repository(real_repo)
1290
# Give the branch the remote repository to let fast-pathing happen.
1357
1291
self._real_branch.repository = self.repository
1292
# XXX: deal with _lock_mode == 'w'
1358
1293
if self._lock_mode == 'r':
1359
1294
self._real_branch.lock_read()
1360
elif self._lock_mode == 'w':
1361
self._real_branch.lock_write(token=self._lock_token)
1363
def _translate_error(self, err, **context):
1364
self.repository._translate_error(err, branch=self, **context)
1366
def _clear_cached_state(self):
1367
super(RemoteBranch, self)._clear_cached_state()
1368
if self._real_branch is not None:
1369
self._real_branch._clear_cached_state()
1371
def _clear_cached_state_of_remote_branch_only(self):
1372
"""Like _clear_cached_state, but doesn't clear the cache of
1375
This is useful when falling back to calling a method of
1376
self._real_branch that changes state. In that case the underlying
1377
branch changes, so we need to invalidate this RemoteBranch's cache of
1378
it. However, there's no need to invalidate the _real_branch's cache
1379
too, in fact doing so might harm performance.
1381
super(RemoteBranch, self)._clear_cached_state()
1384
1297
def control_files(self):
1385
1298
# Defer actually creating RemoteBranchLockableFiles until its needed,
1399
1312
self._ensure_real()
1400
1313
return self._real_branch.get_physical_lock_status()
1402
def get_stacked_on_url(self):
1403
"""Get the URL this branch is stacked against.
1405
:raises NotStacked: If the branch is not stacked.
1406
:raises UnstackableBranchFormat: If the branch does not support
1408
:raises UnstackableRepositoryFormat: If the repository does not support
1412
response = self._client.call('Branch.get_stacked_on_url',
1413
self._remote_path())
1414
if response[0] != 'ok':
1415
raise errors.UnexpectedSmartServerResponse(response)
1417
except errors.ErrorFromSmartServer, err:
1418
# there may not be a repository yet, so we can't call through
1419
# its _translate_error
1420
_translate_error(err, branch=self)
1421
except errors.UnknownSmartMethod, err:
1423
return self._real_branch.get_stacked_on_url()
1425
1315
def lock_read(self):
1426
self.repository.lock_read()
1427
1316
if not self._lock_mode:
1428
1317
self._lock_mode = 'r'
1429
1318
self._lock_count = 1
1439
1328
branch_token = token
1440
1329
repo_token = self.repository.lock_write()
1441
1330
self.repository.unlock()
1331
path = self.bzrdir._path_for_remote_call(self._client)
1443
1333
response = self._client.call(
1444
'Branch.lock_write', self._remote_path(),
1445
branch_token, repo_token or '')
1334
'Branch.lock_write', path, branch_token, repo_token or '')
1446
1335
except errors.ErrorFromSmartServer, err:
1447
self._translate_error(err, token=token)
1336
if err.error_verb == 'LockContention':
1337
raise errors.LockContention('(remote lock)')
1338
elif err.error_verb == 'TokenMismatch':
1339
raise errors.TokenMismatch(token, '(remote token)')
1340
elif err.error_verb == 'UnlockableTransport':
1341
raise errors.UnlockableTransport(self.bzrdir.root_transport)
1342
elif err.error_verb == 'ReadOnlyError':
1343
raise errors.ReadOnlyError(self)
1344
elif err.error_verb == 'LockFailed':
1345
raise errors.LockFailed(err.error_args[0], err.error_args[1])
1448
1347
if response[0] != 'ok':
1449
1348
raise errors.UnexpectedSmartServerResponse(response)
1450
1349
ok, branch_token, repo_token = response
1453
1352
def lock_write(self, token=None):
1454
1353
if not self._lock_mode:
1455
# Lock the branch and repo in one remote call.
1456
1354
remote_tokens = self._remote_lock_write(token)
1457
1355
self._lock_token, self._repo_lock_token = remote_tokens
1458
1356
if not self._lock_token:
1459
1357
raise SmartProtocolError('Remote server did not return a token!')
1460
# Tell the self.repository object that it is locked.
1461
self.repository.lock_write(
1462
self._repo_lock_token, _skip_rpc=True)
1358
# TODO: We really, really, really don't want to call _ensure_real
1359
# here, but it's the easiest way to ensure coherency between the
1360
# state of the RemoteBranch and RemoteRepository objects and the
1361
# physical locks. If we don't materialise the real objects here,
1362
# then getting everything in the right state later is complex, so
1363
# for now we just do it the lazy way.
1364
# -- Andrew Bennetts, 2007-02-22.
1464
1366
if self._real_branch is not None:
1465
self._real_branch.lock_write(token=self._lock_token)
1367
self._real_branch.repository.lock_write(
1368
token=self._repo_lock_token)
1370
self._real_branch.lock_write(token=self._lock_token)
1372
self._real_branch.repository.unlock()
1466
1373
if token is not None:
1467
1374
self._leave_lock = True
1376
# XXX: this case seems to be unreachable; token cannot be None.
1469
1377
self._leave_lock = False
1470
1378
self._lock_mode = 'w'
1471
1379
self._lock_count = 1
1473
1381
raise errors.ReadOnlyTransaction
1475
1383
if token is not None:
1476
# A token was given to lock_write, and we're relocking, so
1477
# check that the given token actually matches the one we
1384
# A token was given to lock_write, and we're relocking, so check
1385
# that the given token actually matches the one we already have.
1479
1386
if token != self._lock_token:
1480
1387
raise errors.TokenMismatch(token, self._lock_token)
1481
1388
self._lock_count += 1
1482
# Re-lock the repository too.
1483
self.repository.lock_write(self._repo_lock_token)
1484
1389
return self._lock_token or None
1486
1391
def _unlock(self, branch_token, repo_token):
1392
path = self.bzrdir._path_for_remote_call(self._client)
1488
response = self._client.call('Branch.unlock', self._remote_path(), branch_token,
1394
response = self._client.call('Branch.unlock', path, branch_token,
1489
1395
repo_token or '')
1490
1396
except errors.ErrorFromSmartServer, err:
1491
self._translate_error(err, token=str((branch_token, repo_token)))
1397
if err.error_verb == 'TokenMismatch':
1398
raise errors.TokenMismatch(
1399
str((branch_token, repo_token)), '(remote tokens)')
1492
1401
if response == ('ok',):
1494
1403
raise errors.UnexpectedSmartServerResponse(response)
1496
1405
def unlock(self):
1498
self._lock_count -= 1
1499
if not self._lock_count:
1500
self._clear_cached_state()
1501
mode = self._lock_mode
1502
self._lock_mode = None
1503
if self._real_branch is not None:
1504
if (not self._leave_lock and mode == 'w' and
1505
self._repo_lock_token):
1506
# If this RemoteBranch will remove the physical lock
1507
# for the repository, make sure the _real_branch
1508
# doesn't do it first. (Because the _real_branch's
1509
# repository is set to be the RemoteRepository.)
1510
self._real_branch.repository.leave_lock_in_place()
1511
self._real_branch.unlock()
1513
# Only write-locked branched need to make a remote method
1514
# call to perfom the unlock.
1516
if not self._lock_token:
1517
raise AssertionError('Locked, but no token!')
1518
branch_token = self._lock_token
1519
repo_token = self._repo_lock_token
1520
self._lock_token = None
1521
self._repo_lock_token = None
1522
if not self._leave_lock:
1523
self._unlock(branch_token, repo_token)
1525
self.repository.unlock()
1406
self._lock_count -= 1
1407
if not self._lock_count:
1408
self._clear_cached_state()
1409
mode = self._lock_mode
1410
self._lock_mode = None
1411
if self._real_branch is not None:
1412
if (not self._leave_lock and mode == 'w' and
1413
self._repo_lock_token):
1414
# If this RemoteBranch will remove the physical lock for the
1415
# repository, make sure the _real_branch doesn't do it
1416
# first. (Because the _real_branch's repository is set to
1417
# be the RemoteRepository.)
1418
self._real_branch.repository.leave_lock_in_place()
1419
self._real_branch.unlock()
1421
# Only write-locked branched need to make a remote method call
1422
# to perfom the unlock.
1424
if not self._lock_token:
1425
raise AssertionError('Locked, but no token!')
1426
branch_token = self._lock_token
1427
repo_token = self._repo_lock_token
1428
self._lock_token = None
1429
self._repo_lock_token = None
1430
if not self._leave_lock:
1431
self._unlock(branch_token, repo_token)
1527
1433
def break_lock(self):
1528
1434
self._ensure_real()
1538
1444
raise NotImplementedError(self.dont_leave_lock_in_place)
1539
1445
self._leave_lock = False
1541
def _last_revision_info(self):
1542
response = self._client.call('Branch.last_revision_info', self._remote_path())
1447
def last_revision_info(self):
1448
"""See Branch.last_revision_info()."""
1449
path = self.bzrdir._path_for_remote_call(self._client)
1450
response = self._client.call('Branch.last_revision_info', path)
1543
1451
if response[0] != 'ok':
1544
1452
raise SmartProtocolError('unexpected response code %s' % (response,))
1545
1453
revno = int(response[1])
1549
1457
def _gen_revision_history(self):
1550
1458
"""See Branch._gen_revision_history()."""
1459
path = self.bzrdir._path_for_remote_call(self._client)
1551
1460
response_tuple, response_handler = self._client.call_expecting_body(
1552
'Branch.revision_history', self._remote_path())
1461
'Branch.revision_history', path)
1553
1462
if response_tuple[0] != 'ok':
1554
raise errors.UnexpectedSmartServerResponse(response_tuple)
1463
raise UnexpectedSmartServerResponse(response_tuple)
1555
1464
result = response_handler.read_body_bytes().split('\x00')
1556
1465
if result == ['']:
1560
def _remote_path(self):
1561
return self.bzrdir._path_for_remote_call(self._client)
1563
def _set_last_revision_descendant(self, revision_id, other_branch,
1564
allow_diverged=False, allow_overwrite_descendant=False):
1566
response = self._client.call('Branch.set_last_revision_ex',
1567
self._remote_path(), self._lock_token, self._repo_lock_token, revision_id,
1568
int(allow_diverged), int(allow_overwrite_descendant))
1569
except errors.ErrorFromSmartServer, err:
1570
self._translate_error(err, other_branch=other_branch)
1571
self._clear_cached_state()
1572
if len(response) != 3 and response[0] != 'ok':
1573
raise errors.UnexpectedSmartServerResponse(response)
1574
new_revno, new_revision_id = response[1:]
1575
self._last_revision_info_cache = new_revno, new_revision_id
1576
if self._real_branch is not None:
1577
cache = new_revno, new_revision_id
1578
self._real_branch._last_revision_info_cache = cache
1580
def _set_last_revision(self, revision_id):
1581
self._clear_cached_state()
1583
response = self._client.call('Branch.set_last_revision',
1584
self._remote_path(), self._lock_token, self._repo_lock_token, revision_id)
1585
except errors.ErrorFromSmartServer, err:
1586
self._translate_error(err)
1587
if response != ('ok',):
1588
raise errors.UnexpectedSmartServerResponse(response)
1590
1469
@needs_write_lock
1591
1470
def set_revision_history(self, rev_history):
1592
1471
# Send just the tip revision of the history; the server will generate
1593
1472
# the full history from that. If the revision doesn't exist in this
1594
1473
# branch, NoSuchRevision will be raised.
1474
path = self.bzrdir._path_for_remote_call(self._client)
1595
1475
if rev_history == []:
1596
1476
rev_id = 'null:'
1598
1478
rev_id = rev_history[-1]
1599
self._set_last_revision(rev_id)
1479
self._clear_cached_state()
1481
response = self._client.call('Branch.set_last_revision',
1482
path, self._lock_token, self._repo_lock_token, rev_id)
1483
except errors.ErrorFromSmartServer, err:
1484
if err.error_verb == 'NoSuchRevision':
1485
raise NoSuchRevision(self, rev_id)
1487
if response != ('ok',):
1488
raise errors.UnexpectedSmartServerResponse(response)
1600
1489
self._cache_revision_history(rev_history)
1602
1491
def get_parent(self):
1607
1496
self._ensure_real()
1608
1497
return self._real_branch.set_parent(url)
1610
def set_stacked_on_url(self, stacked_location):
1611
"""Set the URL this branch is stacked against.
1613
:raises UnstackableBranchFormat: If the branch does not support
1615
:raises UnstackableRepositoryFormat: If the repository does not support
1619
return self._real_branch.set_stacked_on_url(stacked_location)
1621
1499
def sprout(self, to_bzrdir, revision_id=None):
1622
1500
# Like Branch.sprout, except that it sprouts a branch in the default
1623
1501
# format, because RemoteBranches can't be created at arbitrary URLs.
1632
1510
@needs_write_lock
1633
1511
def pull(self, source, overwrite=False, stop_revision=None,
1635
self._clear_cached_state_of_remote_branch_only()
1513
# FIXME: This asks the real branch to run the hooks, which means
1514
# they're called with the wrong target branch parameter.
1515
# The test suite specifically allows this at present but it should be
1516
# fixed. It should get a _override_hook_target branch,
1517
# as push does. -- mbp 20070405
1636
1518
self._ensure_real()
1637
return self._real_branch.pull(
1519
self._real_branch.pull(
1638
1520
source, overwrite=overwrite, stop_revision=stop_revision,
1639
_override_hook_target=self, **kwargs)
1641
1523
@needs_read_lock
1642
1524
def push(self, target, overwrite=False, stop_revision=None):
1648
1530
def is_locked(self):
1649
1531
return self._lock_count >= 1
1652
def revision_id_to_revno(self, revision_id):
1654
return self._real_branch.revision_id_to_revno(revision_id)
1656
1533
@needs_write_lock
1657
1534
def set_last_revision_info(self, revno, revision_id):
1658
1535
revision_id = ensure_null(revision_id)
1536
path = self.bzrdir._path_for_remote_call(self._client)
1660
1538
response = self._client.call('Branch.set_last_revision_info',
1661
self._remote_path(), self._lock_token, self._repo_lock_token, str(revno), revision_id)
1539
path, self._lock_token, self._repo_lock_token, str(revno), revision_id)
1662
1540
except errors.UnknownSmartMethod:
1663
1541
self._ensure_real()
1664
self._clear_cached_state_of_remote_branch_only()
1665
self._real_branch.set_last_revision_info(revno, revision_id)
1666
self._last_revision_info_cache = revno, revision_id
1542
self._clear_cached_state()
1543
return self._real_branch.set_last_revision_info(revno, revision_id)
1668
1544
except errors.ErrorFromSmartServer, err:
1669
self._translate_error(err)
1545
if err.error_verb == 'NoSuchRevision':
1546
raise NoSuchRevision(self, err.error_args[0])
1670
1548
if response == ('ok',):
1671
1549
self._clear_cached_state()
1672
self._last_revision_info_cache = revno, revision_id
1673
# Update the _real_branch's cache too.
1674
if self._real_branch is not None:
1675
cache = self._last_revision_info_cache
1676
self._real_branch._last_revision_info_cache = cache
1678
1551
raise errors.UnexpectedSmartServerResponse(response)
1681
1553
def generate_revision_history(self, revision_id, last_rev=None,
1682
1554
other_branch=None):
1683
medium = self._client._medium
1684
if not medium._is_remote_before((1, 6)):
1686
self._set_last_revision_descendant(revision_id, other_branch,
1687
allow_diverged=True, allow_overwrite_descendant=True)
1689
except errors.UnknownSmartMethod:
1690
medium._remember_remote_is_before((1, 6))
1691
self._clear_cached_state_of_remote_branch_only()
1692
1555
self._ensure_real()
1693
self._real_branch.generate_revision_history(
1556
return self._real_branch.generate_revision_history(
1694
1557
revision_id, last_rev=last_rev, other_branch=other_branch)
1702
1565
self._ensure_real()
1703
1566
return self._real_branch.set_push_location(location)
1706
1568
def update_revisions(self, other, stop_revision=None, overwrite=False,
1708
"""See Branch.update_revisions."""
1711
if stop_revision is None:
1712
stop_revision = other.last_revision()
1713
if revision.is_null(stop_revision):
1714
# if there are no commits, we're done.
1716
self.fetch(other, stop_revision)
1719
# Just unconditionally set the new revision. We don't care if
1720
# the branches have diverged.
1721
self._set_last_revision(stop_revision)
1723
medium = self._client._medium
1724
if not medium._is_remote_before((1, 6)):
1726
self._set_last_revision_descendant(stop_revision, other)
1728
except errors.UnknownSmartMethod:
1729
medium._remember_remote_is_before((1, 6))
1730
# Fallback for pre-1.6 servers: check for divergence
1731
# client-side, then do _set_last_revision.
1732
last_rev = revision.ensure_null(self.last_revision())
1734
graph = self.repository.get_graph()
1735
if self._check_if_descendant_or_diverged(
1736
stop_revision, last_rev, graph, other):
1737
# stop_revision is a descendant of last_rev, but we aren't
1738
# overwriting, so we're done.
1740
self._set_last_revision(stop_revision)
1571
return self._real_branch.update_revisions(
1572
other, stop_revision=stop_revision, overwrite=overwrite,
1745
1576
def _extract_tar(tar, to_dir):
1750
1581
for tarinfo in tar:
1751
1582
tar.extract(tarinfo, to_dir)
1754
def _translate_error(err, **context):
1755
"""Translate an ErrorFromSmartServer into a more useful error.
1757
Possible context keys:
1764
If the error from the server doesn't match a known pattern, then
1765
UnknownErrorFromSmartServer is raised.
1769
return context[name]
1770
except KeyError, keyErr:
1771
mutter('Missing key %r in context %r', keyErr.args[0], context)
1773
if err.error_verb == 'NoSuchRevision':
1774
raise NoSuchRevision(find('branch'), err.error_args[0])
1775
elif err.error_verb == 'nosuchrevision':
1776
raise NoSuchRevision(find('repository'), err.error_args[0])
1777
elif err.error_tuple == ('nobranch',):
1778
raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
1779
elif err.error_verb == 'norepository':
1780
raise errors.NoRepositoryPresent(find('bzrdir'))
1781
elif err.error_verb == 'LockContention':
1782
raise errors.LockContention('(remote lock)')
1783
elif err.error_verb == 'UnlockableTransport':
1784
raise errors.UnlockableTransport(find('bzrdir').root_transport)
1785
elif err.error_verb == 'LockFailed':
1786
raise errors.LockFailed(err.error_args[0], err.error_args[1])
1787
elif err.error_verb == 'TokenMismatch':
1788
raise errors.TokenMismatch(find('token'), '(remote token)')
1789
elif err.error_verb == 'Diverged':
1790
raise errors.DivergedBranches(find('branch'), find('other_branch'))
1791
elif err.error_verb == 'TipChangeRejected':
1792
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
1793
elif err.error_verb == 'UnstackableBranchFormat':
1794
raise errors.UnstackableBranchFormat(*err.error_args)
1795
elif err.error_verb == 'UnstackableRepositoryFormat':
1796
raise errors.UnstackableRepositoryFormat(*err.error_args)
1797
elif err.error_verb == 'NotStacked':
1798
raise errors.NotStacked(branch=find('branch'))
1799
raise errors.UnknownErrorFromSmartServer(err)