~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

(gz) Backslash escape selftest output when printing to non-unicode consoles
 (Martin [gz])

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-2010 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
21
21
    branch,
22
22
    bzrdir,
23
23
    config,
 
24
    controldir,
24
25
    debug,
25
26
    errors,
26
27
    graph,
27
28
    lock,
28
29
    lockdir,
29
30
    repository,
 
31
    repository as _mod_repository,
30
32
    revision,
31
33
    revision as _mod_revision,
 
34
    static_tuple,
32
35
    symbol_versioning,
33
36
)
34
 
from bzrlib.branch import BranchReferenceFormat
 
37
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
35
38
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
36
39
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
37
40
from bzrlib.errors import (
41
44
from bzrlib.lockable_files import LockableFiles
42
45
from bzrlib.smart import client, vfs, repository as smart_repo
43
46
from bzrlib.revision import ensure_null, NULL_REVISION
 
47
from bzrlib.repository import RepositoryWriteLockResult
44
48
from bzrlib.trace import mutter, note, warning
45
49
 
46
50
 
210
214
        if len(branch_info) != 2:
211
215
            raise errors.UnexpectedSmartServerResponse(response)
212
216
        branch_ref, branch_name = branch_info
213
 
        format = bzrdir.network_format_registry.get(control_name)
 
217
        format = controldir.network_format_registry.get(control_name)
214
218
        if repo_name:
215
219
            format.repository_format = repository.network_format_registry.get(
216
220
                repo_name)
242
246
        self._ensure_real()
243
247
        self._real_bzrdir.destroy_repository()
244
248
 
245
 
    def create_branch(self):
 
249
    def create_branch(self, name=None):
246
250
        # as per meta1 formats - just delegate to the format object which may
247
251
        # be parameterised.
248
 
        real_branch = self._format.get_branch_format().initialize(self)
 
252
        real_branch = self._format.get_branch_format().initialize(self,
 
253
            name=name)
249
254
        if not isinstance(real_branch, RemoteBranch):
250
 
            result = RemoteBranch(self, self.find_repository(), real_branch)
 
255
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
256
                                  name=name)
251
257
        else:
252
258
            result = real_branch
253
259
        # BzrDir.clone_on_transport() uses the result of create_branch but does
259
265
        self._next_open_branch_result = result
260
266
        return result
261
267
 
262
 
    def destroy_branch(self):
 
268
    def destroy_branch(self, name=None):
263
269
        """See BzrDir.destroy_branch"""
264
270
        self._ensure_real()
265
 
        self._real_bzrdir.destroy_branch()
 
271
        self._real_bzrdir.destroy_branch(name=name)
266
272
        self._next_open_branch_result = None
267
273
 
268
 
    def create_workingtree(self, revision_id=None, from_branch=None):
 
274
    def create_workingtree(self, revision_id=None, from_branch=None,
 
275
        accelerator_tree=None, hardlink=False):
269
276
        raise errors.NotLocalUrl(self.transport.base)
270
277
 
271
 
    def find_branch_format(self):
 
278
    def find_branch_format(self, name=None):
272
279
        """Find the branch 'format' for this bzrdir.
273
280
 
274
281
        This might be a synthetic object for e.g. RemoteBranch and SVN.
275
282
        """
276
 
        b = self.open_branch()
 
283
        b = self.open_branch(name=name)
277
284
        return b._format
278
285
 
279
 
    def get_branch_reference(self):
 
286
    def get_branch_reference(self, name=None):
280
287
        """See BzrDir.get_branch_reference()."""
 
288
        if name is not None:
 
289
            # XXX JRV20100304: Support opening colocated branches
 
290
            raise errors.NoColocatedBranchSupport(self)
281
291
        response = self._get_branch_reference()
282
292
        if response[0] == 'ref':
283
293
            return response[1]
314
324
            raise errors.UnexpectedSmartServerResponse(response)
315
325
        return response
316
326
 
317
 
    def _get_tree_branch(self):
 
327
    def _get_tree_branch(self, name=None):
318
328
        """See BzrDir._get_tree_branch()."""
319
 
        return None, self.open_branch()
 
329
        return None, self.open_branch(name=name)
320
330
 
