/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/bzr/smart/bzrdir.py

  • Committer: Jelmer Vernooij
  • Date: 2019-10-20 15:03:13 UTC
  • mto: This revision was merged to the branch mainline in revision 7407.
  • Revision ID: jelmer@jelmer.uk-20191020150313-q06o6pncwr6ndu3t
Fix send with git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2010 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Server-side bzrdir related request implmentations."""
 
18
 
 
19
from __future__ import absolute_import
 
20
 
 
21
from ... import (
 
22
    bencode,
 
23
    branch,
 
24
    errors,
 
25
    repository,
 
26
    urlutils,
 
27
    )
 
28
from .. import (
 
29
    BzrProber,
 
30
    )
 
31
from ..bzrdir import (
 
32
    BzrDir,
 
33
    BzrDirFormat,
 
34
    )
 
35
from ...controldir import (
 
36
    network_format_registry,
 
37
    )
 
38
from ...sixish import PY3
 
39
from .request import (
 
40
    FailedSmartServerResponse,
 
41
    SmartServerRequest,
 
42
    SuccessfulSmartServerResponse,
 
43
    )
 
44
 
 
45
 
 
46
class SmartServerRequestOpenBzrDir(SmartServerRequest):
 
47
 
 
48
    def do(self, path):
 
49
        try:
 
50
            t = self.transport_from_client_path(path)
 
51
        except errors.PathNotChild:
 
52
            # The client is trying to ask about a path that they have no access
 
53
            # to.
 
54
            # Ideally we'd return a FailedSmartServerResponse here rather than
 
55
            # a "successful" negative, but we want to be compatibile with
 
56
            # clients that don't anticipate errors from this method.
 
57
            answer = b'no'
 
58
        else:
 
59
            bzr_prober = BzrProber()
 
60
            try:
 
61
                bzr_prober.probe_transport(t)
 
62
            except (errors.NotBranchError, errors.UnknownFormatError):
 
63
                answer = b'no'
 
64
            else:
 
65
                answer = b'yes'
 
66
        return SuccessfulSmartServerResponse((answer,))
 
67
 
 
68
 
 
69
class SmartServerRequestOpenBzrDir_2_1(SmartServerRequest):
 
70
 
 
71
    def do(self, path):
 
72
        """Is there a BzrDir present, and if so does it have a working tree?
 
73
 
 
74
        New in 2.1.
 
