~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: John Arbash Meinel
  • Date: 2008-09-24 12:53:22 UTC
  • mfrom: (3697.7.3 1.7)
  • mto: (3697.7.4 1.7)
  • mto: This revision was merged to the branch mainline in revision 3748.
  • Revision ID: john@arbash-meinel.com-20080924125322-p1i7pnr0m49v1hze
Bring in the 1.7 branch

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
# across to run on the server.
19
19
 
20
20
import bz2
21
 
from cStringIO import StringIO
22
21
 
23
22
from bzrlib import (
24
23
    branch,
29
28
    repository,
30
29
    revision,
31
30
    symbol_versioning,
 
31
    urlutils,
32
32
)
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 (
38
37
    NoSuchRevision,
39
38
    SmartProtocolError,
40
39
    )
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 (
45
 
    deprecated_in,
46
 
    deprecated_method,
47
 
    )
48
42
from bzrlib.revision import ensure_null, NULL_REVISION
49
43
from bzrlib.trace import mutter, note, warning
50
44
 
356
350
 
357
351
        Used before calls to self._real_repository.
358
352
        """
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())
363
357
 
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':
 
430
            return True
 
431
        for fallback_repo in self._fallback_repositories:
 
432
            if fallback_repo.has_revision(revision_id):
 
433
                return True
 
434
        return False
436
435
 
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
439
440
        result = set()
440
441
        for revision_id in revision_ids:
441
442
            if self.has_revision(revision_id):
549
550
        else:
550
551
            raise errors.UnexpectedSmartServerResponse(response)
551
552
 
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)
 
555
            if _skip_rpc:
 
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
 
560
            else:
 
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.
591
598
        """
 
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)
709
721
        return builder
713
725
        
714
726
        :param repository: A repository.
715
727
        """
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.
 
733
        #
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.
 
739
        self._ensure_real()
721
740
 
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()
 
1300
 
 
1301
    def _setup_stacking(self):
 
1302
        # configure stacking into the remote repository, by reading it from
 
1303
        # the vfs branch.
 
1304
        try:
 
1305
            fallback_url = self.get_stacked_on_url()
 
1306
        except (errors.NotStacked, errors.UnstackableBranchFormat,
 
1307
            errors.UnstackableRepositoryFormat), e:
 
1308
            return
 
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)
1280
1317
 
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
 
1349
            # happen.
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)
1315
1355
 
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
1362
1402
            stacking.
1363
1403
        """
1364
 
        self._ensure_real()
1365
 
        return self._real_branch.get_stacked_on_url()
 
1404
        try:
 
1405
            response = self._client.call('Branch.get_stacked_on_url',
 
1406
                self._remote_path())
 
1407
            if response[0] != 'ok':
 
1408
                raise errors.UnexpectedSmartServerResponse(response)
 
1409
            return response[1]
 
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:
 
1415
            self._ensure_real()
 
1416
            return self._real_branch.get_stacked_on_url()
1366
1417
 
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)
1384
1435
        try:
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':
1393
1445
            
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.
1407
 
            self._ensure_real()
 
1453
            # Tell the self.repository object that it is locked.
 
1454
            self.repository.lock_write(
 
1455
                self._repo_lock_token, _skip_rpc=True)
 
1456
 
1408
1457
            if self._real_branch is not None:
1409
 
                self._real_branch.repository.lock_write(
1410
 
                    token=self._repo_lock_token)
1411
 
                try:
1412
 
                    self._real_branch.lock_write(token=self._lock_token)
1413
 
                finally:
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
1417
1461
            else:
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
1424
1467
        else:
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
 
1471
                # already have.
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
1432
1478
 
1433
1479
    def _unlock(self, branch_token, repo_token):
1434
 
        path = self.bzrdir._path_for_remote_call(self._client)
1435
1480
        try:
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)
1443
1488
 
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()
1459
 
            if mode != 'w':
1460
 
                # Only write-locked branched need to make a remote method call
1461
 
                # to perfom the unlock.
1462
 
                return
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)
 
1490
        try:
 
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()
 
1505
                if mode != 'w':
 
1506
                    # Only write-locked branched need to make a remote method
 
1507
                    # call to perfom the unlock.
 
1508
                    return
 
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)
 
1517
        finally:
 
1518
            self.repository.unlock()
1471
1519
 
1472
1520
    def break_lock(self):
1473
1521
        self._ensure_real()
1484
1532
        self._leave_lock = False
1485
1533
 
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])
1494
1541
 
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')
1504
1550
            return []
1505
1551
        return result
1506
1552
 
 
1553
    def _remote_path(self):
 
1554
        return self.bzrdir._path_for_remote_call(self._client)
 
1555
 
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)
1510
1558
        try:
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
1522
1572
 
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()
1526
1575
        try:
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)
1604
1652
        try:
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()
1705
1753
      - bzrdir
1706
1754
      - token
1707
1755
      - other_branch
 
1756
 
 
1757
    If the error from the server doesn't match a known pattern, then
 
1758
    UnknownErrorFromSmartServer is raised.
1708
1759
    """
1709
1760
    def find(name):
1710
1761
        try:
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'))
1735
 
    raise
1736
 
 
 
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)