321
 
    def open_branch(self, _unsupported=False, ignore_fallbacks=False):
322
 
        if _unsupported:
 
331
    def open_branch(self, name=None, unsupported=False,
 
332
                    ignore_fallbacks=False):
 
333
        if unsupported:
323
334
            raise NotImplementedError('unsupported flag support not implemented yet.')
324
335
        if self._next_open_branch_result is not None:
325
336
            # See create_branch for details.
330
341
        if response[0] == 'ref':
331
342
            # a branch reference, use the existing BranchReference logic.
332
343
            format = BranchReferenceFormat()
333
 
            return format.open(self, _found=True, location=response[1],
334
 
                ignore_fallbacks=ignore_fallbacks)
 
344
            return format.open(self, name=name, _found=True,
 
345
                location=response[1], ignore_fallbacks=ignore_fallbacks)
335
346
        branch_format_name = response[1]
336
347
        if not branch_format_name:
337
348
            branch_format_name = None
338
349
        format = RemoteBranchFormat(network_name=branch_format_name)
339
350
        return RemoteBranch(self, self.find_repository(), format=format,
340
 
            setup_stacking=not ignore_fallbacks)
 
351
            setup_stacking=not ignore_fallbacks, name=name)
341
352
 
342
353
    def _open_repo_v1(self, path):
343
354
        verb = 'BzrDir.find_repository'
420
431
        """Return the path to be used for this bzrdir in a remote call."""
421
432
        return client.remote_path_from_transport(self.root_transport)
422
433
 
423
 
    def get_branch_transport(self, branch_format):
 
434
    def get_branch_transport(self, branch_format, name=None):
424
435
        self._ensure_real()
425
 
        return self._real_bzrdir.get_branch_transport(branch_format)
 
436
        return self._real_bzrdir.get_branch_transport(branch_format, name=name)
426
437
 
427
438
    def get_repository_transport(self, repository_format):
428
439
        self._ensure_real()
638
649
        return self._custom_format._serializer
639
650
 
640
651
 
641
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin):
 
652
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
 
653
    controldir.ControlComponent):
642
654
    """Repository accessed over rpc.
643
655
 
644
656
    For the moment most operations are performed using local transport-backed
687
699
        # Additional places to query for data.
688
700
        self._fallback_repositories = []
689
701
 
 
702
    @property
 
703
    def user_transport(self):
 
704
        return self.bzrdir.user_transport
 
705
 
 
706
    @property
 
707
    def control_transport(self):
 
708
        # XXX: Normally you shouldn't directly get at the remote repository
 
709
        # transport, but I'm not sure it's worth making this method
 
710
        # optional -- mbp 2010-04-21
 
711
        return self.bzrdir.get_repository_transport(None)
 
712
        
690
713
    def __str__(self):
691
714
        return "%s(%s)" % (self.__class__.__name__, self.base)
692
715
 
878
901
    def _has_same_fallbacks(self, other_repo):
879
902
        """Returns true if the repositories have the same fallbacks."""
880
903
        # XXX: copied from Repository; it should be unified into a base class
881
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
 
904
        # <https://bugs.launchpad.net/bzr/+bug/401622>
882
905
        my_fb = self._fallback_repositories
883
906
        other_fb = other_repo._fallback_repositories
884
907
        if len(my_fb) != len(other_fb):
900
923
        parents_provider = self._make_parents_provider(other_repository)
901
924
        return graph.Graph(parents_provider)
902
925
 
 
926
    @needs_read_lock
 
927
    def get_known_graph_ancestry(self, revision_ids):
 
928
        """Return the known graph for a set of revision ids and their ancestors.
 
929
        """
 
930
        st = static_tuple.StaticTuple
 
931
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
 
932
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
 
933
        return graph.GraphThunkIdsToKeys(known_graph)
 
934
 
903
935
    def gather_stats(self, revid=None, committers=None):
904
936
        """See Repository.gather_stats()."""
905
937
        path = self.bzrdir._path_for_remote_call(self._client)
971
1003
        pass
972
1004
 
973
1005
    def lock_read(self):
 
1006
        """Lock the repository for read operations.
 
1007
 
 
1008
        :return: A bzrlib.lock.LogicalLockResult.
 
