~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Matt Nordhoff
  • Date: 2009-06-23 05:12:07 UTC
  • mto: This revision was merged to the branch mainline in revision 4474.
  • Revision ID: mnordhoff@mattnordhoff.com-20090623051207-fksdtbzkwtnrw9dd
Update _add_text docstrings that still referred to add_text.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
# TODO: At some point, handle upgrades by just passing the whole request
 
18
# across to run on the server.
 
19
 
17
20
import bz2
18
21
 
19
22
from bzrlib import (
20
 
    bencode,
21
23
    branch,
22
24
    bzrdir,
23
25
    config,
24
26
    debug,
25
27
    errors,
26
28
    graph,
27
 
    lock,
28
29
    lockdir,
 
30
    pack,
29
31
    repository,
30
32
    revision,
31
33
    revision as _mod_revision,
32
34
    symbol_versioning,
 
35
    urlutils,
33
36
)
34
37
from bzrlib.branch import BranchReferenceFormat
35
38
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
42
45
from bzrlib.smart import client, vfs, repository as smart_repo
43
46
from bzrlib.revision import ensure_null, NULL_REVISION
44
47
from bzrlib.trace import mutter, note, warning
 
48
from bzrlib.util import bencode
45
49
 
46
50
 
47
51
class _RpcHelper(object):
59
63
        except errors.ErrorFromSmartServer, err:
60
64
            self._translate_error(err, **err_context)
61
65
 
62
 
    def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
63
 
        try:
64
 
            return self._client.call_with_body_bytes(method, args, body_bytes)
65
 
        except errors.ErrorFromSmartServer, err:
66
 
            self._translate_error(err, **err_context)
67
 
 
68
66
    def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
69
67
                                             **err_context):
70
68
        try:
426
424
        self._custom_format = None
427
425
        self._network_name = None
428
426
        self._creating_bzrdir = None
429
 
        self._supports_chks = None
430
427
        self._supports_external_lookups = None
431
428
        self._supports_tree_reference = None
432
429
        self._rich_root_data = None
433
430
 
434
 
    def __repr__(self):
435
 
        return "%s(_network_name=%r)" % (self.__class__.__name__,
436
 
            self._network_name)
437
 
 
438
431
    @property
439
432
    def fast_deltas(self):
440
433
        self._ensure_real()
448
441
        return self._rich_root_data
449
442
 
450
443
    @property
451
 
    def supports_chks(self):
452
 
        if self._supports_chks is None:
453
 
            self._ensure_real()
454
 
            self._supports_chks = self._custom_format.supports_chks
455
 
        return self._supports_chks
456
 
 
457
 
    @property
458
444
    def supports_external_lookups(self):
459
445
        if self._supports_external_lookups is None:
460
446
            self._ensure_real()
566
552
    def __eq__(self, other):
567
553
        return self.__class__ is other.__class__
568
554
 
 
555
    def check_conversion_target(self, target_format):
 
556
        if self.rich_root_data and not target_format.rich_root_data:
 
557
            raise errors.BadConversionTarget(
 
558
                'Does not support rich root data.', target_format)
 
559
        if (self.supports_tree_reference and
 
560
            not getattr(target_format, 'supports_tree_reference', False)):
 
561
            raise errors.BadConversionTarget(
 
562
                'Does not support nested trees', target_format)
 
563
 
569
564
    def network_name(self):
570
565
        if self._network_name:
571
566
            return self._network_name
573
568
        return self._creating_repo._real_repository._format.network_name()
574
569
 
575
570
    @property
576
 
    def pack_compresses(self):
577
 
        self._ensure_real()
578
 
        return self._custom_format.pack_compresses
579
 
 
580
 
    @property
581
571
    def _serializer(self):
582
572
        self._ensure_real()
583
573
        return self._custom_format._serializer
685
675
        return self._real_repository.get_missing_parent_inventories(
686
676
            check_for_missing_texts=check_for_missing_texts)
687
677
 
688
 
    def _get_rev_id_for_revno_vfs(self, revno, known_pair):
