1
# Copyright (C) 2006-2010 Canonical Ltd
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.
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.
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
17
"""Server-side bzrdir related request implmentations."""
20
from bzrlib import branch, errors, repository, urlutils
21
from bzrlib.bzrdir import (
27
from bzrlib.controldir import (
28
network_format_registry,
30
from bzrlib.smart.request import (
31
FailedSmartServerResponse,
33
SuccessfulSmartServerResponse,
37
class SmartServerRequestOpenBzrDir(SmartServerRequest):
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
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.
50
bzr_prober = BzrProber()
52
bzr_prober.probe_transport(t)
53
except (errors.NotBranchError, errors.UnknownFormatError):
57
return SuccessfulSmartServerResponse((answer,))
60
class SmartServerRequestOpenBzrDir_2_1(SmartServerRequest):
63
"""Is there a BzrDir present, and if so does it have a working tree?
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
72
return SuccessfulSmartServerResponse(('no',))
74
bd = BzrDir.open_from_transport(t)
75
except errors.NotBranchError:
79
if bd.has_workingtree():
83
return SuccessfulSmartServerResponse(answer)
86
class SmartServerRequestBzrDir(SmartServerRequest):
88
def do(self, path, *args):
89
"""Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`."""
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)
97
def _boolean_to_yes_no(self, a_boolean):
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
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)
118
segments = ['..'] * len(relpath.split('/'))
121
return '/'.join(segments)
124
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
126
def do_bzrdir_request(self, require_stacking):
127
"""Get the format that should be used when cloning from this dir.
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
138
branch_ref = self._bzrdir.get_branch_reference()
139
except errors.NotBranchError:
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
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()
158
# Only MetaDir has delegated formats today.
159
branch_name = ('branch', '')
161
return SuccessfulSmartServerResponse((control_name, repository_name,
165
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
167
def do(self, path, network_name):
168
"""Create a branch in the bzr dir at path.
170
This operates precisely like 'bzrdir.create_branch'.
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
177
This is the initial version of this method introduced to the smart
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)
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,
196
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
198
return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
199
rich_root, tree_ref, external_lookup, repo_format))
202
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
204
def do(self, path, network_name, shared):
205
"""Create a repository in the bzr dir at path.
207
This operates precisely like 'bzrdir.create_repository'.
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
214
This is the initial version of this method introduced to the smart
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
221
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
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(
231
return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
232
external_lookup, result._format.network_name()))
235
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
237
def _find(self, path):
238
"""try to find a repository from path upwards
240
This operates precisely like 'bzrdir.find_repository'.
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
246
:raises errors.NoRepositoryPresent: When there is no repository
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(
255
network_name = repository._format.network_name()
256
return path, rich_root, tree_ref, external_lookup, network_name
259
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
262
"""try to find a repository from path upwards
264
This operates precisely like 'bzrdir.find_repository'.
266
If a bzrdir is not present, an exception is propagated
267
rather than 'no branch' because these are different conditions.
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.
273
:return: norepository or ok, relpath.
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', ))
282
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
285
"""try to find a repository from path upwards
287
This operates precisely like 'bzrdir.find_repository'.
289
If a bzrdir is not present, an exception is propagated
290
rather than 'no branch' because these are different conditions.
292
This is the second edition of this method introduced in bzr 1.3, which
293
returns information about the supports_external_lookups format
296
:return: norepository or ok, relpath, rich_root, tree_ref,
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', ))
307
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
310
"""try to find a repository from path upwards
312
This operates precisely like 'bzrdir.find_repository'.
314
If a bzrdir is not present, an exception is propogated
315
rather than 'no branch' because these are different conditions.
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.
320
:return: norepository or ok, relpath, rich_root, tree_ref,
321
external_lookup, network_name.
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', ))
331
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
333
def do_bzrdir_request(self):
334
"""Get the configuration bytes for a config file in bzrdir.
336
The body is not utf8 decoded - it is the literal bytestream from disk.
338
config = self._bzrdir._get_config()
342
content = config._get_config_file().read()
343
return SuccessfulSmartServerResponse((), content)
346
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
349
"""Initialize a bzrdir at path.
351
The default format of the server is used.
352
:return: SmartServerResponse(('ok', ))
354
target_transport = self.transport_from_client_path(path)
355
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
356
return SuccessfulSmartServerResponse(('ok', ))
359
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
361
def parse_NoneTrueFalse(self, arg):
368
raise AssertionError("invalid arg %r" % arg)
370
def parse_NoneString(self, arg):
373
def _serialize_NoneTrueFalse(self, arg):
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.
386
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
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,
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)
415
rich_root = tree_ref = external_lookup = ''
416
repo_bzrdir_name = ''
418
final_stack_pwd = None
421
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
424
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
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
433
repo_lock_token = repo.lock_write().repository_token or ''
435
repo.leave_lock_in_place()
437
final_stack = final_stack or ''
438
final_stack_pwd = final_stack_pwd or ''
440
# We want this to be relative to the bzrdir.
442
final_stack_pwd = urlutils.relative_url(
443
target_transport.base, final_stack_pwd)
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 = '.'
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))
459
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
461
def do_bzrdir_request(self):
462
"""open a branch at path and return the branch reference or branch."""
464
reference_url = self._bzrdir.get_branch_reference()
465
if reference_url is None:
466
return SuccessfulSmartServerResponse(('ok', ''))
468
return SuccessfulSmartServerResponse(('ok', reference_url))
469
except errors.NotBranchError, e:
470
return FailedSmartServerResponse(('nobranch',))
473
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
475
def do_bzrdir_request(self):
476
"""open a branch at path and return the reference or format."""
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))
484
return SuccessfulSmartServerResponse(('ref', reference_url))
485
except errors.NotBranchError, e:
486
return FailedSmartServerResponse(('nobranch',))
489
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
491
def do_bzrdir_request(self):
492
"""Open a branch at path and return the reference or format.
494
This version introduced in 2.1.
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.
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))
508
return SuccessfulSmartServerResponse(('ref', reference_url))
509
except errors.NotBranchError, e:
510
# Stringify the exception so that its .detail attribute will be
516
if detail.startswith(': '):
519
return FailedSmartServerResponse(resp)