1009
        """
974
1010
        # wrong eventually - want a local lock cache context
975
1011
        if not self._lock_mode:
976
1012
            self._note_lock('r')
983
1019
                repo.lock_read()
984
1020
        else:
985
1021
            self._lock_count += 1
 
1022
        return lock.LogicalLockResult(self.unlock)
986
1023
 
987
1024
    def _remote_lock_write(self, token):
988
1025
        path = self.bzrdir._path_for_remote_call(self._client)
1028
1065
            raise errors.ReadOnlyError(self)
1029
1066
        else:
1030
1067
            self._lock_count += 1
1031
 
        return self._lock_token or None
 
1068
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
1032
1069
 
1033
1070
    def leave_lock_in_place(self):
1034
1071
        if not self._lock_token:
1214
1251
            # state, so always add a lock here. If a caller passes us a locked
1215
1252
            # repository, they are responsible for unlocking it later.
1216
1253
            repository.lock_read()
 
1254
        self._check_fallback_repository(repository)
1217
1255
        self._fallback_repositories.append(repository)
1218
1256
        # If self._real_repository was parameterised already (e.g. because a
1219
1257
        # _real_branch had its get_stacked_on_url method called), then the
1220
1258
        # repository to be added may already be in the _real_repositories list.
1221
1259
        if self._real_repository is not None:
1222
 
            fallback_locations = [repo.bzrdir.root_transport.base for repo in
 
1260
            fallback_locations = [repo.user_url for repo in
1223
1261
                self._real_repository._fallback_repositories]
1224
 
            if repository.bzrdir.root_transport.base not in fallback_locations:
 
1262
            if repository.user_url not in fallback_locations:
1225
1263
                self._real_repository.add_fallback_repository(repository)
1226
1264
 
 
1265
    def _check_fallback_repository(self, repository):
 
1266
        """Check that this repository can fallback to repository safely.
 
1267
 
 
1268
        Raise an error if not.
 
1269
 
 
1270
        :param repository: A repository to fallback to.
 
1271
        """
 
1272
        return _mod_repository.InterRepository._assert_same_model(
 
1273
            self, repository)
 
1274
 
1227
1275
    def add_inventory(self, revid, inv, parents):
1228
1276
        self._ensure_real()
1229
1277
        return self._real_repository.add_inventory(revid, inv, parents)
1230
1278
 
1231
1279
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1232
 
                               parents):
 
1280
            parents, basis_inv=None, propagate_caches=False):
1233
1281
        self._ensure_real()
1234
1282
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
1235
 
            delta, new_revision_id, parents)
 
1283
            delta, new_revision_id, parents, basis_inv=basis_inv,
 
1284
            propagate_caches=propagate_caches)
1236
1285
 
1237
1286
    def add_revision(self, rev_id, rev, inv=None, config=None):
1238
1287
        self._ensure_real()
1268
1317
        return self._real_repository.make_working_trees()
1269
1318
 
1270
1319
    def refresh_data(self):
1271
 
        """Re-read any data needed to to synchronise with disk.
 
1320
        """Re-read any data needed to synchronise with disk.
1272
1321
 
1273
1322
        This method is intended to be called after another repository instance
1274
1323
        (such as one used by a smart server) has inserted data into the
1275
 
        repository. It may not be called during a write group, but may be
1276
 
        called at any other time.
 
1324
        repository. On all repositories this will work outside of write groups.
 
1325
        Some repository formats (pack and newer for bzrlib native formats)
 
1326
        support refresh_data inside write groups. If called inside a write
 
1327
        group on a repository that does not support refreshing in a write group
 
1328
        IsInWriteGroupError will be raised.
1277
1329
        """
1278
 
        if self.is_in_write_group():
1279
 
            raise errors.InternalBzrError(
1280
 
                "May not refresh_data while in a write group.")
1281
1330
        if self._real_repository is not None:
1282
1331
            self._real_repository.refresh_data()
1283
1332
 
1501
1550
        self._ensure_real()
1502
1551
        return self._real_repository._get_inventory_xml(revision_id)
1503
1552
 
1504
 
    def _deserialise_inventory(self, revision_id, xml):
1505
 
        self._ensure_real()
