~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/bzrdir.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-05-06 04:07:37 UTC
  • mfrom: (4332.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090506040737-tdiqyojy2uia33qb
Make it easier to blackbox test rules (Marius Kruger)

Show diffs side-by-side

added added

removed removed

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