~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/bzrdir.py

  • Committer: Martin Packman
  • Date: 2011-11-17 13:45:49 UTC
  • mto: This revision was merged to the branch mainline in revision 6271.
  • Revision ID: martin.packman@canonical.com-20111117134549-080e1fhtrzoicexg
Only assert FileExists path in test_transform directory clash tests to avoid stringification fallout

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