1506
 
        return self._real_repository._deserialise_inventory(revision_id, xml)
1507
 
 
1508
1553
    def reconcile(self, other=None, thorough=False):
1509
1554
        self._ensure_real()
1510
1555
        return self._real_repository.reconcile(other=other, thorough=thorough)
1585
1630
        return self._real_repository.inventories
1586
1631
 
1587
1632
    @needs_write_lock
1588
 
    def pack(self, hint=None):
 
1633
    def pack(self, hint=None, clean_obsolete_packs=False):
1589
1634
        """Compress the data within the repository.
1590
1635
 
1591
1636
        This is not currently implemented within the smart server.
1592
1637
        """
1593
1638
        self._ensure_real()
1594
 
        return self._real_repository.pack(hint=hint)
 
1639
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
1595
1640
 
1596
1641
    @property
1597
1642
    def revisions(self):
1937
1982
        if response_tuple[0] != 'ok':
1938
1983
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1939
1984
        byte_stream = response_handler.read_streamed_body()
1940
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
 
1985
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
 
1986
            self._record_counter)
1941
1987
        if src_format.network_name() != repo._format.network_name():
1942
1988
            raise AssertionError(
1943
1989
                "Mismatched RemoteRepository and stream src %r, %r" % (
2025
2071
    def network_name(self):
2026
2072
        return self._network_name
2027
2073
 
2028
 
    def open(self, a_bzrdir, ignore_fallbacks=False):
2029
 
        return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
 
2074
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
 
2075
        return a_bzrdir.open_branch(name=name, 
 
2076
            ignore_fallbacks=ignore_fallbacks)
2030
2077
 
2031
 
    def _vfs_initialize(self, a_bzrdir):
 
2078
    def _vfs_initialize(self, a_bzrdir, name):
2032
2079
        # Initialisation when using a local bzrdir object, or a non-vfs init
2033
2080
        # method is not available on the server.
2034
2081
        # self._custom_format is always set - the start of initialize ensures
2035
2082
        # that.
2036
2083
        if isinstance(a_bzrdir, RemoteBzrDir):
2037
2084
            a_bzrdir._ensure_real()
2038
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
 
2085
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
 
2086
                name)
2039
2087
        else:
2040
2088
            # We assume the bzrdir is parameterised; it may not be.
2041
 
            result = self._custom_format.initialize(a_bzrdir)
 
2089
            result = self._custom_format.initialize(a_bzrdir, name)
2042
2090
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2043
2091
            not isinstance(result, RemoteBranch)):
2044
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
 
2092
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
 
2093
                                  name=name)
2045
2094
        return result
2046
2095
 
2047
 
    def initialize(self, a_bzrdir):
 
2096
    def initialize(self, a_bzrdir, name=None):
2048
2097
        # 1) get the network name to use.
2049
2098
        if self._custom_format:
2050
2099
            network_name = self._custom_format.network_name()
2056
2105
            network_name = reference_format.network_name()
2057
2106
        # Being asked to create on a non RemoteBzrDir:
2058
2107
        if not isinstance(a_bzrdir, RemoteBzrDir):
2059
 
            return self._vfs_initialize(a_bzrdir)
 
2108
            return self._vfs_initialize(a_bzrdir, name=name)
2060
2109
        medium = a_bzrdir._client._medium
2061
2110
        if medium._is_remote_before((1, 13)):
2062
 
            return self._vfs_initialize(a_bzrdir)
 
2111
            return self._vfs_initialize(a_bzrdir, name=name)
2063
2112
        # Creating on a remote bzr dir.
2064
2113
        # 2) try direct creation via RPC
2065
2114
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
2115
        if name is not None:
 
2116
            # XXX JRV20100304: Support creating colocated branches
 
2117
            raise errors.NoColocatedBranchSupport(self)
2066
2118
        verb = 'BzrDir.create_branch'
2067
2119
        try:
2068
2120
            response = a_bzrdir._call(verb, path, network_name)
2069
2121
        except errors.UnknownSmartMethod:
2070
2122
            # Fallback - use vfs methods
2071
2123
            medium._remember_remote_is_before((1, 13))
2072
 
            return self._vfs_initialize(a_bzrdir)
 
2124
            return self._vfs_initialize(a_bzrdir, name=name)
