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
# 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()
160
# Only MetaDir has delegated formats today.
161
branch_name = ('branch', '')
163
return SuccessfulSmartServerResponse((control_name, repository_name,
167
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
169
def do(self, path, network_name):
170
"""Create a branch in the bzr dir at path.
172
This operates precisely like 'bzrdir.create_branch'.
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
179
This is the initial version of this method introduced to the smart
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)
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,
198
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
200
return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
201
rich_root, tree_ref, external_lookup, repo_format))
204
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
206
def do(self, path, network_name, shared):
207
"""Create a repository in the bzr dir at path.
209
This operates precisely like 'bzrdir.create_repository'.
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
216
This is the initial version of this method introduced to the smart
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
223
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
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(
233
return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
234
external_lookup, result._format.network_name()))
237
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
239
def _find(self, path):
240
"""try to find a repository from path upwards
242
This operates precisely like 'bzrdir.find_repository'.
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
248
:raises errors.NoRepositoryPresent: When there is no repository
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(
257
network_name = repository._format.network_name()
258
return path, rich_root, tree_ref, external_lookup, network_name
261
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
264
"""try to find a repository from path upwards
266
This operates precisely like 'bzrdir.find_repository'.
268
If a bzrdir is not present, an exception is propagated
269
rather than 'no branch' because these are different conditions.
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.
275
:return: norepository or ok, relpath.
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', ))
284
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
287
"""try to find a repository from path upwards
289
This operates precisely like 'bzrdir.find_repository'.
291
If a bzrdir is not present, an exception is propagated
292
rather than 'no branch' because these are different conditions.
294
This is the second edition of this method introduced in bzr 1.3, which
295
returns information about the supports_external_lookups format
298
:return: norepository or ok, relpath, rich_root, tree_ref,
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', ))
309
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
312
"""try to find a repository from path upwards
314
This operates precisely like 'bzrdir.find_repository'.
316
If a bzrdir is not present, an exception is propogated
317
rather than 'no branch' because these are different conditions.
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.
322
:return: norepository or ok, relpath, rich_root, tree_ref,
323
external_lookup, network_name.
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', ))
333
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
335
def do_bzrdir_request(self):
336
"""Get the configuration bytes for a config file in bzrdir.
338
The body is not utf8 decoded - it is the literal bytestream from disk.
340
config = self._bzrdir._get_config()
344
content = config._get_config_file().read()
345
return SuccessfulSmartServerResponse((), content)
348
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
351
"""Initialize a bzrdir at path.
353
The default format of the server is used.
354
:return: SmartServerResponse(('ok', ))
356
target_transport = self.transport_from_client_path(path)
357
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
358
return SuccessfulSmartServerResponse(('ok', ))
361
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
363
def parse_NoneTrueFalse(self, arg):
370
raise AssertionError("invalid arg %r" % arg)
372
def parse_NoneString(self, arg):
375
def _serialize_NoneTrueFalse(self, arg):
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.
388
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
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,
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)
417
rich_root = tree_ref = external_lookup = ''
418
repo_bzrdir_name = ''
420
final_stack_pwd = None
423
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
426
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
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
435
repo_lock_token = repo.lock_write().repository_token or ''
437
repo.leave_lock_in_place()
439
final_stack = final_stack or ''
440
final_stack_pwd = final_stack_pwd or ''
442
# We want this to be relative to the bzrdir.
444
final_stack_pwd = urlutils.relative_url(
445
target_transport.base, final_stack_pwd)
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 = '.'
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))
461
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
463
def do_bzrdir_request(self):
464
"""open a branch at path and return the branch reference or branch."""
466
reference_url = self._bzrdir.get_branch_reference()
467
if reference_url is None:
468
return SuccessfulSmartServerResponse(('ok', ''))
470
return SuccessfulSmartServerResponse(('ok', reference_url))
471
except errors.NotBranchError, e:
472
return FailedSmartServerResponse(('nobranch',))
475
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
477
def do_bzrdir_request(self):
478
"""open a branch at path and return the reference or format."""
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))
486
return SuccessfulSmartServerResponse(('ref', reference_url))
487
except errors.NotBranchError, e:
488
return FailedSmartServerResponse(('nobranch',))
491
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
493
def do_bzrdir_request(self):
494
"""Open a branch at path and return the reference or format.
496
This version introduced in 2.1.
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.
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))
510
return SuccessfulSmartServerResponse(('ref', reference_url))
511
except errors.NotBranchError, e:
512
# Stringify the exception so that its .detail attribute will be
518
if detail.startswith(': '):
521
return FailedSmartServerResponse(resp)