~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/bzrdir.py

  • Committer: Jelmer Vernooij
  • Date: 2011-11-30 20:02:16 UTC
  • mto: This revision was merged to the branch mainline in revision 6333.
  • Revision ID: jelmer@samba.org-20111130200216-aoju21pdl20d1gkd
Consistently pass tree path when exporting.

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