2073
2125
        if response[0] != 'ok':
2074
2126
            raise errors.UnexpectedSmartServerResponse(response)
2075
2127
        # Turn the response into a RemoteRepository object.
2083
2135
                a_bzrdir._client)
2084
2136
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2085
2137
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2086
 
            format=format, setup_stacking=False)
 
2138
            format=format, setup_stacking=False, name=name)
2087
2139
        # XXX: We know this is a new branch, so it must have revno 0, revid
2088
2140
        # NULL_REVISION. Creating the branch locked would make this be unable
2089
2141
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2116
2168
    """
2117
2169
 
2118
2170
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2119
 
        _client=None, format=None, setup_stacking=True):
 
2171
        _client=None, format=None, setup_stacking=True, name=None):
2120
2172
        """Create a RemoteBranch instance.
2121
2173
 
2122
2174
        :param real_branch: An optional local implementation of the branch
2128
2180
        :param setup_stacking: If True make an RPC call to determine the
2129
2181
            stacked (or not) status of the branch. If False assume the branch
2130
2182
            is not stacked.
 
2183
        :param name: Colocated branch name
2131
2184
        """
2132
2185
        # We intentionally don't call the parent class's __init__, because it
2133
2186
        # will try to assign to self.tags, which is a property in this subclass.
2152
2205
            self._real_branch = None
2153
2206
        # Fill out expected attributes of branch for bzrlib API users.
2154
2207
        self._clear_cached_state()
2155
 
        self.base = self.bzrdir.root_transport.base
 
2208
        # TODO: deprecate self.base in favor of user_url
 
2209
        self.base = self.bzrdir.user_url
 
2210
        self._name = name
2156
2211
        self._control_files = None
2157
2212
        self._lock_mode = None
2158
2213
        self._lock_token = None
2223
2278
                    'to use vfs implementation')
2224
2279
            self.bzrdir._ensure_real()
2225
2280
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2226
 
                ignore_fallbacks=self._real_ignore_fallbacks)
 
2281
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
2227
2282
            if self.repository._real_repository is None:
2228
2283
                # Give the remote repository the matching real repo.
2229
2284
                real_repo = self._real_branch.repository
2314
2369
        self._ensure_real()
2315
2370
        return self._real_branch._get_tags_bytes()
2316
2371
 
 
2372
    @needs_read_lock
2317
2373
    def _get_tags_bytes(self):
 
2374
        if self._tags_bytes is None:
 
2375
            self._tags_bytes = self._get_tags_bytes_via_hpss()
 
2376
        return self._tags_bytes
 
2377
 
 
2378
    def _get_tags_bytes_via_hpss(self):
2318
2379
        medium = self._client._medium
2319
2380
        if medium._is_remote_before((1, 13)):
2320
2381
            return self._vfs_get_tags_bytes()
2330
2391
        return self._real_branch._set_tags_bytes(bytes)
2331
2392
 
2332
2393
    def _set_tags_bytes(self, bytes):
 
2394
        if self.is_locked():
 
2395
            self._tags_bytes = bytes
2333
2396
        medium = self._client._medium
2334
2397
        if medium._is_remote_before((1, 18)):
2335
2398
            self._vfs_set_tags_bytes(bytes)
2344
2407
            self._vfs_set_tags_bytes(bytes)
2345
2408
 
2346
2409
    def lock_read(self):
 
2410
        """Lock the branch for read operations.
 
2411
 
 
2412
        :return: A bzrlib.lock.LogicalLockResult.
 
