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
46
class _RpcHelper(object):
47
"""Mixin class that helps with issuing RPCs."""
49
def _call(self, method, *args, **err_context):
51
return self._client.call(method, *args)
52
except errors.ErrorFromSmartServer, err:
53
self._translate_error(err, **err_context)
55
def _call_expecting_body(self, method, *args, **err_context):
57
return self._client.call_expecting_body(method, *args)
58
except errors.ErrorFromSmartServer, err:
59
self._translate_error(err, **err_context)
61
def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
64
return self._client.call_with_body_bytes_expecting_body(
65
method, args, body_bytes)
66
except errors.ErrorFromSmartServer, err:
67
self._translate_error(err, **err_context)
69
52
# Note: RemoteBzrDirFormat is in bzrdir.py
71
class RemoteBzrDir(BzrDir, _RpcHelper):
54
class RemoteBzrDir(BzrDir):
72
55
"""Control directory on a remote server, accessed via bzr:// or similar."""
74
57
def __init__(self, transport, _client=None):
177
158
path = self._path_for_remote_call(self._client)
178
159
verb = 'BzrDir.find_repositoryV2'
180
response = self._call(verb, path)
181
except errors.UnknownSmartMethod:
182
verb = 'BzrDir.find_repository'
183
response = self._call(verb, path)
162
response = self._client.call(verb, path)
163
except errors.UnknownSmartMethod:
164
verb = 'BzrDir.find_repository'
165
response = self._client.call(verb, path)
166
except errors.ErrorFromSmartServer, err:
167
if err.error_verb == 'norepository':
168
raise errors.NoRepositoryPresent(self)
184
170
if response[0] != 'ok':
185
171
raise errors.UnexpectedSmartServerResponse(response)
186
172
if verb == 'BzrDir.find_repository':
231
215
def needs_format_conversion(self, format=None):
232
216
"""Upgrading of remote bzrdirs is not supported yet."""
234
symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
235
% 'needs_format_conversion(format=None)')
238
def clone(self, url, revision_id=None, force_new_repo=False,
239
preserve_stacking=False):
219
def clone(self, url, revision_id=None, force_new_repo=False):
240
220
self._ensure_real()
241
221
return self._real_bzrdir.clone(url, revision_id=revision_id,
242
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
244
def get_config(self):
246
return self._real_bzrdir.get_config()
222
force_new_repo=force_new_repo)
249
225
class RemoteRepositoryFormat(repository.RepositoryFormat):
334
308
self._reconcile_fixes_text_parents = False
335
309
self._reconcile_backsup_inventory = False
336
310
self.base = self.bzrdir.transport.base
337
# Additional places to query for data.
338
self._fallback_repositories = []
340
312
def __str__(self):
341
313
return "%s(%s)" % (self.__class__.__name__, self.base)
343
315
__repr__ = __str__
345
def abort_write_group(self, suppress_errors=False):
317
def abort_write_group(self):
346
318
"""Complete a write group on the decorated repository.
348
320
Smart methods peform operations in a single step so this api
349
321
is not really applicable except as a compatibility thunk
350
322
for older plugins that don't use e.g. the CommitBuilder
353
:param suppress_errors: see Repository.abort_write_group.
355
325
self._ensure_real()
356
return self._real_repository.abort_write_group(
357
suppress_errors=suppress_errors)
326
return self._real_repository.abort_write_group()
359
328
def commit_write_group(self):
360
329
"""Complete a write group on the decorated repository.
373
342
Used before calls to self._real_repository.
375
if self._real_repository is None:
344
if not self._real_repository:
376
345
self.bzrdir._ensure_real()
377
self._set_real_repository(
378
self.bzrdir._real_bzrdir.open_repository())
380
def _translate_error(self, err, **context):
381
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())
383
349
def find_text_key_references(self):
384
350
"""Find the text key references within the repository.
420
386
path = self.bzrdir._path_for_remote_call(self._client)
421
response = self._call_expecting_body(
422
'Repository.get_revision_graph', path, revision_id)
388
response = self._client.call_expecting_body(
389
'Repository.get_revision_graph', path, revision_id)
390
except errors.ErrorFromSmartServer, err:
391
if err.error_verb == 'nosuchrevision':
392
raise NoSuchRevision(self, revision_id)
423
394
response_tuple, response_handler = response
424
395
if response_tuple[0] != 'ok':
425
396
raise errors.UnexpectedSmartServerResponse(response_tuple)
441
412
# The null revision is always present.
443
414
path = self.bzrdir._path_for_remote_call(self._client)
444
response = self._call('Repository.has_revision', path, revision_id)
415
response = self._client.call(
416
'Repository.has_revision', path, revision_id)
445
417
if response[0] not in ('yes', 'no'):
446
418
raise errors.UnexpectedSmartServerResponse(response)
447
if response[0] == 'yes':
449
for fallback_repo in self._fallback_repositories:
450
if fallback_repo.has_revision(revision_id):
419
return response[0] == 'yes'
454
421
def has_revisions(self, revision_ids):
455
422
"""See Repository.has_revisions()."""
456
# FIXME: This does many roundtrips, particularly when there are
457
# fallback repositories. -- mbp 20080905
459
424
for revision_id in revision_ids:
460
425
if self.has_revision(revision_id):
464
429
def has_same_location(self, other):
465
430
return (self.__class__ == other.__class__ and
466
431
self.bzrdir.transport.base == other.bzrdir.transport.base)
468
433
def get_graph(self, other_repository=None):
469
434
"""Return the graph for this repository format"""
470
parents_provider = self._make_parents_provider(other_repository)
435
parents_provider = self
436
if (other_repository is not None and
437
other_repository.bzrdir.transport.base !=
438
self.bzrdir.transport.base):
439
parents_provider = graph._StackedParentsProvider(
440
[parents_provider, other_repository._make_parents_provider()])
471
441
return graph.Graph(parents_provider)
473
443
def gather_stats(self, revid=None, committers=None):
550
522
path = self.bzrdir._path_for_remote_call(self._client)
551
523
if token is None:
553
err_context = {'token': token}
554
response = self._call('Repository.lock_write', path, token,
526
response = self._client.call('Repository.lock_write', path, token)
527
except errors.ErrorFromSmartServer, err:
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])
556
536
if response[0] == 'ok':
557
537
ok, token = response
560
540
raise errors.UnexpectedSmartServerResponse(response)
562
def lock_write(self, token=None, _skip_rpc=False):
542
def lock_write(self, token=None):
563
543
if not self._lock_mode:
565
if self._lock_token is not None:
566
if token != self._lock_token:
567
raise errors.TokenMismatch(token, self._lock_token)
568
self._lock_token = token
570
self._lock_token = self._remote_lock_write(token)
544
self._lock_token = self._remote_lock_write(token)
571
545
# if self._lock_token is None, then this is something like packs or
572
546
# svn where we don't get to lock the repo, or a weave style repository
573
547
# where we cannot lock it over the wire and attempts to do so will
718
695
# FIXME: It ought to be possible to call this without immediately
719
696
# triggering _ensure_real. For now it's the easiest thing to do.
720
697
self._ensure_real()
721
real_repo = self._real_repository
722
builder = real_repo.get_commit_builder(branch, parents,
698
builder = self._real_repository.get_commit_builder(branch, parents,
723
699
config, timestamp=timestamp, timezone=timezone,
724
700
committer=committer, revprops=revprops, revision_id=revision_id)
727
def add_fallback_repository(self, repository):
728
"""Add a repository to use for looking up data not held locally.
730
:param repository: A repository.
732
# XXX: At the moment the RemoteRepository will allow fallbacks
733
# unconditionally - however, a _real_repository will usually exist,
734
# and may raise an error if it's not accommodated by the underlying
735
# format. Eventually we should check when opening the repository
736
# whether it's willing to allow them or not.
738
# We need to accumulate additional repositories here, to pass them in
740
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.
745
703
def add_inventory(self, revid, inv, parents):
746
704
self._ensure_real()
747
705
return self._real_repository.add_inventory(revid, inv, parents)
749
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
752
return self._real_repository.add_inventory_by_delta(basis_revision_id,
753
delta, new_revision_id, parents)
755
707
def add_revision(self, rev_id, rev, inv=None, config=None):
756
708
self._ensure_real()
757
709
return self._real_repository.add_revision(
819
769
not revision.is_null(revision_id)):
820
770
self.get_revision(revision_id)
822
inter = repository.InterRepository.get(source, self)
824
return inter.fetch(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts)
825
except NotImplementedError:
826
raise errors.IncompatibleRepositories(source, self)
773
return self._real_repository.fetch(
774
source, revision_id=revision_id, pb=pb)
828
776
def create_bundle(self, target, base, fileobj, format=None):
829
777
self._ensure_real()
849
797
self._ensure_real()
850
798
return self._real_repository.iter_files_bytes(desired_files)
853
def _fetch_order(self):
854
"""Decorate the real repository for now.
856
In the long term getting this back from the remote repository as part
857
of open would be more efficient.
860
return self._real_repository._fetch_order
863
def _fetch_uses_deltas(self):
864
"""Decorate the real repository for now.
866
In the long term getting this back from the remote repository as part
867
of open would be more efficient.
870
return self._real_repository._fetch_uses_deltas
873
def _fetch_reconcile(self):
874
"""Decorate the real repository for now.
876
In the long term getting this back from the remote repository as part
877
of open would be more efficient.
880
return self._real_repository._fetch_reconcile
882
def get_parent_map(self, revision_ids):
800
def get_parent_map(self, keys):
883
801
"""See bzrlib.Graph.get_parent_map()."""
884
return self._make_parents_provider().get_parent_map(revision_ids)
802
# Hack to build up the caching logic.
803
ancestry = self._parents_map
805
# Repository is not locked, so there's no cache.
806
missing_revisions = set(keys)
809
missing_revisions = set(key for key in keys if key not in ancestry)
810
if missing_revisions:
811
parent_map = self._get_parent_map(missing_revisions)
812
if 'hpss' in debug.debug_flags:
813
mutter('retransmitted revisions: %d of %d',
814
len(set(ancestry).intersection(parent_map)),
816
ancestry.update(parent_map)
817
present_keys = [k for k in keys if k in ancestry]
818
if 'hpss' in debug.debug_flags:
819
if self._requested_parents is not None and len(ancestry) != 0:
820
self._requested_parents.update(present_keys)
821
mutter('Current RemoteRepository graph hit rate: %d%%',
822
100.0 * len(self._requested_parents) / len(ancestry))
823
return dict((k, ancestry[k]) for k in present_keys)
886
def _get_parent_map_rpc(self, keys):
825
def _get_parent_map(self, keys):
887
826
"""Helper for get_parent_map that performs the RPC."""
888
827
medium = self._client._medium
889
828
if medium._is_remote_before((1, 2)):
1202
1137
count = str(recipe[2])
1203
1138
return '\n'.join((start_keys, stop_keys, count))
1206
path = self.bzrdir._path_for_remote_call(self._client)
1208
response = self._call('PackRepository.autopack', path)
1209
except errors.UnknownSmartMethod:
1211
self._real_repository._pack_collection.autopack()
1213
if self._real_repository is not None:
1214
# Reset the real repository's cache of pack names.
1215
# XXX: At some point we may be able to skip this and just rely on
1216
# the automatic retry logic to do the right thing, but for now we
1217
# err on the side of being correct rather than being optimal.
1218
self._real_repository._pack_collection.reload_pack_names()
1219
if response[0] != 'ok':
1220
raise errors.UnexpectedSmartServerResponse(response)
1223
1141
class RemoteBranchLockableFiles(LockableFiles):
1224
1142
"""A 'LockableFiles' implementation that talks to a smart server.
1318
1229
self._repo_lock_token = None
1319
1230
self._lock_count = 0
1320
1231
self._leave_lock = False
1321
# The base class init is not called, so we duplicate this:
1322
hooks = branch.Branch.hooks['open']
1325
self._setup_stacking()
1327
def _setup_stacking(self):
1328
# configure stacking into the remote repository, by reading it from
1331
fallback_url = self.get_stacked_on_url()
1332
except (errors.NotStacked, errors.UnstackableBranchFormat,
1333
errors.UnstackableRepositoryFormat), e:
1335
# it's relative to this branch...
1336
fallback_url = urlutils.join(self.base, fallback_url)
1337
transports = [self.bzrdir.root_transport]
1338
if self._real_branch is not None:
1339
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)
1344
1233
def _get_real_transport(self):
1345
1234
# if we try vfs access, return the real branch's vfs transport
1364
1253
'to use vfs implementation')
1365
1254
self.bzrdir._ensure_real()
1366
1255
self._real_branch = self.bzrdir._real_bzrdir.open_branch()
1367
if self.repository._real_repository is None:
1368
# Give the remote repository the matching real repo.
1369
real_repo = self._real_branch.repository
1370
if isinstance(real_repo, RemoteRepository):
1371
real_repo._ensure_real()
1372
real_repo = real_repo._real_repository
1373
self.repository._set_real_repository(real_repo)
1374
# Give the real branch the remote repository to let fast-pathing
1256
# Give the remote repository the matching real repo.
1257
real_repo = self._real_branch.repository
1258
if isinstance(real_repo, RemoteRepository):
1259
real_repo._ensure_real()
1260
real_repo = real_repo._real_repository
1261
self.repository._set_real_repository(real_repo)
1262
# Give the branch the remote repository to let fast-pathing happen.
1376
1263
self._real_branch.repository = self.repository
1264
# XXX: deal with _lock_mode == 'w'
1377
1265
if self._lock_mode == 'r':
1378
1266
self._real_branch.lock_read()
1379
elif self._lock_mode == 'w':
1380
self._real_branch.lock_write(token=self._lock_token)
1382
def _translate_error(self, err, **context):
1383
self.repository._translate_error(err, branch=self, **context)
1385
1268
def _clear_cached_state(self):
1386
1269
super(RemoteBranch, self)._clear_cached_state()
1418
1301
self._ensure_real()
1419
1302
return self._real_branch.get_physical_lock_status()
1421
def get_stacked_on_url(self):
1422
"""Get the URL this branch is stacked against.
1424
:raises NotStacked: If the branch is not stacked.
1425
:raises UnstackableBranchFormat: If the branch does not support
1427
:raises UnstackableRepositoryFormat: If the repository does not support
1431
# there may not be a repository yet, so we can't use
1432
# self._translate_error, so we can't use self._call either.
1433
response = self._client.call('Branch.get_stacked_on_url',
1434
self._remote_path())
1435
except errors.ErrorFromSmartServer, err:
1436
# there may not be a repository yet, so we can't call through
1437
# its _translate_error
1438
_translate_error(err, branch=self)
1439
except errors.UnknownSmartMethod, err:
1441
return self._real_branch.get_stacked_on_url()
1442
if response[0] != 'ok':
1443
raise errors.UnexpectedSmartServerResponse(response)
1446
1304
def lock_read(self):
1447
self.repository.lock_read()
1448
1305
if not self._lock_mode:
1449
1306
self._lock_mode = 'r'
1450
1307
self._lock_count = 1
1460
1317
branch_token = token
1461
1318
repo_token = self.repository.lock_write()
1462
1319
self.repository.unlock()
1463
err_context = {'token': token}
1464
response = self._call(
1465
'Branch.lock_write', self._remote_path(), branch_token,
1466
repo_token or '', **err_context)
1320
path = self.bzrdir._path_for_remote_call(self._client)
1322
response = self._client.call(
1323
'Branch.lock_write', path, branch_token, repo_token or '')
1324
except errors.ErrorFromSmartServer, err:
1325
if err.error_verb == 'LockContention':
1326
raise errors.LockContention('(remote lock)')
1327
elif err.error_verb == 'TokenMismatch':
1328
raise errors.TokenMismatch(token, '(remote token)')
1329
elif err.error_verb == 'UnlockableTransport':
1330
raise errors.UnlockableTransport(self.bzrdir.root_transport)
1331
elif err.error_verb == 'ReadOnlyError':
1332
raise errors.ReadOnlyError(self)
1333
elif err.error_verb == 'LockFailed':
1334
raise errors.LockFailed(err.error_args[0], err.error_args[1])
1467
1336
if response[0] != 'ok':
1468
1337
raise errors.UnexpectedSmartServerResponse(response)
1469
1338
ok, branch_token, repo_token = response
1472
1341
def lock_write(self, token=None):
1473
1342
if not self._lock_mode:
1474
# Lock the branch and repo in one remote call.
1475
1343
remote_tokens = self._remote_lock_write(token)
1476
1344
self._lock_token, self._repo_lock_token = remote_tokens
1477
1345
if not self._lock_token:
1478
1346
raise SmartProtocolError('Remote server did not return a token!')
1479
# Tell the self.repository object that it is locked.
1480
self.repository.lock_write(
1481
self._repo_lock_token, _skip_rpc=True)
1347
# TODO: We really, really, really don't want to call _ensure_real
1348
# here, but it's the easiest way to ensure coherency between the
1349
# state of the RemoteBranch and RemoteRepository objects and the
1350
# physical locks. If we don't materialise the real objects here,
1351
# then getting everything in the right state later is complex, so
1352
# for now we just do it the lazy way.
1353
# -- Andrew Bennetts, 2007-02-22.
1483
1355
if self._real_branch is not None:
1484
self._real_branch.lock_write(token=self._lock_token)
1356
self._real_branch.repository.lock_write(
1357
token=self._repo_lock_token)
1359
self._real_branch.lock_write(token=self._lock_token)
1361
self._real_branch.repository.unlock()
1485
1362
if token is not None:
1486
1363
self._leave_lock = True
1365
# XXX: this case seems to be unreachable; token cannot be None.
1488
1366
self._leave_lock = False
1489
1367
self._lock_mode = 'w'
1490
1368
self._lock_count = 1
1492
1370
raise errors.ReadOnlyTransaction
1494
1372
if token is not None:
1495
# A token was given to lock_write, and we're relocking, so
1496
# check that the given token actually matches the one we
1373
# A token was given to lock_write, and we're relocking, so check
1374
# that the given token actually matches the one we already have.
1498
1375
if token != self._lock_token:
1499
1376
raise errors.TokenMismatch(token, self._lock_token)
1500
1377
self._lock_count += 1
1501
# Re-lock the repository too.
1502
self.repository.lock_write(self._repo_lock_token)
1503
1378
return self._lock_token or None
1505
1380
def _unlock(self, branch_token, repo_token):
1506
err_context = {'token': str((branch_token, repo_token))}
1507
response = self._call(
1508
'Branch.unlock', self._remote_path(), branch_token,
1509
repo_token or '', **err_context)
1381
path = self.bzrdir._path_for_remote_call(self._client)
1383
response = self._client.call('Branch.unlock', path, branch_token,
1385
except errors.ErrorFromSmartServer, err:
1386
if err.error_verb == 'TokenMismatch':
1387
raise errors.TokenMismatch(
1388
str((branch_token, repo_token)), '(remote tokens)')
1510
1390
if response == ('ok',):
1512
1392
raise errors.UnexpectedSmartServerResponse(response)
1514
1394
def unlock(self):
1516
self._lock_count -= 1
1517
if not self._lock_count:
1518
self._clear_cached_state()
1519
mode = self._lock_mode
1520
self._lock_mode = None
1521
if self._real_branch is not None:
1522
if (not self._leave_lock and mode == 'w' and
1523
self._repo_lock_token):
1524
# If this RemoteBranch will remove the physical lock
1525
# for the repository, make sure the _real_branch
1526
# doesn't do it first. (Because the _real_branch's
1527
# repository is set to be the RemoteRepository.)
1528
self._real_branch.repository.leave_lock_in_place()
1529
self._real_branch.unlock()
1531
# Only write-locked branched need to make a remote method
1532
# call to perfom the unlock.
1534
if not self._lock_token:
1535
raise AssertionError('Locked, but no token!')
1536
branch_token = self._lock_token
1537
repo_token = self._repo_lock_token
1538
self._lock_token = None
1539
self._repo_lock_token = None
1540
if not self._leave_lock:
1541
self._unlock(branch_token, repo_token)
1543
self.repository.unlock()
1395
self._lock_count -= 1
1396
if not self._lock_count:
1397
self._clear_cached_state()
1398
mode = self._lock_mode
1399
self._lock_mode = None
1400
if self._real_branch is not None:
1401
if (not self._leave_lock and mode == 'w' and
1402
self._repo_lock_token):
1403
# If this RemoteBranch will remove the physical lock for the
1404
# repository, make sure the _real_branch doesn't do it
1405
# first. (Because the _real_branch's repository is set to
1406
# be the RemoteRepository.)
1407
self._real_branch.repository.leave_lock_in_place()
1408
self._real_branch.unlock()
1410
# Only write-locked branched need to make a remote method call
1411
# to perfom the unlock.
1413
if not self._lock_token:
1414
raise AssertionError('Locked, but no token!')
1415
branch_token = self._lock_token
1416
repo_token = self._repo_lock_token
1417
self._lock_token = None
1418
self._repo_lock_token = None
1419
if not self._leave_lock:
1420
self._unlock(branch_token, repo_token)
1545
1422
def break_lock(self):
1546
1423
self._ensure_real()
1578
def _remote_path(self):
1579
return self.bzrdir._path_for_remote_call(self._client)
1581
1457
def _set_last_revision_descendant(self, revision_id, other_branch,
1582
1458
allow_diverged=False, allow_overwrite_descendant=False):
1583
err_context = {'other_branch': other_branch}
1584
response = self._call('Branch.set_last_revision_ex',
1585
self._remote_path(), self._lock_token, self._repo_lock_token,
1586
revision_id, int(allow_diverged), int(allow_overwrite_descendant),
1459
path = self.bzrdir._path_for_remote_call(self._client)
1461
response = self._client.call('Branch.set_last_revision_ex',
1462
path, self._lock_token, self._repo_lock_token, revision_id,
1463
int(allow_diverged), int(allow_overwrite_descendant))
1464
except errors.ErrorFromSmartServer, err:
1465
if err.error_verb == 'NoSuchRevision':
1466
raise NoSuchRevision(self, revision_id)
1467
elif err.error_verb == 'Diverged':
1468
raise errors.DivergedBranches(self, other_branch)
1588
1470
self._clear_cached_state()
1589
1471
if len(response) != 3 and response[0] != 'ok':
1590
1472
raise errors.UnexpectedSmartServerResponse(response)
1591
1473
new_revno, new_revision_id = response[1:]
1592
1474
self._last_revision_info_cache = new_revno, new_revision_id
1593
if self._real_branch is not None:
1594
cache = new_revno, new_revision_id
1595
self._real_branch._last_revision_info_cache = cache
1475
self._real_branch._last_revision_info_cache = new_revno, new_revision_id
1597
1477
def _set_last_revision(self, revision_id):
1478
path = self.bzrdir._path_for_remote_call(self._client)
1598
1479
self._clear_cached_state()
1599
response = self._call('Branch.set_last_revision',
1600
self._remote_path(), self._lock_token, self._repo_lock_token,
1481
response = self._client.call('Branch.set_last_revision',
1482
path, self._lock_token, self._repo_lock_token, revision_id)
1483
except errors.ErrorFromSmartServer, err:
1484
if err.error_verb == 'NoSuchRevision':
1485
raise NoSuchRevision(self, revision_id)
1602
1487
if response != ('ok',):
1603
1488
raise errors.UnexpectedSmartServerResponse(response)
1622
1507
self._ensure_real()
1623
1508
return self._real_branch.set_parent(url)
1625
def set_stacked_on_url(self, stacked_location):
1626
"""Set the URL this branch is stacked against.
1628
:raises UnstackableBranchFormat: If the branch does not support
1630
:raises UnstackableRepositoryFormat: If the repository does not support
1510
def sprout(self, to_bzrdir, revision_id=None):
1511
# Like Branch.sprout, except that it sprouts a branch in the default
1512
# format, because RemoteBranches can't be created at arbitrary URLs.
1513
# XXX: if to_bzrdir is a RemoteBranch, this should perhaps do
1514
# to_bzrdir.create_branch...
1633
1515
self._ensure_real()
1634
return self._real_branch.set_stacked_on_url(stacked_location)
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...
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)
1651
result = branch.Branch.sprout(
1652
self, to_bzrdir, revision_id=revision_id)
1516
result = self._real_branch._format.initialize(to_bzrdir)
1517
self.copy_content_into(result, revision_id=revision_id)
1518
result.set_parent(self.bzrdir.root_transport.base)
1655
1521
@needs_write_lock
1671
1537
def is_locked(self):
1672
1538
return self._lock_count >= 1
1675
def revision_id_to_revno(self, revision_id):
1677
return self._real_branch.revision_id_to_revno(revision_id)
1679
1540
@needs_write_lock
1680
1541
def set_last_revision_info(self, revno, revision_id):
1681
1542
revision_id = ensure_null(revision_id)
1543
path = self.bzrdir._path_for_remote_call(self._client)
1683
response = self._call('Branch.set_last_revision_info',
1684
self._remote_path(), self._lock_token, self._repo_lock_token,
1685
str(revno), revision_id)
1545
response = self._client.call('Branch.set_last_revision_info',
1546
path, self._lock_token, self._repo_lock_token, str(revno), revision_id)
1686
1547
except errors.UnknownSmartMethod:
1687
1548
self._ensure_real()
1688
1549
self._clear_cached_state_of_remote_branch_only()
1689
1550
self._real_branch.set_last_revision_info(revno, revision_id)
1690
1551
self._last_revision_info_cache = revno, revision_id
1553
except errors.ErrorFromSmartServer, err:
1554
if err.error_verb == 'NoSuchRevision':
1555
raise NoSuchRevision(self, err.error_args[0])
1692
1557
if response == ('ok',):
1693
1558
self._clear_cached_state()
1694
1559
self._last_revision_info_cache = revno, revision_id
1772
1637
for tarinfo in tar:
1773
1638
tar.extract(tarinfo, to_dir)
1776
def _translate_error(err, **context):
1777
"""Translate an ErrorFromSmartServer into a more useful error.
1779
Possible context keys:
1787
If the error from the server doesn't match a known pattern, then
1788
UnknownErrorFromSmartServer is raised.
1792
return context[name]
1793
except KeyError, key_err:
1794
mutter('Missing key %r in context %r', key_err.args[0], context)
1797
"""Get the path from the context if present, otherwise use first error
1801
return context['path']
1802
except KeyError, key_err:
1804
return err.error_args[0]
1805
except IndexError, idx_err:
1807
'Missing key %r in context %r', key_err.args[0], context)
1810
if err.error_verb == 'NoSuchRevision':
1811
raise NoSuchRevision(find('branch'), err.error_args[0])
1812
elif err.error_verb == 'nosuchrevision':
1813
raise NoSuchRevision(find('repository'), err.error_args[0])
1814
elif err.error_tuple == ('nobranch',):
1815
raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
1816
elif err.error_verb == 'norepository':
1817
raise errors.NoRepositoryPresent(find('bzrdir'))
1818
elif err.error_verb == 'LockContention':
1819
raise errors.LockContention('(remote lock)')
1820
elif err.error_verb == 'UnlockableTransport':
1821
raise errors.UnlockableTransport(find('bzrdir').root_transport)
1822
elif err.error_verb == 'LockFailed':
1823
raise errors.LockFailed(err.error_args[0], err.error_args[1])
1824
elif err.error_verb == 'TokenMismatch':
1825
raise errors.TokenMismatch(find('token'), '(remote token)')
1826
elif err.error_verb == 'Diverged':
1827
raise errors.DivergedBranches(find('branch'), find('other_branch'))
1828
elif err.error_verb == 'TipChangeRejected':
1829
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
1830
elif err.error_verb == 'UnstackableBranchFormat':
1831
raise errors.UnstackableBranchFormat(*err.error_args)
1832
elif err.error_verb == 'UnstackableRepositoryFormat':
1833
raise errors.UnstackableRepositoryFormat(*err.error_args)
1834
elif err.error_verb == 'NotStacked':
1835
raise errors.NotStacked(branch=find('branch'))
1836
elif err.error_verb == 'PermissionDenied':
1838
if len(err.error_args) >= 2:
1839
extra = err.error_args[1]
1842
raise errors.PermissionDenied(path, extra=extra)
1843
elif err.error_verb == 'ReadError':
1845
raise errors.ReadError(path)
1846
elif err.error_verb == 'NoSuchFile':
1848
raise errors.NoSuchFile(path)
1849
elif err.error_verb == 'FileExists':
1850
raise errors.FileExists(err.error_args[0])
1851
elif err.error_verb == 'DirectoryNotEmpty':
1852
raise errors.DirectoryNotEmpty(err.error_args[0])
1853
elif err.error_verb == 'ShortReadvError':
1854
args = err.error_args
1855
raise errors.ShortReadvError(
1856
args[0], int(args[1]), int(args[2]), int(args[3]))
1857
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
1858
encoding = str(err.error_args[0]) # encoding must always be a string
1859
val = err.error_args[1]
1860
start = int(err.error_args[2])
1861
end = int(err.error_args[3])
1862
reason = str(err.error_args[4]) # reason must always be a string
1863
if val.startswith('u:'):
1864
val = val[2:].decode('utf-8')
1865
elif val.startswith('s:'):
1866
val = val[2:].decode('base64')
1867
if err.error_verb == 'UnicodeDecodeError':
1868
raise UnicodeDecodeError(encoding, val, start, end, reason)
1869
elif err.error_verb == 'UnicodeEncodeError':
1870
raise UnicodeEncodeError(encoding, val, start, end, reason)
1871
elif err.error_verb == 'ReadOnlyError':
1872
raise errors.TransportNotPossible('readonly transport')
1873
raise errors.UnknownErrorFromSmartServer(err)