689
 
        self._ensure_real()
690
 
        return self._real_repository.get_rev_id_for_revno(
691
 
            revno, known_pair)
692
 
 
693
 
    def get_rev_id_for_revno(self, revno, known_pair):
694
 
        """See Repository.get_rev_id_for_revno."""
695
 
        path = self.bzrdir._path_for_remote_call(self._client)
696
 
        try:
697
 
            if self._client._medium._is_remote_before((1, 17)):
698
 
                return self._get_rev_id_for_revno_vfs(revno, known_pair)
699
 
            response = self._call(
700
 
                'Repository.get_rev_id_for_revno', path, revno, known_pair)
701
 
        except errors.UnknownSmartMethod:
702
 
            self._client._medium._remember_remote_is_before((1, 17))
703
 
            return self._get_rev_id_for_revno_vfs(revno, known_pair)
704
 
        if response[0] == 'ok':
705
 
            return True, response[1]
706
 
        elif response[0] == 'history-incomplete':
707
 
            known_pair = response[1:3]
708
 
            for fallback in self._fallback_repositories:
709
 
                found, result = fallback.get_rev_id_for_revno(revno, known_pair)
710
 
                if found:
711
 
                    return True, result
712
 
                else:
713
 
                    known_pair = result
714
 
            # Not found in any fallbacks
715
 
            return False, known_pair
716
 
        else:
717
 
            raise errors.UnexpectedSmartServerResponse(response)
718
 
 
719
678
    def _ensure_real(self):
720
679
        """Ensure that there is a _real_repository set.
721
680
 
730
689
        invocation. If in doubt chat to the bzr network team.
731
690
        """
732
691
        if self._real_repository is None:
733
 
            if 'hpssvfs' in debug.debug_flags:
 
692
            if 'hpss' in debug.debug_flags:
734
693
                import traceback
735
694
                warning('VFS Repository access triggered\n%s',
736
695
                    ''.join(traceback.format_stack()))
820
779
            result.add(_mod_revision.NULL_REVISION)
821
780
        return result
822
781
 
823
 
    def _has_same_fallbacks(self, other_repo):
824
 
        """Returns true if the repositories have the same fallbacks."""
825
 
        # XXX: copied from Repository; it should be unified into a base class
826
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
827
 
        my_fb = self._fallback_repositories
828
 
        other_fb = other_repo._fallback_repositories
829
 
        if len(my_fb) != len(other_fb):
830
 
            return False
831
 
        for f, g in zip(my_fb, other_fb):
832
 
            if not f.has_same_location(g):
833
 
                return False
834
 
        return True
835
 
 
836
782
    def has_same_location(self, other):
837
 
        # TODO: Move to RepositoryBase and unify with the regular Repository
838
 
        # one; unfortunately the tests rely on slightly different behaviour at
839
 
        # present -- mbp 20090710
840
783
        return (self.__class__ is other.__class__ and
841
784
                self.bzrdir.transport.base == other.bzrdir.transport.base)
842
785
 
1048
991
 
1049
992
    def unlock(self):
1050
993
        if not self._lock_count:
1051
 
            return lock.cant_unlock_not_held(self)
 
994
            raise errors.LockNotHeld(self)
1052
995
        self._lock_count -= 1
1053
996
        if self._lock_count > 0:
1054
997
            return
1181
1124
        self._ensure_real()
1182
1125
        return self._real_repository.get_inventory(revision_id)
1183
1126
 
1184
 
    def iter_inventories(self, revision_ids, ordering=None):
 
1127
    def iter_inventories(self, revision_ids):
1185
1128
        self._ensure_real()
1186
 
        return self._real_repository.iter_inventories(revision_ids, ordering)
 
1129
        return self._real_repository.iter_inventories(revision_ids)
1187
1130
 
1188
1131
    @needs_read_lock
1189
1132
    def get_revision(self, revision_id):
1253
1196
            raise errors.InternalBzrError(
1254
1197
                "May not fetch while in a write group.")