2413
        """
2347
2414
        self.repository.lock_read()
2348
2415
        if not self._lock_mode:
2349
2416
            self._note_lock('r')
2353
2420
                self._real_branch.lock_read()
2354
2421
        else:
2355
2422
            self._lock_count += 1
 
2423
        return lock.LogicalLockResult(self.unlock)
2356
2424
 
2357
2425
    def _remote_lock_write(self, token):
2358
2426
        if token is None:
2359
2427
            branch_token = repo_token = ''
2360
2428
        else:
2361
2429
            branch_token = token
2362
 
            repo_token = self.repository.lock_write()
 
2430
            repo_token = self.repository.lock_write().repository_token
2363
2431
            self.repository.unlock()
2364
2432
        err_context = {'token': token}
2365
 
        response = self._call(
2366
 
            'Branch.lock_write', self._remote_path(), branch_token,
2367
 
            repo_token or '', **err_context)
 
2433
        try:
 
2434
            response = self._call(
 
2435
                'Branch.lock_write', self._remote_path(), branch_token,
 
2436
                repo_token or '', **err_context)
 
2437
        except errors.LockContention, e:
 
2438
            # The LockContention from the server doesn't have any
 
2439
            # information about the lock_url. We re-raise LockContention
 
2440
            # with valid lock_url.
 
2441
            raise errors.LockContention('(remote lock)',
 
2442
                self.repository.base.split('.bzr/')[0])
2368
2443
        if response[0] != 'ok':
2369
2444
            raise errors.UnexpectedSmartServerResponse(response)
2370
2445
        ok, branch_token, repo_token = response
2391
2466
            self._lock_mode = 'w'
2392
2467
            self._lock_count = 1
2393
2468
        elif self._lock_mode == 'r':
2394
 
            raise errors.ReadOnlyTransaction
 
2469
            raise errors.ReadOnlyError(self)
2395
2470
        else:
2396
2471
            if token is not None:
2397
2472
                # A token was given to lock_write, and we're relocking, so
2402
2477
            self._lock_count += 1
2403
2478
            # Re-lock the repository too.
2404
2479
            self.repository.lock_write(self._repo_lock_token)
2405
 
        return self._lock_token or None
 
2480
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
2406
2481
 
2407
2482
    def _unlock(self, branch_token, repo_token):
2408
2483
        err_context = {'token': str((branch_token, repo_token))}
2731
2806
        medium = self._branch._client._medium
2732
2807
        if medium._is_remote_before((1, 14)):
2733
2808
            return self._vfs_set_option(value, name, section)
 
2809
        if isinstance(value, dict):
 
2810
            if medium._is_remote_before((2, 2)):
 
2811
                return self._vfs_set_option(value, name, section)
 
2812
            return self._set_config_option_dict(value, name, section)
 
2813
        else:
 
2814
            return self._set_config_option(value, name, section)
 
2815
 
 
2816
    def _set_config_option(self, value, name, section):
2734
2817
        try:
2735
2818
            path = self._branch._remote_path()
2736
2819
            response = self._branch._client.call('Branch.set_config_option',
2737
2820
                path, self._branch._lock_token, self._branch._repo_lock_token,
2738
2821
                value.encode('utf8'), name, section or '')
2739
2822
        except errors.UnknownSmartMethod:
 
2823
            medium = self._branch._client._medium
2740
2824
            medium._remember_remote_is_before((1, 14))
2741
2825
            return self._vfs_set_option(value, name, section)
2742
2826
        if response != ():
2743
2827
            raise errors.UnexpectedSmartServerResponse(response)
2744
2828
 
 
2829
    def _serialize_option_dict(self, option_dict):
 
2830
        utf8_dict = {}
 
2831
        for key, value in option_dict.items():
 
2832
            if isinstance(key, unicode):
 
2833
                key = key.encode('utf8')
 
2834
            if isinstance(value, unicode):
 
2835
                value = value.encode('utf8')
 
2836
            utf8_dict[key] = value
 
2837
        return bencode.bencode(utf8_dict)
 
2838
 
 
2839
    def _set_config_option_dict(self, value, name, section):
 
2840
        try:
 
2841
            path = self._branch._remote_path()
 
2842
            serialised_dict = self._serialize_option_dict(value)
 
2843
            response = self._branch._client.call(
 
2844
                'Branch.set_config_option_dict',
 
2845
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
2846
                serialised_dict, name, section or '')
 
2847
        except errors.UnknownSmartMethod:
 
2848
            medium = self._branch._client._medium
 
2849
            medium._remember_remote_is_before((2, 2))
 
2850
            return self._vfs_set_option(value, name, section)
 
2851
        if response != ():
 
2852
            raise errors.UnexpectedSmartServerResponse(response)
 
2853
 
2745
2854
    def _real_object(self):
2746
2855
        self._branch._ensure_real()
2747
2856
        return self._branch._real_branch