~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/bzrdir.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

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