1255
1198
        # fast path same-url fetch operations
1256
 
        if (self.has_same_location(source)
1257
 
            and fetch_spec is None
1258
 
            and self._has_same_fallbacks(source)):
 
1199
        if self.has_same_location(source) and fetch_spec is None:
1259
1200
            # check that last_revision is in 'from' and then return a
1260
1201
            # no-operation.
1261
1202
            if (revision_id is not None and
1473
1414
        return self._real_repository.get_revision_reconcile(revision_id)
1474
1415
 
1475
1416
    @needs_read_lock
1476
 
    def check(self, revision_ids=None, callback_refs=None, check_repo=True):
 
1417
    def check(self, revision_ids=None):
1477
1418
        self._ensure_real()
1478
 
        return self._real_repository.check(revision_ids=revision_ids,
1479
 
            callback_refs=callback_refs, check_repo=check_repo)
 
1419
        return self._real_repository.check(revision_ids=revision_ids)
1480
1420
 
1481
1421
    def copy_content_into(self, destination, revision_id=None):
1482
1422
        self._ensure_real()
1522
1462
        return self._real_repository.inventories
1523
1463
 
1524
1464
    @needs_write_lock
1525
 
    def pack(self, hint=None):
 
1465
    def pack(self):
1526
1466
        """Compress the data within the repository.
1527
1467
 
1528
1468
        This is not currently implemented within the smart server.
1529
1469
        """
1530
1470
        self._ensure_real()
1531
 
        return self._real_repository.pack(hint=hint)
 
1471
        return self._real_repository.pack()
1532
1472
 
1533
1473
    @property
1534
1474
    def revisions(self):
1622
1562
        self._ensure_real()
1623
1563
        return self._real_repository.revision_graph_can_have_wrong_parents()
1624
1564
 
1625
 
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
 
1565
    def _find_inconsistent_revision_parents(self):
1626
1566
        self._ensure_real()
1627
 
        return self._real_repository._find_inconsistent_revision_parents(
1628
 
            revisions_iterator)
 
1567
        return self._real_repository._find_inconsistent_revision_parents()
1629
1568
 
1630
1569
    def _check_for_inconsistent_revision_parents(self):
1631
1570
        self._ensure_real()
1637
1576
            providers.insert(0, other)
1638
1577
        providers.extend(r._make_parents_provider() for r in
1639
1578
                         self._fallback_repositories)
1640
 
        return graph.StackedParentsProvider(providers)
 
1579
        return graph._StackedParentsProvider(providers)
1641
1580
 
1642
1581
    def _serialise_search_recipe(self, recipe):
1643
1582
        """Serialise a graph search recipe.
1685
1624
    def insert_stream(self, stream, src_format, resume_tokens):
1686
1625
        target = self.target_repo
1687
1626
        target._unstacked_provider.missing_keys.clear()
1688
 
        candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
1689
1627
        if target._lock_token:
1690
 
            candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
1691
 
            lock_args = (target._lock_token or '',)
 
1628
            verb = 'Repository.insert_stream_locked'
 
1629
            extra_args = (target._lock_token or '',)
 
1630
            required_version = (1, 14)
1692
1631
        else:
1693
 
            candidate_calls.append(('Repository.insert_stream', (1, 13)))
1694
 
            lock_args = ()
 
1632
            verb = 'Repository.insert_stream'
 
1633
            extra_args = ()
 
1634
            required_version = (1, 13)
1695
1635
        client = target._client
1696
1636
        medium = client._medium
 
1637
        if medium._is_remote_before(required_version):
 
1638
            # No possible way this can work.
 
1639
            return self._insert_real(stream, src_format, resume_tokens)
1697
1640
        path = target.bzrdir._path_for_remote_call(client)
1698
 
        # Probe for the verb to use with an empty stream before sending the
1699
 
        # real stream to it.  We do this both to avoid the risk of sending a
1700
 
        # large request that is then rejected, and because we don't want to
1701
 
        # implement a way to buffer, rewind, or restart the stream.
1702
 
        found_verb = False
1703
 
        for verb, required_version in candidate_calls:
1704
 
            if medium._is_remote_before(required_version):
1705
 
                continue
1706
 
            if resume_tokens:
1707
 
                # We've already done the probing (and set _is_remote_before) on
1708
 
                # a previous insert.
1709
 
                found_verb = True
1710
 
                break
 
1641
        if not resume_tokens:
 
1642
            # XXX: Ugly but important for correctness, *will* be fixed during
 
1643
            # 1.13 cycle. Pushing a stream that is interrupted results in a
 
1644
            # fallback to the _real_repositories sink *with a partial stream*.
 
1645
            # Thats bad because we insert less data than bzr expected. To avoid
 
1646
            # this we do a trial push to make sure the verb is accessible, and
 
1647
            # do not fallback when actually pushing the stream. A cleanup patch
 
1648
            # is going to look at rewinding/restarting the stream/partial
 
1649
            # buffering etc.
1711
1650
            byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1712
1651
            try:
1713
1652
                response = client.call_with_body_stream(
1714
 
                    (verb, path, '') + lock_args, byte_stream)
 
1653
                    (verb, path, '') + extra_args, byte_stream)
1715
1654
            except errors.UnknownSmartMethod:
1716
1655
                medium._remember_remote_is_before(required_version)
1717
 
            else:
1718
 
                found_verb = True
1719
 
                break
1720
 
        if not found_verb:
1721
 
            # Have to use VFS.
1722
 
            return self._insert_real(stream, src_format, resume_tokens)
1723
 
        self._last_inv_record = None
1724
 
        self._last_substream = None
1725
 
        if required_version < (1, 19):
1726
 
            # Remote side doesn't support inventory deltas.  Wrap the stream to
1727
 
            # make sure we don't send any.  If the stream contains inventory
1728
 
            # deltas we'll interrupt the smart insert_stream request and
1729
 
            # fallback to VFS.
1730
 
            stream = self._stop_stream_if_inventory_delta(stream)
 
1656
                return self._insert_real(stream, src_format, resume_tokens)
1731
1657
        byte_stream = smart_repo._stream_to_byte_stream(
1732
1658
            stream, src_format)
1733
1659
        resume_tokens = ' '.join(resume_tokens)
1734
1660
        response = client.call_with_body_stream(
1735
 
            (verb, path, resume_tokens) + lock_args, byte_stream)
 
1661
            (verb, path, resume_tokens) + extra_args, byte_stream)
1736
1662
        if response[0][0] not in ('ok', 'missing-basis'):
1737
1663
            raise errors.UnexpectedSmartServerResponse(response)
1738
 
        if self._last_substream is not None:
1739
 
            # The stream included an inventory-delta record, but the remote
1740
 
            # side isn't new enough to support them.  So we need to send the
1741
 
            # rest of the stream via VFS.
1742
 
            return self._resume_stream_with_vfs(response, src_format)
1743
1664
        if response[0][0] == 'missing-basis':
1744
1665
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1745
1666
            resume_tokens = tokens
1748
1669
            self.target_repo.refresh_data()
1749
1670
            return [], set()
1750
1671
 
1751
 
    def _resume_stream_with_vfs(self, response, src_format):
1752
 
        """Resume sending a stream via VFS, first resending the record and
1753
 
        substream that couldn't be sent via an insert_stream verb.
1754
 
        """
1755
 
        if response[0][0] == 'missing-basis':
1756
 
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1757
 
            # Ignore missing_keys, we haven't finished inserting yet
1758
 
        else:
1759
 
            tokens = []
1760
 
        def resume_substream():
1761
 
            # Yield the substream that was interrupted.
1762
 
            for record in self._last_substream:
1763
 
                yield record
1764
 
            self._last_substream = None
1765
 
        def resume_stream():
1766
 
            # Finish sending the interrupted substream
1767
 
            yield ('inventory-deltas', resume_substream())
1768
 
            # Then simply continue sending the rest of the stream.
1769
 
            for substream_kind, substream in self._last_stream:
1770
 
                yield substream_kind, substream
1771
 
        return self._insert_real(resume_stream(), src_format, tokens)
1772
 
 
1773
 
    def _stop_stream_if_inventory_delta(self, stream):
1774
 
        """Normally this just lets the original stream pass-through unchanged.
1775
 
 
1776
 
        However if any 'inventory-deltas' substream occurs it will stop
1777
 
        streaming, and store the interrupted substream and stream in
1778
 
        self._last_substream and self._last_stream so that the stream can be
1779
 
        resumed by _resume_stream_with_vfs.
1780
 
        """
1781
 
                    
1782
 
        stream_iter = iter(stream)
1783
 
        for substream_kind, substream in stream_iter:
1784
 
            if substream_kind == 'inventory-deltas':
1785
 
                self._last_substream = substream
1786
 
                self._last_stream = stream_iter
1787
 
                return
1788
 
            else:
1789
 
                yield substream_kind, substream
1790
 
            
1791
1672
 
1792
1673
class RemoteStreamSource(repository.StreamSource):
1793
1674
    """Stream data from a remote server."""
1796
1677
        if (self.from_repository._fallback_repositories and
1797
1678
            self.to_format._fetch_order == 'topological'):
1798
1679
            return self._real_stream(self.from_repository, search)
1799
 
        sources = []
1800
 
        seen = set()
1801
 
        repos = [self.from_repository]
1802
 
        while repos:
1803
 
            repo = repos.pop(0)
1804
 
            if repo in seen:
1805
 
                continue
1806
 
            seen.add(repo)
1807
 
            repos.extend(repo._fallback_repositories)
1808
 
            sources.append(repo)
1809
 
        return self.missing_parents_chain(search, sources)
1810
 
 
1811
 
    def get_stream_for_missing_keys(self, missing_keys):
1812
 
        self.from_repository._ensure_real()
1813
 
        real_repo = self.from_repository._real_repository
1814
 
        real_source = real_repo._get_source(self.to_format)
1815
 
        return real_source.get_stream_for_missing_keys(missing_keys)
 
1680
        return self.missing_parents_chain(search, [self.from_repository] +
 
1681
            self.from_repository._fallback_repositories)
1816
1682
 
1817
1683
    def _real_stream(self, repo, search):
1818
1684
        """Get a stream for search from repo.
1825
1691
        """
1826
1692
        source = repo._get_source(self.to_format)
1827
1693
        if isinstance(source, RemoteStreamSource):
1828
 
            repo._ensure_real()
1829
 
            source = repo._real_repository._get_source(self.to_format)
 
1694
            return repository.StreamSource.get_stream(source, search)
1830
1695
        return source.get_stream(search)
1831
1696
 
1832
1697
    def _get_stream(self, repo, search):
1849
1714
            return self._real_stream(repo, search)
1850
1715
        client = repo._client
1851
1716
        medium = client._medium
 
1717
        if medium._is_remote_before((1, 13)):
 
1718
            # streaming was added in 1.13
 
1719
            return self._real_stream(repo, search)
1852
1720
        path = repo.bzrdir._path_for_remote_call(client)
1853
 
        search_bytes = repo._serialise_search_result(search)
1854
 
        args = (path, self.to_format.network_name())
1855
 
        candidate_verbs = [
1856
 
            ('Repository.get_stream_1.19', (1, 19)),
1857
 
            ('Repository.get_stream', (1, 13))]
1858
 
        found_verb = False
1859
 
        for verb, version in candidate_verbs:
1860
 
            if medium._is_remote_before(version):
1861
 
                continue
1862
 
            try:
1863
 
                response = repo._call_with_body_bytes_expecting_body(
1864
 
                    verb, args, search_bytes)
1865
 
            except errors.UnknownSmartMethod:
1866
 
                medium._remember_remote_is_before(version)
1867
 
            else:
1868
 
                response_tuple, response_handler = response
1869
 
                found_verb = True
1870
 
                break
1871
 
        if not found_verb:
 
1721
        try:
 
1722
            search_bytes = repo._serialise_search_result(search)
 
1723
            response = repo._call_with_body_bytes_expecting_body(
 
1724
                'Repository.get_stream',
 
1725
                (path, self.to_format.network_name()), search_bytes)
 
1726
            response_tuple, response_handler = response
 
1727
        except errors.UnknownSmartMethod:
 
1728
            medium._remember_remote_is_before((1,13))
1872
1729
            return self._real_stream(repo, search)
1873
1730
        if response_tuple[0] != 'ok':
1874
1731
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2038
1895
        self._ensure_real()
2039
1896
        return self._custom_format.supports_stacking()
2040
1897
 
2041
 
    def supports_set_append_revisions_only(self):
2042
 
        self._ensure_real()
2043
 
        return self._custom_format.supports_set_append_revisions_only()
2044
 
 
2045
1898
 
2046
1899
class RemoteBranch(branch.Branch, _RpcHelper):
2047
1900
    """Branch stored on a server accessed by HPSS RPC.
2066
1919
        # We intentionally don't call the parent class's __init__, because it
2067
1920
        # will try to assign to self.tags, which is a property in this subclass.
2068
1921
        # And the parent's __init__ doesn't do much anyway.
 
1922
        self._revision_id_to_revno_cache = None
 
1923
        self._partial_revision_id_to_revno_cache = {}
 
1924
        self._revision_history_cache = None
 
1925
        self._last_revision_info_cache = None
 
1926
        self._merge_sorted_revisions_cache = None
2069
1927
        self.bzrdir = remote_bzrdir
2070
1928
        if _client is not None:
2071
1929
            self._client = _client
2085
1943
        else:
2086
1944
            self._real_branch = None
2087
1945
        # Fill out expected attributes of branch for bzrlib API users.
2088
 
        self._clear_cached_state()
2089
1946
        self.base = self.bzrdir.root_transport.base
2090
1947
        self._control_files = None
2091
1948
        self._lock_mode = None
2103
1960
                    self._real_branch._format.network_name()
2104
1961
        else:
2105
1962
            self._format = format
2106
 
        # when we do _ensure_real we may need to pass ignore_fallbacks to the
2107
 
        # branch.open_branch method.
2108
 
        self._real_ignore_fallbacks = not setup_stacking
2109
1963
        if not self._format._network_name:
2110
1964
            # Did not get from open_branchV2 - old server.
2111
1965
            self._ensure_real()
2116
1970
        hooks = branch.Branch.hooks['open']
2117
1971
        for hook in hooks:
2118
1972
            hook(self)
2119
 
        self._is_stacked = False
2120
1973
        if setup_stacking:
2121
1974
            self._setup_stacking()
2122
1975
 
2128
1981
        except (errors.NotStacked, errors.UnstackableBranchFormat,
2129
1982
            errors.UnstackableRepositoryFormat), e:
2130
1983
            return
2131
 
        self._is_stacked = True
2132
1984
        self._activate_fallback_location(fallback_url)
2133
1985
 
2134
1986
    def _get_config(self):
2156
2008
                raise AssertionError('smart server vfs must be enabled '
2157
2009
                    'to use vfs implementation')
2158
2010
            self.bzrdir._ensure_real()
2159
 
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2160
 
                ignore_fallbacks=self._real_ignore_fallbacks)
 
