33
33
from bzrlib.branch import BranchReferenceFormat
34
34
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
35
from bzrlib.config import BranchConfig, TreeConfig
36
35
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
36
from bzrlib.errors import (
39
38
SmartProtocolError,
41
40
from bzrlib.lockable_files import LockableFiles
42
from bzrlib.pack import ContainerPushParser
43
41
from bzrlib.smart import client, vfs
44
from bzrlib.symbol_versioning import (
48
42
from bzrlib.revision import ensure_null, NULL_REVISION
49
43
from bzrlib.trace import mutter, note, warning
357
351
Used before calls to self._real_repository.
359
if not self._real_repository:
353
if self._real_repository is None:
360
354
self.bzrdir._ensure_real()
361
#self._real_repository = self.bzrdir._real_bzrdir.open_repository()
362
self._set_real_repository(self.bzrdir._real_bzrdir.open_repository())
355
self._set_real_repository(
356
self.bzrdir._real_bzrdir.open_repository())
364
358
def _translate_error(self, err, **context):
365
359
self.bzrdir._translate_error(err, repository=self, **context)
432
426
'Repository.has_revision', path, revision_id)
433
427
if response[0] not in ('yes', 'no'):
434
428
raise errors.UnexpectedSmartServerResponse(response)
435
return response[0] == 'yes'
429
if response[0] == 'yes':
431
for fallback_repo in self._fallback_repositories:
432
if fallback_repo.has_revision(revision_id):
437
436
def has_revisions(self, revision_ids):
438
437
"""See Repository.has_revisions()."""
438
# FIXME: This does many roundtrips, particularly when there are
439
# fallback repositories. -- mbp 20080905
440
441
for revision_id in revision_ids:
441
442
if self.has_revision(revision_id):
550
551
raise errors.UnexpectedSmartServerResponse(response)
552
def lock_write(self, token=None):
553
def lock_write(self, token=None, _skip_rpc=False):
553
554
if not self._lock_mode:
554
self._lock_token = self._remote_lock_write(token)
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)
555
562
# if self._lock_token is None, then this is something like packs or
556
563
# svn where we don't get to lock the repo, or a weave style repository
557
564
# where we cannot lock it over the wire and attempts to do so will
589
596
:param repository: The repository to fallback to for non-hpss
590
597
implemented operations.
599
if self._real_repository is not None:
600
raise AssertionError('_real_repository is already set')
592
601
if isinstance(repository, RemoteRepository):
593
602
raise AssertionError()
594
603
self._real_repository = repository
604
for fb in self._fallback_repositories:
605
self._real_repository.add_fallback_repository(fb)
595
606
if self._lock_mode == 'w':
596
607
# if we are already locked, the real repository must be able to
597
608
# acquire the lock with our token.
703
714
# FIXME: It ought to be possible to call this without immediately
704
715
# triggering _ensure_real. For now it's the easiest thing to do.
705
716
self._ensure_real()
706
builder = self._real_repository.get_commit_builder(branch, parents,
717
real_repo = self._real_repository
718
builder = real_repo.get_commit_builder(branch, parents,
707
719
config, timestamp=timestamp, timezone=timezone,
708
720
committer=committer, revprops=revprops, revision_id=revision_id)
714
726
:param repository: A repository.
716
if not self._format.supports_external_lookups:
717
raise errors.UnstackableRepositoryFormat(self._format, self.base)
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.
718
734
# We need to accumulate additional repositories here, to pass them in
719
735
# on various RPC's.
720
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.
722
741
def add_inventory(self, revid, inv, parents):
723
742
self._ensure_real()
1277
1296
self._repo_lock_token = None
1278
1297
self._lock_count = 0
1279
1298
self._leave_lock = False
1299
self._setup_stacking()
1301
def _setup_stacking(self):
1302
# configure stacking into the remote repository, by reading it from
1305
fallback_url = self.get_stacked_on_url()
1306
except (errors.NotStacked, errors.UnstackableBranchFormat,
1307
errors.UnstackableRepositoryFormat), e:
1309
# it's relative to this branch...
1310
fallback_url = urlutils.join(self.base, fallback_url)
1311
transports = [self.bzrdir.root_transport]
1312
if self._real_branch is not None:
1313
transports.append(self._real_branch._transport)
1314
fallback_bzrdir = BzrDir.open(fallback_url, transports)
1315
fallback_repo = fallback_bzrdir.open_repository()
1316
self.repository.add_fallback_repository(fallback_repo)
1281
1318
def _get_real_transport(self):
1282
1319
# if we try vfs access, return the real branch's vfs transport
1301
1338
'to use vfs implementation')
1302
1339
self.bzrdir._ensure_real()
1303
1340
self._real_branch = self.bzrdir._real_bzrdir.open_branch()
1304
# Give the remote repository the matching real repo.
1305
real_repo = self._real_branch.repository
1306
if isinstance(real_repo, RemoteRepository):
1307
real_repo._ensure_real()
1308
real_repo = real_repo._real_repository
1309
self.repository._set_real_repository(real_repo)
1310
# Give the branch the remote repository to let fast-pathing happen.
1341
if self.repository._real_repository is None:
1342
# Give the remote repository the matching real repo.
1343
real_repo = self._real_branch.repository
1344
if isinstance(real_repo, RemoteRepository):
1345
real_repo._ensure_real()
1346
real_repo = real_repo._real_repository
1347
self.repository._set_real_repository(real_repo)
1348
# Give the real branch the remote repository to let fast-pathing
1311
1350
self._real_branch.repository = self.repository
1312
# XXX: deal with _lock_mode == 'w'
1313
1351
if self._lock_mode == 'r':
1314
1352
self._real_branch.lock_read()
1353
elif self._lock_mode == 'w':
1354
self._real_branch.lock_write(token=self._lock_token)
1316
1356
def _translate_error(self, err, **context):
1317
1357
self.repository._translate_error(err, branch=self, **context)
1361
1401
:raises UnstackableRepositoryFormat: If the repository does not support
1365
return self._real_branch.get_stacked_on_url()
1405
response = self._client.call('Branch.get_stacked_on_url',
1406
self._remote_path())
1407
if response[0] != 'ok':
1408
raise errors.UnexpectedSmartServerResponse(response)
1410
except errors.ErrorFromSmartServer, err:
1411
# there may not be a repository yet, so we can't call through
1412
# its _translate_error
1413
_translate_error(err, branch=self)
1414
except errors.UnknownSmartMethod, err:
1416
return self._real_branch.get_stacked_on_url()
1367
1418
def lock_read(self):
1419
self.repository.lock_read()
1368
1420
if not self._lock_mode:
1369
1421
self._lock_mode = 'r'
1370
1422
self._lock_count = 1
1380
1432
branch_token = token
1381
1433
repo_token = self.repository.lock_write()
1382
1434
self.repository.unlock()
1383
path = self.bzrdir._path_for_remote_call(self._client)
1385
1436
response = self._client.call(
1386
'Branch.lock_write', path, branch_token, repo_token or '')
1437
'Branch.lock_write', self._remote_path(),
1438
branch_token, repo_token or '')
1387
1439
except errors.ErrorFromSmartServer, err:
1388
1440
self._translate_error(err, token=token)
1389
1441
if response[0] != 'ok':
1394
1446
def lock_write(self, token=None):
1395
1447
if not self._lock_mode:
1448
# Lock the branch and repo in one remote call.
1396
1449
remote_tokens = self._remote_lock_write(token)
1397
1450
self._lock_token, self._repo_lock_token = remote_tokens
1398
1451
if not self._lock_token:
1399
1452
raise SmartProtocolError('Remote server did not return a token!')
1400
# TODO: We really, really, really don't want to call _ensure_real
1401
# here, but it's the easiest way to ensure coherency between the
1402
# state of the RemoteBranch and RemoteRepository objects and the
1403
# physical locks. If we don't materialise the real objects here,
1404
# then getting everything in the right state later is complex, so
1405
# for now we just do it the lazy way.
1406
# -- Andrew Bennetts, 2007-02-22.
1453
# Tell the self.repository object that it is locked.
1454
self.repository.lock_write(
1455
self._repo_lock_token, _skip_rpc=True)
1408
1457
if self._real_branch is not None:
1409
self._real_branch.repository.lock_write(
1410
token=self._repo_lock_token)
1412
self._real_branch.lock_write(token=self._lock_token)
1414
self._real_branch.repository.unlock()
1458
self._real_branch.lock_write(token=self._lock_token)
1415
1459
if token is not None:
1416
1460
self._leave_lock = True
1418
# XXX: this case seems to be unreachable; token cannot be None.
1419
1462
self._leave_lock = False
1420
1463
self._lock_mode = 'w'
1421
1464
self._lock_count = 1
1423
1466
raise errors.ReadOnlyTransaction
1425
1468
if token is not None:
1426
# A token was given to lock_write, and we're relocking, so check
1427
# that the given token actually matches the one we already have.
1469
# A token was given to lock_write, and we're relocking, so
1470
# check that the given token actually matches the one we
1428
1472
if token != self._lock_token:
1429
1473
raise errors.TokenMismatch(token, self._lock_token)
1430
1474
self._lock_count += 1
1475
# Re-lock the repository too.
1476
self.repository.lock_write(self._repo_lock_token)
1431
1477
return self._lock_token or None
1433
1479
def _unlock(self, branch_token, repo_token):
1434
path = self.bzrdir._path_for_remote_call(self._client)
1436
response = self._client.call('Branch.unlock', path, branch_token,
1481
response = self._client.call('Branch.unlock', self._remote_path(), branch_token,
1437
1482
repo_token or '')
1438
1483
except errors.ErrorFromSmartServer, err:
1439
1484
self._translate_error(err, token=str((branch_token, repo_token)))
1442
1487
raise errors.UnexpectedSmartServerResponse(response)
1444
1489
def unlock(self):
1445
self._lock_count -= 1
1446
if not self._lock_count:
1447
self._clear_cached_state()
1448
mode = self._lock_mode
1449
self._lock_mode = None
1450
if self._real_branch is not None:
1451
if (not self._leave_lock and mode == 'w' and
1452
self._repo_lock_token):
1453
# If this RemoteBranch will remove the physical lock for the
1454
# repository, make sure the _real_branch doesn't do it
1455
# first. (Because the _real_branch's repository is set to
1456
# be the RemoteRepository.)
1457
self._real_branch.repository.leave_lock_in_place()
1458
self._real_branch.unlock()
1460
# Only write-locked branched need to make a remote method call
1461
# to perfom the unlock.
1463
if not self._lock_token:
1464
raise AssertionError('Locked, but no token!')
1465
branch_token = self._lock_token
1466
repo_token = self._repo_lock_token
1467
self._lock_token = None
1468
self._repo_lock_token = None
1469
if not self._leave_lock:
1470
self._unlock(branch_token, repo_token)
1491
self._lock_count -= 1
1492
if not self._lock_count:
1493
self._clear_cached_state()
1494
mode = self._lock_mode
1495
self._lock_mode = None
1496
if self._real_branch is not None:
1497
if (not self._leave_lock and mode == 'w' and
1498
self._repo_lock_token):
1499
# If this RemoteBranch will remove the physical lock
1500
# for the repository, make sure the _real_branch
1501
# doesn't do it first. (Because the _real_branch's
1502
# repository is set to be the RemoteRepository.)
1503
self._real_branch.repository.leave_lock_in_place()
1504
self._real_branch.unlock()
1506
# Only write-locked branched need to make a remote method
1507
# call to perfom the unlock.
1509
if not self._lock_token:
1510
raise AssertionError('Locked, but no token!')
1511
branch_token = self._lock_token
1512
repo_token = self._repo_lock_token
1513
self._lock_token = None
1514
self._repo_lock_token = None
1515
if not self._leave_lock:
1516
self._unlock(branch_token, repo_token)
1518
self.repository.unlock()
1472
1520
def break_lock(self):
1473
1521
self._ensure_real()
1484
1532
self._leave_lock = False
1486
1534
def _last_revision_info(self):
1487
path = self.bzrdir._path_for_remote_call(self._client)
1488
response = self._client.call('Branch.last_revision_info', path)
1535
response = self._client.call('Branch.last_revision_info', self._remote_path())
1489
1536
if response[0] != 'ok':
1490
1537
raise SmartProtocolError('unexpected response code %s' % (response,))
1491
1538
revno = int(response[1])
1495
1542
def _gen_revision_history(self):
1496
1543
"""See Branch._gen_revision_history()."""
1497
path = self.bzrdir._path_for_remote_call(self._client)
1498
1544
response_tuple, response_handler = self._client.call_expecting_body(
1499
'Branch.revision_history', path)
1545
'Branch.revision_history', self._remote_path())
1500
1546
if response_tuple[0] != 'ok':
1501
1547
raise errors.UnexpectedSmartServerResponse(response_tuple)
1502
1548
result = response_handler.read_body_bytes().split('\x00')
1553
def _remote_path(self):
1554
return self.bzrdir._path_for_remote_call(self._client)
1507
1556
def _set_last_revision_descendant(self, revision_id, other_branch,
1508
1557
allow_diverged=False, allow_overwrite_descendant=False):
1509
path = self.bzrdir._path_for_remote_call(self._client)
1511
1559
response = self._client.call('Branch.set_last_revision_ex',
1512
path, self._lock_token, self._repo_lock_token, revision_id,
1560
self._remote_path(), self._lock_token, self._repo_lock_token, revision_id,
1513
1561
int(allow_diverged), int(allow_overwrite_descendant))
1514
1562
except errors.ErrorFromSmartServer, err:
1515
1563
self._translate_error(err, other_branch=other_branch)
1518
1566
raise errors.UnexpectedSmartServerResponse(response)
1519
1567
new_revno, new_revision_id = response[1:]
1520
1568
self._last_revision_info_cache = new_revno, new_revision_id
1521
self._real_branch._last_revision_info_cache = new_revno, new_revision_id
1569
if self._real_branch is not None:
1570
cache = new_revno, new_revision_id
1571
self._real_branch._last_revision_info_cache = cache
1523
1573
def _set_last_revision(self, revision_id):
1524
path = self.bzrdir._path_for_remote_call(self._client)
1525
1574
self._clear_cached_state()
1527
1576
response = self._client.call('Branch.set_last_revision',
1528
path, self._lock_token, self._repo_lock_token, revision_id)
1577
self._remote_path(), self._lock_token, self._repo_lock_token, revision_id)
1529
1578
except errors.ErrorFromSmartServer, err:
1530
1579
self._translate_error(err)
1531
1580
if response != ('ok',):
1600
1649
@needs_write_lock
1601
1650
def set_last_revision_info(self, revno, revision_id):
1602
1651
revision_id = ensure_null(revision_id)
1603
path = self.bzrdir._path_for_remote_call(self._client)
1605
1653
response = self._client.call('Branch.set_last_revision_info',
1606
path, self._lock_token, self._repo_lock_token, str(revno), revision_id)
1654
self._remote_path(), self._lock_token, self._repo_lock_token, str(revno), revision_id)
1607
1655
except errors.UnknownSmartMethod:
1608
1656
self._ensure_real()
1609
1657
self._clear_cached_state_of_remote_branch_only()
1732
1783
raise errors.DivergedBranches(find('branch'), find('other_branch'))
1733
1784
elif err.error_verb == 'TipChangeRejected':
1734
1785
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
1786
elif err.error_verb == 'UnstackableBranchFormat':
1787
raise errors.UnstackableBranchFormat(*err.error_args)
1788
elif err.error_verb == 'UnstackableRepositoryFormat':
1789
raise errors.UnstackableRepositoryFormat(*err.error_args)
1790
elif err.error_verb == 'NotStacked':
1791
raise errors.NotStacked(branch=find('branch'))
1792
raise errors.UnknownErrorFromSmartServer(err)