75
        """
 
76
        try:
 
77
            t = self.transport_from_client_path(path)
 
78
        except errors.PathNotChild:
 
79
            # The client is trying to ask about a path that they have no access
 
80
            # to.
 
81
            return SuccessfulSmartServerResponse((b'no',))
 
82
        try:
 
83
            bd = BzrDir.open_from_transport(t)
 
84
        except errors.NotBranchError:
 
85
            answer = (b'no',)
 
86
        else:
 
87
            answer = (b'yes',)
 
88
            if bd.has_workingtree():
 
89
                answer += (b'yes',)
 
90
            else:
 
91
                answer += (b'no',)
 
92
        return SuccessfulSmartServerResponse(answer)
 
93
 
 
94
 
 
95
class SmartServerRequestBzrDir(SmartServerRequest):
 
96
 
 
97
    def do(self, path, *args):
 
98
        """Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`."""
 
99
        try:
 
100
            self._bzrdir = BzrDir.open_from_transport(
 
101
                self.transport_from_client_path(path))
 
102
        except errors.NotBranchError as e:
 
103
            return FailedSmartServerResponse((b'nobranch',))
 
104
        return self.do_bzrdir_request(*args)
 
105
 
 
106
    def _boolean_to_yes_no(self, a_boolean):
 
107
        if a_boolean:
 
108
            return b'yes'
 
109
        else:
 
110
            return b'no'
 
111
 
 
112
    def _format_to_capabilities(self, repo_format):
 
113
        rich_root = self._boolean_to_yes_no(repo_format.rich_root_data)
 
114
        tree_ref = self._boolean_to_yes_no(
 
115
            repo_format.supports_tree_reference)
 
116
        external_lookup = self._boolean_to_yes_no(
 
117
            repo_format.supports_external_lookups)
 
118
        return rich_root, tree_ref, external_lookup
 
119
 
 
120
    def _repo_relpath(self, current_transport, repository):
 
121
        """Get the relative path for repository from current_transport."""
 
122
        # the relpath of the bzrdir in the found repository gives us the
 
123
        # path segments to pop-out.
 
124
        relpath = repository.user_transport.relpath(
 
125
            current_transport.base)
 
126
        if len(relpath):
 
127
            segments = ['..'] * len(relpath.split('/'))
 
128
        else:
 
129
            segments = []
 
130
        return '/'.join(segments)
 
131
 
 
132
 
 
133
class SmartServerBzrDirRequestDestroyBranch(SmartServerRequestBzrDir):
 
134
 
 
135
    def do_bzrdir_request(self, name=None):
 
136
        """Destroy the branch with the specified name.
 
137
 
 
138
        New in 2.5.0.
 
139
        :return: On success, 'ok'.
 
140
        """
 
141
        try:
 
142
            self._bzrdir.destroy_branch(
 
143
                name.decode('utf-8') if name is not None else None)
 
144
        except errors.NotBranchError as e:
 
145
            return FailedSmartServerResponse((b'nobranch',))
 
146
        return SuccessfulSmartServerResponse((b'ok',))
 
147
 
 
148
 
 
149
class SmartServerBzrDirRequestHasWorkingTree(SmartServerRequestBzrDir):
 
150
 
 
151
    def do_bzrdir_request(self, name=None):
 
152
        """Check whether there is a working tree present.
 
153
 
 
154
        New in 2.5.0.
 
155
 
 
156
        :return: If there is a working tree present, 'yes'.
 
157
            Otherwise 'no'.
 
158
        """
 
159
        if self._bzrdir.has_workingtree():
 
160
            return SuccessfulSmartServerResponse((b'yes', ))
 
161
        else:
 
162
            return SuccessfulSmartServerResponse((b'no', ))
 
163
 
 
164
 
 
165
class SmartServerBzrDirRequestDestroyRepository(SmartServerRequestBzrDir):
 
166
 
 
167
    def do_bzrdir_request(self, name=None):
 
168
        """Destroy the repository.
 
169
 
 
170
        New in 2.5.0.
 
171
 
 
172
        :return: On success, 'ok'.
 
173
        """
 
174
        try:
 
175
            self._bzrdir.destroy_repository()
 
176
        except errors.NoRepositoryPresent as e:
 
177
            return FailedSmartServerResponse((b'norepository',))
 
178
        return SuccessfulSmartServerResponse((b'ok',))
 
179
 
 
180
 
 
181
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
 
182
 
 
183
    def do_bzrdir_request(self, require_stacking):
 
184
        """Get the format that should be used when cloning from this dir.
 
185
 
 
186
        New in 1.13.
 
187
 
 
188
        :return: on success, a 3-tuple of network names for (control,
 
189
            repository, branch) directories, where '' signifies "not present".
 
190
            If this BzrDir contains a branch reference then this will fail with
 
191
            BranchReference; clients should resolve branch references before
 
192
            calling this RPC.
 
193
        """
 
194
        try:
 
195
            branch_ref = self._bzrdir.get_branch_reference()
 
196
        except errors.NotBranchError:
 
197
            branch_ref = None
 
198
        if branch_ref is not None:
 
199
            # The server shouldn't try to resolve references, and it quite
 
200
            # possibly can't reach them anyway.  The client needs to resolve
 
201
            # the branch reference to determine the cloning_metadir.
 
202
            return FailedSmartServerResponse((b'BranchReference',))
 
203
        if require_stacking == b"True":
 
204
            require_stacking = True
 
205
        else:
 
206
            require_stacking = False
 
207
        control_format = self._bzrdir.cloning_metadir(
 
208
            require_stacking=require_stacking)
 
209
        control_name = control_format.network_name()
 
210
        if not control_format.fixed_components:
 
211
            branch_name = (b'branch',
 
212
                           control_format.get_branch_format().network_name())
 
213
            repository_name = control_format.repository_format.network_name()
 
214
        else:
 
215
            # Only MetaDir has delegated formats today.
 
216
            branch_name = (b'branch', b'')
 
217
            repository_name = b''
 
218
        return SuccessfulSmartServerResponse((control_name, repository_name,
 
219
                                              branch_name))
 
220
 
 
221
 
 
222
class SmartServerBzrDirRequestCheckoutMetaDir(SmartServerRequestBzrDir):
 
223
    """Get the format to use for checkouts.
 
224
 
 
225
    New in 2.5.
 
226
 
 
227
    :return: on success, a 3-tuple of network names for (control,
 
228
        repository, branch) directories, where '' signifies "not present".
 
229
        If this BzrDir contains a branch reference then this will fail with
 
230
        BranchReference; clients should resolve branch references before
 
231
        calling this RPC (they should not try to create a checkout of a
 
232
        checkout).
 
233
    """
 
234
 
 
235
    def do_bzrdir_request(self):
 
236
        try:
 
237
            branch_ref = self._bzrdir.get_branch_reference()
 
238
        except errors.NotBranchError:
 
239
            branch_ref = None
 
240
        if branch_ref is not None:
 
241
            # The server shouldn't try to resolve references, and it quite
 
242
            # possibly can't reach them anyway.  The client needs to resolve
 
243
            # the branch reference to determine the cloning_metadir.
 
244
            return FailedSmartServerResponse((b'BranchReference',))
 
245
        control_format = self._bzrdir.checkout_metadir()
 
246
        control_name = control_format.network_name()
 
247
        if not control_format.fixed_components:
 
248
            branch_name = control_format.get_branch_format().network_name()
 
249
            repo_name = control_format.repository_format.network_name()
 
250
        else:
 
251
            branch_name = b''
 
252
            repo_name = b''
 
253
        return SuccessfulSmartServerResponse(
 
254
            (control_name, repo_name, branch_name))
 
255
 
 
256
 
 
257
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
 
258
 
 
259
    def do(self, path, network_name):
 
260
        """Create a branch in the bzr dir at path.
 
261
 
 
262
        This operates precisely like 'bzrdir.create_branch'.
 
263
 
 
264
        If a bzrdir is not present, an exception is propogated
 
265
        rather than 'no branch' because these are different conditions (and
 
266
        this method should only be called after establishing that a bzr dir
 
267
        exists anyway).
 
268
 
 
269
        This is the initial version of this method introduced to the smart
 
270
        server for 1.13.
 
271
 
 
272
        :param path: The path to the bzrdir.
 
273
        :param network_name: The network name of the branch type to create.
 
274
        :return: ('ok', branch_format, repo_path, rich_root, tree_ref,
 
275
            external_lookup, repo_format)
 
276
        """
 
277
        bzrdir = BzrDir.open_from_transport(
 
278
            self.transport_from_client_path(path))
 
279
        format = branch.network_format_registry.get(network_name)
 
280
        bzrdir.branch_format = format
 
281
        result = format.initialize(bzrdir, name="")
 
282
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
283
            result.repository._format)
 
284
        branch_format = result._format.network_name()
 
285
        repo_format = result.repository._format.network_name()
 
286
        repo_path = self._repo_relpath(bzrdir.root_transport,
 
287
                                       result.repository)
 
288
        # branch format, repo relpath, rich_root, tree_ref, external_lookup,
 
289
        # repo_network_name
 
290
        return SuccessfulSmartServerResponse((b'ok', branch_format, repo_path,
 
291
                                              rich_root, tree_ref, external_lookup, repo_format))
 
292
 
 
293
 
 
294
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
 
295
 
 
296
    def do(self, path, network_name, shared):
 
297
        """Create a repository in the bzr dir at path.
 
298
 
 
299
        This operates precisely like 'bzrdir.create_repository'.
 
300
 
 
301
        If a bzrdir is not present, an exception is propagated
 
302
        rather than 'no branch' because these are different conditions (and
 
303
        this method should only be called after establishing that a bzr dir
 
304
        exists anyway).
 
305
 
 
306
        This is the initial version of this method introduced to the smart
 
307
        server for 1.13.
 
308
 
 
309
        :param path: The path to the bzrdir.
 
310
        :param network_name: The network name of the repository type to create.
 
311
        :param shared: The value to pass create_repository for the shared
 
312
            parameter.
 
313
        :return: (ok, rich_root, tree_ref, external_lookup, network_name)
 
314
        """
 
315
        bzrdir = BzrDir.open_from_transport(
 
316
            self.transport_from_client_path(path))
 
317
        shared = shared == b'True'
 
318
        format = repository.network_format_registry.get(network_name)
 
319
        bzrdir.repository_format = format
 
320
        result = format.initialize(bzrdir, shared=shared)
 
321
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
322
            result._format)
 
323
        return SuccessfulSmartServerResponse((b'ok', rich_root, tree_ref,
 
324
                                              external_lookup, result._format.network_name()))
 
325
 
 
326
 
 
327
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
 
328
 
 
329
    def _find(self, path):
 
330
        """try to find a repository from path upwards
 
331
 
 
332
        This operates precisely like 'bzrdir.find_repository'.
 
333
 
 
334
        :return: (relpath, rich_root, tree_ref, external_lookup, network_name).
 
335
            All are strings, relpath is a / prefixed path, the next three are
 
336
            either 'yes' or 'no', and the last is a repository format network
 
337
            name.
 
338
        :raises errors.NoRepositoryPresent: When there is no repository
 
339
            present.
 
340
        """
 
341
        bzrdir = BzrDir.open_from_transport(
 
342
            self.transport_from_client_path(path))
 
343
        repository = bzrdir.find_repository()
 
344
        path = self._repo_relpath(bzrdir.root_transport, repository)
 
345
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
346
            repository._format)
 
347
        network_name = repository._format.network_name()
 
348
        return path, rich_root, tree_ref, external_lookup, network_name
 
349
 
 
350
 
 
351
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
 
352
 
 
353
    def do(self, path):
 
354
        """try to find a repository from path upwards
 
355
 
 
356
        This operates precisely like 'bzrdir.find_repository'.
 
357
 
 
358
        If a bzrdir is not present, an exception is propagated
 
359
        rather than 'no branch' because these are different conditions.
 
360
 
 
361
        This is the initial version of this method introduced with the smart
 
362
        server. Modern clients will try the V2 method that adds support for the
 
363
        supports_external_lookups attribute.
 
364
 
 
365
        :return: norepository or ok, relpath.
 
366
        """
 
367
        try:
 
368
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
 
369
            return SuccessfulSmartServerResponse((b'ok', path.encode('utf-8'), rich_root, tree_ref))
 
370
        except errors.NoRepositoryPresent:
 
371
            return FailedSmartServerResponse((b'norepository', ))
 
372
 
 
373
 
 
374
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
 
375
 
 
376
    def do(self, path):
 
377
        """try to find a repository from path upwards
 
378
 
 
379
        This operates precisely like 'bzrdir.find_repository'.
 
380
 
 
381
        If a bzrdir is not present, an exception is propagated
 
382
        rather than 'no branch' because these are different conditions.
 
383
 
 
384
        This is the second edition of this method introduced in bzr 1.3, which
 
385
        returns information about the supports_external_lookups format
 
386
        attribute too.
 
387
 
 
388
        :return: norepository or ok, relpath, rich_root, tree_ref,
 
389
            external_lookup.
 
390
        """
 
391
        try:
 
392
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
 
393
            return SuccessfulSmartServerResponse(
 
394
                (b'ok', path.encode('utf-8'), rich_root, tree_ref, external_lookup))
 
395
        except errors.NoRepositoryPresent:
 
396
            return FailedSmartServerResponse((b'norepository', ))
 
397
 
 
398
 
 
399
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
 
400
 
 
401
    def do(self, path):
 
402
        """try to find a repository from path upwards
 
403
 
 
404
        This operates precisely like 'bzrdir.find_repository'.
 
405
 
 
406
        If a bzrdir is not present, an exception is propogated
 
407
        rather than 'no branch' because these are different conditions.
 
408
 
 
409
        This is the third edition of this method introduced in bzr 1.13, which
 
410
        returns information about the network name of the repository format.
 
411
 
 
412
        :return: norepository or ok, relpath, rich_root, tree_ref,
 
413
            external_lookup, network_name.
 
414
        """
 
415
        try:
 
416
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
 
417
            return SuccessfulSmartServerResponse(
 
418
                (b'ok', path.encode('utf-8'), rich_root, tree_ref, external_lookup, name))
 
419
        except errors.NoRepositoryPresent:
 
420
            return FailedSmartServerResponse((b'norepository', ))
 
421
 
 
422
 
 
423
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
 
424
 
 
425
    def do_bzrdir_request(self):
 
426
        """Get the configuration bytes for a config file in bzrdir.
 
427
 
 
428
        The body is not utf8 decoded - it is the literal bytestream from disk.
 
429
        """
 
430
        config = self._bzrdir._get_config()
 
431
        if config is None:
 
432
            content = b''
 
433
        else:
 
434
            content = config._get_config_file().read()
 
435
        return SuccessfulSmartServerResponse((), content)
 
436
 
 
437
 
 
438
class SmartServerBzrDirRequestGetBranches(SmartServerRequestBzrDir):
 
439
 
 
440
    def do_bzrdir_request(self):
 
441
        """Get the branches in a control directory.
 
442
 
 
443
        The body is a bencoded dictionary, with values similar to the return
 
444
        value of the open branch request.
 
445
        """
 
446
        branches = self._bzrdir.get_branches()
 
447
        ret = {}
 
448
        for name, b in branches.items():
 
449
            if name is None:
 
450
                name = b""
 
451
            ret[name.encode('utf-8')] = (b"branch", b._format.network_name())
 
452
        return SuccessfulSmartServerResponse(
 
453
            (b"success", ), bencode.bencode(ret))
 
454
 
 
455
 
 
456
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
 
457
 
 
458
    def do(self, path):
 
459
        """Initialize a bzrdir at path.
 
460
 
 
461
        The default format of the server is used.
 
462
        :return: SmartServerResponse(('ok', ))
 
463
        """
 
464
        target_transport = self.transport_from_client_path(path)
 
465
        BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
 
466
        return SuccessfulSmartServerResponse((b'ok', ))
 
467
 
 
468
 
 
469
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
 
470
 
 
471
    def parse_NoneTrueFalse(self, arg):
 
472
        if not arg:
 
473
            return None
 
474
        if arg == b'False':
 
475
            return False
 
476
        if arg == b'True':
 
477
            return True
 
478
        raise AssertionError("invalid arg %r" % arg)
 
479
 
 
480
    def parse_NoneBytestring(self, arg):
 
481
        return arg or None
 
482
 
 
483
    def parse_NoneString(self, arg):
 
484
        if not arg:
 
485
            return None
 
486
        if PY3:
 
487
            return arg.decode('utf-8')
 
488
        else:
 
489
            return arg
 
490
 
 
491
    def _serialize_NoneTrueFalse(self, arg):
 
492
        if arg is False:
 
493
            return b'False'
 
494
        if not arg:
 
495
            return b''
 
496
        return b'True'
 
497
 
 
498
    def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
 
499
           force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
 
500
           make_working_trees, shared_repo):
 
501
        """Initialize a bzrdir at path as per
 
502
        BzrDirFormat.initialize_on_transport_ex.
 
503
 
 
504
        New in 1.16.  (Replaces BzrDirFormat.initialize_ex verb from 1.15).
 
505
 
 
506
        :return: return SuccessfulSmartServerResponse((repo_path, rich_root,
 
507
            tree_ref, external_lookup, repo_network_name,
 
508
            repo_bzrdir_network_name, bzrdir_format_network_name,
 
509
            NoneTrueFalse(stacking), final_stack, final_stack_pwd,
 
510
            repo_lock_token))
 
511
        """
 
512
        target_transport = self.transport_from_client_path(path)
 
513
        format = network_format_registry.get(bzrdir_network_name)
 
514
        use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
 
515
        create_prefix = self.parse_NoneTrueFalse(create_prefix)
 
516
        force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
 
517
        stacked_on = self.parse_NoneString(stacked_on)
 
518
        stack_on_pwd = self.parse_NoneString(stack_on_pwd)
 
519
        make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
 
520
        shared_repo = self.parse_NoneTrueFalse(shared_repo)
 
521
        if stack_on_pwd == b'.':
 
522
            stack_on_pwd = target_transport.base.encode('utf-8')
 
523
        repo_format_name = self.parse_NoneBytestring(repo_format_name)
 
524
        repo, bzrdir, stacking, repository_policy = \
 
525
            format.initialize_on_transport_ex(target_transport,
 
526
                                              use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
527
                                              force_new_repo=force_new_repo, stacked_on=stacked_on,
 
528
                                              stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
529
                                              make_working_trees=make_working_trees, shared_repo=shared_repo)
 
530
        if repo is None:
 
531
            repo_path = ''
 
532
            repo_name = b''
 
533
            rich_root = tree_ref = external_lookup = b''
 
534
            repo_bzrdir_name = b''
 
535
            final_stack = None
 
536
            final_stack_pwd = None
 
537
            repo_lock_token = b''
 
538
        else:
 
539
            repo_path = self._repo_relpath(bzrdir.root_transport, repo)
 
540
            if repo_path == '':
 
541
                repo_path = '.'
 
542
            rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
543
                repo._format)
 
544
            repo_name = repo._format.network_name()
 
545
            repo_bzrdir_name = repo.controldir._format.network_name()
 
546
            final_stack = repository_policy._stack_on
 
547
            final_stack_pwd = repository_policy._stack_on_pwd
 
548
            # It is returned locked, but we need to do the lock to get the lock
 
549
            # token.
 
550
            repo.unlock()
 
551
            repo_lock_token = repo.lock_write().repository_token or b''
 
552
            if repo_lock_token:
 
553
                repo.leave_lock_in_place()
 
554
            repo.unlock()
 
555
        final_stack = final_stack or ''
 
556
        final_stack_pwd = final_stack_pwd or ''
 
557
 
 
558
        # We want this to be relative to the bzrdir.
 
559
        if final_stack_pwd:
 
560
            final_stack_pwd = urlutils.relative_url(
 
561
                target_transport.base, final_stack_pwd)
 
562
 
 
563
        # Can't meaningfully return a root path.
 
564
        if final_stack.startswith('/'):
 
565
            client_path = self._root_client_path + final_stack[1:]
 
566
            final_stack = urlutils.relative_url(
 
567
                self._root_client_path, client_path)
 
568
            final_stack_pwd = '.'
 
569
 
 
570
        return SuccessfulSmartServerResponse((repo_path.encode('utf-8'),
 
571
                                              rich_root, tree_ref, external_lookup, repo_name, repo_bzrdir_name,
 
572
                                              bzrdir._format.network_name(),
 
573
                                              self._serialize_NoneTrueFalse(
 
574
                                                  stacking), final_stack.encode('utf-8'),
 
575
                                              final_stack_pwd.encode('utf-8'), repo_lock_token))
 
576
 
 
577
 
 
578
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
 
579
 
 
580
    def do_bzrdir_request(self):
 
581
        """open a branch at path and return the branch reference or branch."""
 
582
        try:
 
583
            reference_url = self._bzrdir.get_branch_reference()
 
584
            if reference_url is None:
 
585
                reference_url = ''
 
586
            return SuccessfulSmartServerResponse((b'ok', reference_url.encode('utf-8')))
 
587
        except errors.NotBranchError as e:
 
588
            return FailedSmartServerResponse((b'nobranch',))
 
589
 
 
590
 
 
591
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
 
592
 
 
593
    def do_bzrdir_request(self):
 
594
        """open a branch at path and return the reference or format."""
 
595
        try:
 
596
            reference_url = self._bzrdir.get_branch_reference()
 
597
            if reference_url is None:
 
598
                br = self._bzrdir.open_branch(ignore_fallbacks=True)
 
599
                format = br._format.network_name()
 
600
                return SuccessfulSmartServerResponse((b'branch', format))
 
601
            else:
 
602
                return SuccessfulSmartServerResponse((b'ref', reference_url.encode('utf-8')))
 
603
        except errors.NotBranchError as e:
 
604
            return FailedSmartServerResponse((b'nobranch',))
 
605
 
 
606
 
 
607
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
 
608
 
 
609
    def do_bzrdir_request(self):
 
610
        """Open a branch at path and return the reference or format.
 
611
 
 
612
        This version introduced in 2.1.
 
613
 
 
614
        Differences to SmartServerRequestOpenBranchV2:
 
615
          * can return 2-element ('nobranch', extra), where 'extra' is a string
 
616
            with an explanation like 'location is a repository'.  Previously
 
617
            a 'nobranch' response would never have more than one element.
 
618
        """
 
619
        try:
 
620
            reference_url = self._bzrdir.get_branch_reference()
 
621
            if reference_url is None:
 
622
                br = self._bzrdir.open_branch(ignore_fallbacks=True)
 
623
                format = br._format.network_name()
 
624
                return SuccessfulSmartServerResponse((b'branch', format))
 
625
            else:
 
626
                return SuccessfulSmartServerResponse((b'ref', reference_url.encode('utf-8')))
 
627
        except errors.NotBranchError as e:
 
628
            # Stringify the exception so that its .detail attribute will be
 
629
            # filled out.
 
630
            str(e)
 
631
            resp = (b'nobranch',)
 
632
            detail = e.detail
 
633
            if detail:
 
634
                if detail.startswith(': '):
 
635
                    detail = detail[2:]
 
636
                resp += (detail.encode('utf-8'),)
 
637
            return FailedSmartServerResponse(resp)