2011
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
2161
2012
            if self.repository._real_repository is None:
2162
2013
                # Give the remote repository the matching real repo.
2163
2014
                real_repo = self._real_branch.repository
2237
2088
            raise errors.UnexpectedSmartServerResponse(response)
2238
2089
        return response[1]
2239
2090
 
2240
 
    def set_stacked_on_url(self, url):
2241
 
        branch.Branch.set_stacked_on_url(self, url)
2242
 
        if not url:
2243
 
            self._is_stacked = False
2244
 
        else:
2245
 
            self._is_stacked = True
2246
 
        
2247
2091
    def _vfs_get_tags_bytes(self):
2248
2092
        self._ensure_real()
2249
2093
        return self._real_branch._get_tags_bytes()
2259
2103
            return self._vfs_get_tags_bytes()
2260
2104
        return response[0]
2261
2105
 
2262
 
    def _vfs_set_tags_bytes(self, bytes):
2263
 
        self._ensure_real()
2264
 
        return self._real_branch._set_tags_bytes(bytes)
2265
 
 
2266
 
    def _set_tags_bytes(self, bytes):
2267
 
        medium = self._client._medium
2268
 
        if medium._is_remote_before((1, 18)):
2269
 
            self._vfs_set_tags_bytes(bytes)
2270
 
        try:
2271
 
            args = (
2272
 
                self._remote_path(), self._lock_token, self._repo_lock_token)
2273
 
            response = self._call_with_body_bytes(
2274
 
                'Branch.set_tags_bytes', args, bytes)
2275
 
        except errors.UnknownSmartMethod:
2276
 
            medium._remember_remote_is_before((1, 18))
2277
 
            self._vfs_set_tags_bytes(bytes)
2278
 
 
2279
2106
    def lock_read(self):
2280
2107
        self.repository.lock_read()
2281
2108
        if not self._lock_mode:
2335
2162
            self.repository.lock_write(self._repo_lock_token)
2336
2163
        return self._lock_token or None
2337
2164
 
 
2165
    def _set_tags_bytes(self, bytes):
 
2166
        self._ensure_real()
 
2167
        return self._real_branch._set_tags_bytes(bytes)
 
2168
 
2338
2169
    def _unlock(self, branch_token, repo_token):
2339
2170
        err_context = {'token': str((branch_token, repo_token))}
2340
2171
        response = self._call(
2389
2220
            raise NotImplementedError(self.dont_leave_lock_in_place)
2390
2221
        self._leave_lock = False
2391
2222
 
2392
 
    def get_rev_id(self, revno, history=None):
2393
 
        if revno == 0:
2394
 
            return _mod_revision.NULL_REVISION
2395
 
        last_revision_info = self.last_revision_info()
2396
 
        ok, result = self.repository.get_rev_id_for_revno(
2397
 
            revno, last_revision_info)
2398
 
        if ok:
2399
 
            return result
2400
 
        missing_parent = result[1]
2401
 
        # Either the revision named by the server is missing, or its parent
2402
 
        # is.  Call get_parent_map to determine which, so that we report a
2403
 
        # useful error.
2404
 
        parent_map = self.repository.get_parent_map([missing_parent])
2405
 
        if missing_parent in parent_map:
2406
 
            missing_parent = parent_map[missing_parent]
2407
 
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2408
 
 
2409
2223
    def _last_revision_info(self):
2410
2224
        response = self._call('Branch.last_revision_info', self._remote_path())
2411
2225
        if response[0] != 'ok':
2416
2230
 
2417
2231
    def _gen_revision_history(self):
2418
2232
        """See Branch._gen_revision_history()."""
2419
 
        if self._is_stacked:
2420
 
            self._ensure_real()
2421
 
            return self._real_branch._gen_revision_history()
2422
2233
        response_tuple, response_handler = self._call_expecting_body(
2423
2234
            'Branch.revision_history', self._remote_path())
2424
2235
        if response_tuple[0] != 'ok':
2759
2570
                    'Missing key %r in context %r', key_err.args[0], context)
2760
2571
                raise err
2761
2572
 
2762
 
    if err.error_verb == 'IncompatibleRepositories':
2763
 
        raise errors.IncompatibleRepositories(err.error_args[0],
2764
 
            err.error_args[1], err.error_args[2])
2765
 
    elif err.error_verb == 'NoSuchRevision':
 
2573
    if err.error_verb == 'NoSuchRevision':
2766
2574
        raise NoSuchRevision(find('branch'), err.error_args[0])
2767
2575
    elif err.error_verb == 'nosuchrevision':
2768
2576
        raise NoSuchRevision(find('repository'), err.error_args[0])