~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/bzrdir.py

  • Committer: Robert Collins
  • Date: 2009-07-07 04:32:13 UTC
  • mto: This revision was merged to the branch mainline in revision 4524.
  • Revision ID: robertc@robertcollins.net-20090707043213-4hjjhgr40iq7gk2d
More informative assertions in xml serialisation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Server-side bzrdir related request implmentations."""
18
18
 
19
19
 
20
 
from bzrlib import errors
21
 
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
20
from bzrlib import branch, errors, repository, urlutils
 
21
from bzrlib.bzrdir import (
 
22
    BzrDir,
 
23
    BzrDirFormat,
 
24
    BzrDirMetaFormat1,
 
25
    network_format_registry,
 
26
    )
22
27
from bzrlib.smart.request import (
23
28
    FailedSmartServerResponse,
24
29
    SmartServerRequest,
30
35
 
31
36
    def do(self, path):
32
37
        from bzrlib.bzrdir import BzrDirFormat
33
 
        t = self._backing_transport.clone(path)
34
 
        default_format = BzrDirFormat.get_default_format()
35
 
        real_bzrdir = default_format.open(t, _found=True)
36
38
        try:
37
 
            real_bzrdir._format.probe_transport(t)
38
 
        except (errors.NotBranchError, errors.UnknownFormatError):
 
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.
39
46
            answer = 'no'
40
47
        else:
41
 
            answer = 'yes'
 
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'
42
56
        return SuccessfulSmartServerResponse((answer,))
43
57
 
44
58
 
45
 
class SmartServerRequestFindRepository(SmartServerRequest):
 
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):
46
234
 
47
235
    def do(self, path):
48
236
        """try to find a repository from path upwards
49
 
        
 
237
 
50
238
        This operates precisely like 'bzrdir.find_repository'.
51
 
        
52
 
        If a bzrdir is not present, an exception is propogated
 
239
 
 
240
        If a bzrdir is not present, an exception is propagated
53
241
        rather than 'no branch' because these are different conditions.
54
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
 
55
247
        :return: norepository or ok, relpath.
56
248
        """
57
 
        bzrdir = BzrDir.open_from_transport(self._backing_transport.clone(path))
58
 
        try:
59
 
            repository = bzrdir.find_repository()
60
 
            # the relpath of the bzrdir in the found repository gives us the 
61
 
            # path segments to pop-out.
62
 
            relpath = repository.bzrdir.root_transport.relpath(bzrdir.root_transport.base)
63
 
            if len(relpath):
64
 
                segments = ['..'] * len(relpath.split('/'))
65
 
            else:
66
 
                segments = []
67
 
            if repository.supports_rich_root():
68
 
                rich_root = 'yes'
69
 
            else:
70
 
                rich_root = 'no'
71
 
            if repository._format.supports_tree_reference:
72
 
                tree_ref = 'yes'
73
 
            else:
74
 
                tree_ref = 'no'
75
 
            return SuccessfulSmartServerResponse(('ok', '/'.join(segments), rich_root, tree_ref))
76
 
        except errors.NoRepositoryPresent:
77
 
            return FailedSmartServerResponse(('norepository', ))
 
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)
78
318
 
79
319
 
80
320
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
85
325
        The default format of the server is used.
86
326
        :return: SmartServerResponse(('ok', ))
87
327
        """
88
 
        target_transport = self._backing_transport.clone(path)
 
328
        target_transport = self.transport_from_client_path(path)
89
329
        BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
90
330
        return SuccessfulSmartServerResponse(('ok', ))
91
331
 
92
332
 
93
 
class SmartServerRequestOpenBranch(SmartServerRequest):
94
 
 
95
 
    def do(self, path):
96
 
        """try to open a branch at path and return ok/nobranch.
97
 
        
98
 
        If a bzrdir is not present, an exception is propogated
99
 
        rather than 'no branch' because these are different conditions.
 
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
 
358
        BzrDirFormat.initialize_on_transport_ex.
 
359
 
 
360
        New in 1.16.  (Replaces BzrDirFormat.initialize_ex verb from 1.15).
 
361
 
 
362
        :return: return SuccessfulSmartServerResponse((repo_path, rich_root,
 
363
            tree_ref, external_lookup, repo_network_name,
 
364
            repo_bzrdir_network_name, bzrdir_format_network_name,
 
365
            NoneTrueFalse(stacking), final_stack, final_stack_pwd,
 
366
            repo_lock_token))
100
367
        """
