~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/bzrdir.py

  • Committer: John Arbash Meinel
  • Date: 2005-09-15 21:35:53 UTC
  • mfrom: (907.1.57)
  • mto: (1393.2.1)
  • mto: This revision was merged to the branch mainline in revision 1396.
  • Revision ID: john@arbash-meinel.com-20050915213552-a6c83a5ef1e20897
(broken) Transport work is merged in. Tests do not pass yet.

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
 
        # XXX: There should be a method that tells us that the format does/does
154
 
        # not have subformats.
155
 
        if isinstance(control_format, BzrDirMetaFormat1):
156
 
            branch_name = ('branch',
157
 
                control_format.get_branch_format().network_name())
158
 
            repository_name = control_format.repository_format.network_name()
159
 
        else:
160
 
            # Only MetaDir has delegated formats today.
161
 
            branch_name = ('branch', '')
162
 
            repository_name = ''
163
 
        return SuccessfulSmartServerResponse((control_name, repository_name,
164
 
            branch_name))
165
 
 
166
 
 
167
 
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
168
 
 
169
 
    def do(self, path, network_name):
170
 
        """Create a branch in the bzr dir at path.
171
 
 
172
 
        This operates precisely like 'bzrdir.create_branch'.
173
 
 
174
 
        If a bzrdir is not present, an exception is propogated
175
 
        rather than 'no branch' because these are different conditions (and
176
 
        this method should only be called after establishing that a bzr dir
177
 
        exists anyway).
178
 
 
179
 
        This is the initial version of this method introduced to the smart
180
 
        server for 1.13.
181
 
 
182
 
        :param path: The path to the bzrdir.
183
 
        :param network_name: The network name of the branch type to create.
184
 
        :return: ('ok', branch_format, repo_path, rich_root, tree_ref,
185
 
            external_lookup, repo_format)
186
 
        """
187
 
        bzrdir = BzrDir.open_from_transport(
188
 
            self.transport_from_client_path(path))
189
 
        format = branch.network_format_registry.get(network_name)
190
 
        bzrdir.branch_format = format
191
 
        result = format.initialize(bzrdir)
192
 
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
193
 
            result.repository._format)
194
 
        branch_format = result._format.network_name()
195
 
        repo_format = result.repository._format.network_name()
196
 
        repo_path = self._repo_relpath(bzrdir.root_transport,
197
 
            result.repository)
198
 
        # branch format, repo relpath, rich_root, tree_ref, external_lookup,
199
 
        # repo_network_name
200
 
        return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
201
 
            rich_root, tree_ref, external_lookup, repo_format))
202
 
 
203
 
 
204
 
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
205
 
 
206
 
    def do(self, path, network_name, shared):
207
 
        """Create a repository in the bzr dir at path.
208
 
 
209
 
        This operates precisely like 'bzrdir.create_repository'.
210
 
 
211
 
        If a bzrdir is not present, an exception is propagated
212
 
        rather than 'no branch' because these are different conditions (and
213
 
        this method should only be called after establishing that a bzr dir
214
 
        exists anyway).
215
 
 
216
 
        This is the initial version of this method introduced to the smart
217
 
        server for 1.13.
218
 
 
219
 
        :param path: The path to the bzrdir.
220
 
        :param network_name: The network name of the repository type to create.
221
 
        :param shared: The value to pass create_repository for the shared
222
 
            parameter.
223
 
        :return: (ok, rich_root, tree_ref, external_lookup, network_name)
224
 
        """
225
 
        bzrdir = BzrDir.open_from_transport(
226
 
            self.transport_from_client_path(path))
227
 
        shared = shared == 'True'
228
 
        format = repository.network_format_registry.get(network_name)
229
 
        bzrdir.repository_format = format
230
 
        result = format.initialize(bzrdir, shared=shared)
231
 
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
232
 
            result._format)
233
 
        return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
234
 
            external_lookup, result._format.network_name()))
235
 
 
236
 
 
237
 
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
238
 
 
239
 
    def _find(self, path):