101
 
        bzrdir = BzrDir.open_from_transport(self._backing_transport.clone(path))
 
368
        target_transport = self.transport_from_client_path(path)
 
369
        format = network_format_registry.get(bzrdir_network_name)
 
370
        use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
 
371
        create_prefix = self.parse_NoneTrueFalse(create_prefix)
 
372
        force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
 
373
        stacked_on = self.parse_NoneString(stacked_on)
 
374
        stack_on_pwd = self.parse_NoneString(stack_on_pwd)
 
375
        make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
 
376
        shared_repo = self.parse_NoneTrueFalse(shared_repo)
 
377
        if stack_on_pwd == '.':
 
378
            stack_on_pwd = target_transport.base
 
379
        repo_format_name = self.parse_NoneString(repo_format_name)
 
380
        repo, bzrdir, stacking, repository_policy = \
 
381
            format.initialize_on_transport_ex(target_transport,
 
382
            use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
383
            force_new_repo=force_new_repo, stacked_on=stacked_on,
 
384
            stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
385
            make_working_trees=make_working_trees, shared_repo=shared_repo)
 
386
        if repo is None:
 
387
            repo_path = ''
 
388
            repo_name = ''
 
389
            rich_root = tree_ref = external_lookup = ''
 
390
            repo_bzrdir_name = ''
 
391
            final_stack = None
 
392
            final_stack_pwd = None
 
393
            repo_lock_token = ''
 
394
        else:
 
395
            repo_path = self._repo_relpath(bzrdir.root_transport, repo)
 
396
            if repo_path == '':
 
397
                repo_path = '.'
 
398
            rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
399
                repo._format)
 
400
            repo_name = repo._format.network_name()
 
401
            repo_bzrdir_name = repo.bzrdir._format.network_name()
 
402
            final_stack = repository_policy._stack_on
 
403
            final_stack_pwd = repository_policy._stack_on_pwd
 
404
            # It is returned locked, but we need to do the lock to get the lock
 
405
            # token.
 
406
            repo.unlock()
 
407
            repo_lock_token = repo.lock_write() or ''
 
408
            if repo_lock_token:
 
409
                repo.leave_lock_in_place()
 
410
            repo.unlock()
 
411
        final_stack = final_stack or ''
 
412
        final_stack_pwd = final_stack_pwd or ''
 
413
 
 
414
        # We want this to be relative to the bzrdir.
 
415
        if final_stack_pwd:
 
416
            final_stack_pwd = urlutils.relative_url(
 
417
                target_transport.base, final_stack_pwd)
 
418
 
 
419
        # Can't meaningfully return a root path.
 
420
        if final_stack.startswith('/'):
 
421
            client_path = self._root_client_path + final_stack[1:]
 
422
            final_stack = urlutils.relative_url(
 
423
                self._root_client_path, client_path)
 
424
            final_stack_pwd = '.'
 
425
 
 
426
        return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
 
427
            external_lookup, repo_name, repo_bzrdir_name,
 
428
            bzrdir._format.network_name(),
 
429
            self._serialize_NoneTrueFalse(stacking), final_stack,
 
430
            final_stack_pwd, repo_lock_token))
 
431
 
 
432
 
 
433
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
 
434
 
 
435
    def do_bzrdir_request(self):
 
436
        """open a branch at path and return the branch reference or branch."""
102
437
        try:
103
 
            reference_url = bzrdir.get_branch_reference()
 
438
            reference_url = self._bzrdir.get_branch_reference()
104
439
            if reference_url is None:
105
440
                return SuccessfulSmartServerResponse(('ok', ''))
106
441
            else:
107
442
                return SuccessfulSmartServerResponse(('ok', reference_url))
108
443
        except errors.NotBranchError:
109
444
            return FailedSmartServerResponse(('nobranch', ))
 
445
 
 
446
 
 
447
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
 
448
 
 
449
    def do_bzrdir_request(self):
 
450
        """open a branch at path and return the reference or format."""
 
451
        try:
 
452
            reference_url = self._bzrdir.get_branch_reference()
 
453
            if reference_url is None:
 
454
                br = self._bzrdir.open_branch(ignore_fallbacks=True)
 
455
                format = br._format.network_name()
 
456
                return SuccessfulSmartServerResponse(('branch', format))
 
457
            else:
 
458
                return SuccessfulSmartServerResponse(('ref', reference_url))
 
459
        except errors.NotBranchError:
 
460
            return FailedSmartServerResponse(('nobranch', ))