240
 
        """try to find a repository from path upwards
241
 
 
242
 
        This operates precisely like 'bzrdir.find_repository'.
243
 
 
244
 
        :return: (relpath, rich_root, tree_ref, external_lookup, network_name).
245
 
            All are strings, relpath is a / prefixed path, the next three are
246
 
            either 'yes' or 'no', and the last is a repository format network
247
 
            name.
248
 
        :raises errors.NoRepositoryPresent: When there is no repository
249
 
            present.
250
 
        """
251
 
        bzrdir = BzrDir.open_from_transport(
252
 
            self.transport_from_client_path(path))
253
 
        repository = bzrdir.find_repository()
254
 
        path = self._repo_relpath(bzrdir.root_transport, repository)
255
 
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
256
 
            repository._format)
257
 
        network_name = repository._format.network_name()
258
 
        return path, rich_root, tree_ref, external_lookup, network_name
259
 
 
260
 
 
261
 
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
262
 
 
263
 
    def do(self, path):
264
 
        """try to find a repository from path upwards
265
 
 
266
 
        This operates precisely like 'bzrdir.find_repository'.
267
 
 
268
 
        If a bzrdir is not present, an exception is propagated
269
 
        rather than 'no branch' because these are different conditions.
270
 
 
271
 
        This is the initial version of this method introduced with the smart
272
 
        server. Modern clients will try the V2 method that adds support for the
273
 
        supports_external_lookups attribute.
274
 
 
275
 
        :return: norepository or ok, relpath.
276
 
        """
277
 
        try:
278
 
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
279
 
            return SuccessfulSmartServerResponse(('ok', path, rich_root, tree_ref))
280
 
        except errors.NoRepositoryPresent:
281
 
            return FailedSmartServerResponse(('norepository', ))
282
 
 
283
 
 
284
 
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
285
 
 
286
 
    def do(self, path):
287
 
        """try to find a repository from path upwards
288
 
 
289
 
        This operates precisely like 'bzrdir.find_repository'.
290
 
 
291
 
        If a bzrdir is not present, an exception is propagated
292
 
        rather than 'no branch' because these are different conditions.
293
 
 
294
 
        This is the second edition of this method introduced in bzr 1.3, which
295
 
        returns information about the supports_external_lookups format
296
 
        attribute too.
297
 
 
298
 
        :return: norepository or ok, relpath, rich_root, tree_ref,
299
 
            external_lookup.
300
 
        """
301
 
        try:
302
 
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
303
 
            return SuccessfulSmartServerResponse(
304
 
                ('ok', path, rich_root, tree_ref, external_lookup))
305
 
        except errors.NoRepositoryPresent:
306
 
            return FailedSmartServerResponse(('norepository', ))
307
 
 
308
 
 
309
 
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
310
 
 
311
 
    def do(self, path):
312
 
        """try to find a repository from path upwards
313
 
 
314
 
        This operates precisely like 'bzrdir.find_repository'.
315
 
 
316
 
        If a bzrdir is not present, an exception is propogated
317
 
        rather than 'no branch' because these are different conditions.
318
 
 
319
 
        This is the third edition of this method introduced in bzr 1.13, which
320
 
        returns information about the network name of the repository format.
321
 
 
322
 
        :return: norepository or ok, relpath, rich_root, tree_ref,
323
 
            external_lookup, network_name.
324
 
        """
325
 
        try:
326
 
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
327
 
            return SuccessfulSmartServerResponse(
328
 
                ('ok', path, rich_root, tree_ref, external_lookup, name))
329
 
        except errors.NoRepositoryPresent:
330
 
            return FailedSmartServerResponse(('norepository', ))
331
 
 
332
 
 
333
 
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
334
 
 
335
 
    def do_bzrdir_request(self):
336
 
        """Get the configuration bytes for a config file in bzrdir.
337
 
        
338
 
        The body is not utf8 decoded - it is the literal bytestream from disk.
339
 
        """
340
 
        config = self._bzrdir._get_config()
341
 
        if config is None:
342
 
            content = ''
343
 
        else:
344
 
            content = config._get_config_file().read()
345
 
        return SuccessfulSmartServerResponse((), content)
346
 
 
347
 
 
348
 
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
349
 
 
350
 
    def do(self, path):
351
 
        """Initialize a bzrdir at path.
352
 
 
353
 
        The default format of the server is used.
354
 
        :return: SmartServerResponse(('ok', ))
355
 
        """
356
 
        target_transport = self.transport_from_client_path(path)
357
 
        BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
358
 
        return SuccessfulSmartServerResponse(('ok', ))
359
 
 
360
 
 
361
 
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
362
 
 
363
 
    def parse_NoneTrueFalse(self, arg):
364
 
        if not arg:
365
 
            return None
366
 
        if arg == 'False':
367
 
            return False
368
 
        if arg == 'True':
369
 
            return True
370
 
        raise AssertionError("invalid arg %r" % arg)
371
 
 
372
 
    def parse_NoneString(self, arg):
373
 
        return arg or None
374
 
 
375
 
    def _serialize_NoneTrueFalse(self, arg):
376
 
        if arg is False:
377
 
            return 'False'
378
 
        if not arg:
379
 
            return ''
380
 
        return 'True'
381
 
 
382
 
    def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
383
 
        force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
384
 
        make_working_trees, shared_repo):
385
 
        """Initialize a bzrdir at path as per
386
 
        BzrDirFormat.initialize_on_transport_ex.
387
 
 
388
 
        New in 1.16.  (Replaces BzrDirFormat.initialize_ex verb from 1.15).
389
 
 
390
 
        :return: return SuccessfulSmartServerResponse((repo_path, rich_root,
391
 
            tree_ref, external_lookup, repo_network_name,
392
 
            repo_bzrdir_network_name, bzrdir_format_network_name,
393
 
            NoneTrueFalse(stacking), final_stack, final_stack_pwd,
394
 
            repo_lock_token))
395
 
        """
396
 
        target_transport = self.transport_from_client_path(path)
397
 
        format = network_format_registry.get(bzrdir_network_name)
398
 
        use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
399
 
        create_prefix = self.parse_NoneTrueFalse(create_prefix)
400
 
        force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
401
 
        stacked_on = self.parse_NoneString(stacked_on)
402
 
        stack_on_pwd = self.parse_NoneString(stack_on_pwd)
403
 
        make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
404
 
        shared_repo = self.parse_NoneTrueFalse(shared_repo)
405
 
        if stack_on_pwd == '.':
406
 
            stack_on_pwd = target_transport.base
407
 
        repo_format_name = self.parse_NoneString(repo_format_name)
408
 
        repo, bzrdir, stacking, repository_policy = \
409
 
            format.initialize_on_transport_ex(target_transport,
410
 
            use_existing_dir=use_existing_dir, create_prefix=create_prefix,
411
 
            force_new_repo=force_new_repo, stacked_on=stacked_on,
412
 
            stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
413
 
            make_working_trees=make_working_trees, shared_repo=shared_repo)
414
 
        if repo is None:
415
 
            repo_path = ''
416
 
            repo_name = ''
417
 
            rich_root = tree_ref = external_lookup = ''
418
 
            repo_bzrdir_name = ''
419
 
            final_stack = None
420
 
            final_stack_pwd = None
421
 
            repo_lock_token = ''
422
 
        else:
423
 
            repo_path = self._repo_relpath(bzrdir.root_transport, repo)
424
 
            if repo_path == '':
425
 
                repo_path = '.'
426
 
            rich_root, tree_ref, external_lookup = self._format_to_capabilities(
427
 
                repo._format)
428
 
            repo_name = repo._format.network_name()
429
 
            repo_bzrdir_name = repo.bzrdir._format.network_name()
430
 
            final_stack = repository_policy._stack_on
431
 
            final_stack_pwd = repository_policy._stack_on_pwd
432
 
            # It is returned locked, but we need to do the lock to get the lock
433
 
            # token.
434
 
            repo.unlock()
435
 
            repo_lock_token = repo.lock_write().repository_token or ''
436
 
            if repo_lock_token:
437
 
                repo.leave_lock_in_place()
438
 
            repo.unlock()
439
 
        final_stack = final_stack or ''
440
 
        final_stack_pwd = final_stack_pwd or ''
441
 
 
442
 
        # We want this to be relative to the bzrdir.
443
 
        if final_stack_pwd:
444
 
            final_stack_pwd = urlutils.relative_url(
445
 
                target_transport.base, final_stack_pwd)
446
 
 
447
 
        # Can't meaningfully return a root path.
448
 
        if final_stack.startswith('/'):
449
 
            client_path = self._root_client_path + final_stack[1:]
450
 
            final_stack = urlutils.relative_url(
451
 
                self._root_client_path, client_path)
452
 
            final_stack_pwd = '.'
453
 
 
454
 
        return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
455
 
            external_lookup, repo_name, repo_bzrdir_name,
456
 
            bzrdir._format.network_name(),
457
 
            self._serialize_NoneTrueFalse(stacking), final_stack,
458
 
            final_stack_pwd, repo_lock_token))
459
 
 
460
 
 
461
 
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
462
 
 
463
 
    def do_bzrdir_request(self):
464
 
        """open a branch at path and return the branch reference or branch."""
465
 
        try:
466
 
            reference_url = self._bzrdir.get_branch_reference()
467
 
            if reference_url is None:
468
 
                return SuccessfulSmartServerResponse(('ok', ''))
469
 
            else:
470
 
                return SuccessfulSmartServerResponse(('ok', reference_url))
471
 
        except errors.NotBranchError, e:
472
 
            return FailedSmartServerResponse(('nobranch',))
473
 
 
474
 
 
475
 
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
476
 
 
477
 
    def do_bzrdir_request(self):
478
 
        """open a branch at path and return the reference or format."""
479
 
        try:
480
 
            reference_url = self._bzrdir.get_branch_reference()
481
 
            if reference_url is None:
482
 
                br = self._bzrdir.open_branch(ignore_fallbacks=True)
483
 
                format = br._format.network_name()
484
 
                return SuccessfulSmartServerResponse(('branch', format))
485
 
            else:
486
 
                return SuccessfulSmartServerResponse(('ref', reference_url))
487
 
        except errors.NotBranchError, e:
488
 
            return FailedSmartServerResponse(('nobranch',))
489
 
 
490
 
 
491
 
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
492
 
 
493
 
    def do_bzrdir_request(self):
494
 
        """Open a branch at path and return the reference or format.
495
 
        
496
 
        This version introduced in 2.1.
497
 
 
498
 
        Differences to SmartServerRequestOpenBranchV2:
499
 
          * can return 2-element ('nobranch', extra), where 'extra' is a string
500
 
            with an explanation like 'location is a repository'.  Previously
501
 
            a 'nobranch' response would never have more than one element.
502
 
        """
503
 
        try:
504
 
            reference_url = self._bzrdir.get_branch_reference()
505
 
            if reference_url is None:
506
 
                br = self._bzrdir.open_branch(ignore_fallbacks=True)
507
 
                format = br._format.network_name()
508
 
                return SuccessfulSmartServerResponse(('branch', format))
509
 
            else:
510
 
                return SuccessfulSmartServerResponse(('ref', reference_url))
511
 
        except errors.NotBranchError, e:
512
 
            # Stringify the exception so that its .detail attribute will be
513
 
            # filled out.
514
 
            str(e)
515
 
            resp = ('nobranch',)
516
 
            detail = e.detail
517
 
            if detail:
518
 
                if detail.startswith(': '):
519
 
                    detail = detail[2:]
520
 
                resp += (detail,)
521
 
            return FailedSmartServerResponse(resp)
522