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 (
26
from bzrlib.controldir import (
27
network_format_registry,
29
from bzrlib.smart.request import (
30
FailedSmartServerResponse,
32
SuccessfulSmartServerResponse,
36
class SmartServerRequestOpenBzrDir(SmartServerRequest):
40
t = self.transport_from_client_path(path)
41
except errors.PathNotChild:
42
# The client is trying to ask about a path that they have no access
44
# Ideally we'd return a FailedSmartServerResponse here rather than
45
# a "successful" negative, but we want to be compatibile with
46
# clients that don't anticipate errors from this method.
49
bzr_prober = BzrProber()
51
bzr_prober.probe_transport(t)
52
except (errors.NotBranchError, errors.UnknownFormatError):
56
return SuccessfulSmartServerResponse((answer,))
59
class SmartServerRequestOpenBzrDir_2_1(SmartServerRequest):
62
"""Is there a BzrDir present, and if so does it have a working tree?
67
t = self.transport_from_client_path(path)
68
except errors.PathNotChild:
69
# The client is trying to ask about a path that they have no access
71
return SuccessfulSmartServerResponse(('no',))
73
bd = BzrDir.open_from_transport(t)
74
except errors.NotBranchError:
78
if bd.has_workingtree():
82
return SuccessfulSmartServerResponse(answer)
85
class SmartServerRequestBzrDir(SmartServerRequest):
87
def do(self, path, *args):
88
"""Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`."""
90
self._bzrdir = BzrDir.open_from_transport(
91
self.transport_from_client_path(path))
92
except errors.NotBranchError, e:
93
return FailedSmartServerResponse(('nobranch',))
94
return self.do_bzrdir_request(*args)
96
def _boolean_to_yes_no(self, a_boolean):
102
def _format_to_capabilities(self, repo_format):
103
rich_root = self._boolean_to_yes_no(repo_format.rich_root_data)
104
tree_ref = self._boolean_to_yes_no(
105
repo_format.supports_tree_reference)
106
external_lookup = self._boolean_to_yes_no(
107
repo_format.supports_external_lookups)
108
return rich_root, tree_ref, external_lookup
110
def _repo_relpath(self, current_transport, repository):
111
"""Get the relative path for repository from current_transport."""
112
# the relpath of the bzrdir in the found repository gives us the
113
# path segments to pop-out.
114
relpath = repository.user_transport.relpath(
115
current_transport.base)
117
segments = ['..'] * len(relpath.split('/'))
120
return '/'.join(segments)
123
class SmartServerBzrDirRequestDestroyBranch(SmartServerRequestBzrDir):
125
def do_bzrdir_request(self, name=None):
126
"""Destroy the branch with the specified name.
129
:return: On success, 'ok'.
132
self._bzrdir.destroy_branch(name)
133
except errors.NotBranchError, e:
134
return FailedSmartServerResponse(('nobranch',))
135
return SuccessfulSmartServerResponse(('ok',))
138
class SmartServerBzrDirRequestHasWorkingTree(SmartServerRequestBzrDir):
140
def do_bzrdir_request(self, name=None):
141
"""Check whether there is a working tree present.
145
:return: If there is a working tree present, 'yes'.
148
if self._bzrdir.has_workingtree():
149
return SuccessfulSmartServerResponse(('yes', ))
151
return SuccessfulSmartServerResponse(('no', ))
154
class SmartServerBzrDirRequestDestroyRepository(SmartServerRequestBzrDir):
156
def do_bzrdir_request(self, name=None):
157
"""Destroy the repository.
161
:return: On success, 'ok'.
164
self._bzrdir.destroy_repository()
165
except errors.NoRepositoryPresent, e:
166
return FailedSmartServerResponse(('norepository',))
167
return SuccessfulSmartServerResponse(('ok',))
170
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
172
def do_bzrdir_request(self, require_stacking):
173
"""Get the format that should be used when cloning from this dir.
177
:return: on success, a 3-tuple of network names for (control,
178
repository, branch) directories, where '' signifies "not present".
179
If this BzrDir contains a branch reference then this will fail with
180
BranchReference; clients should resolve branch references before
184
branch_ref = self._bzrdir.get_branch_reference()
185
except errors.NotBranchError:
187
if branch_ref is not None:
188
# The server shouldn't try to resolve references, and it quite
189
# possibly can't reach them anyway. The client needs to resolve
190
# the branch reference to determine the cloning_metadir.
191
return FailedSmartServerResponse(('BranchReference',))
192
if require_stacking == "True":
193
require_stacking = True
195
require_stacking = False
196
control_format = self._bzrdir.cloning_metadir(
197
require_stacking=require_stacking)
198
control_name = control_format.network_name()
199
if not control_format.fixed_components:
200
branch_name = ('branch',
201
control_format.get_branch_format().network_name())
202
repository_name = control_format.repository_format.network_name()
204
# Only MetaDir has delegated formats today.
205
branch_name = ('branch', '')
207
return SuccessfulSmartServerResponse((control_name, repository_name,
211
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
213
def do(self, path, network_name):
214
"""Create a branch in the bzr dir at path.
216
This operates precisely like 'bzrdir.create_branch'.
218
If a bzrdir is not present, an exception is propogated
219
rather than 'no branch' because these are different conditions (and
220
this method should only be called after establishing that a bzr dir
223
This is the initial version of this method introduced to the smart
226
:param path: The path to the bzrdir.
227
:param network_name: The network name of the branch type to create.
228
:return: ('ok', branch_format, repo_path, rich_root, tree_ref,
229
external_lookup, repo_format)
231
bzrdir = BzrDir.open_from_transport(
232
self.transport_from_client_path(path))
233
format = branch.network_format_registry.get(network_name)
234
bzrdir.branch_format = format
235
result = format.initialize(bzrdir)
236
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
237
result.repository._format)
238
branch_format = result._format.network_name()
239
repo_format = result.repository._format.network_name()
240
repo_path = self._repo_relpath(bzrdir.root_transport,
242
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
244
return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
245
rich_root, tree_ref, external_lookup, repo_format))
248
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
250
def do(self, path, network_name, shared):
251
"""Create a repository in the bzr dir at path.
253
This operates precisely like 'bzrdir.create_repository'.
255
If a bzrdir is not present, an exception is propagated
256
rather than 'no branch' because these are different conditions (and
257
this method should only be called after establishing that a bzr dir
260
This is the initial version of this method introduced to the smart
263
:param path: The path to the bzrdir.
264
:param network_name: The network name of the repository type to create.
265
:param shared: The value to pass create_repository for the shared
267
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
269
bzrdir = BzrDir.open_from_transport(
270
self.transport_from_client_path(path))
271
shared = shared == 'True'
272
format = repository.network_format_registry.get(network_name)
273
bzrdir.repository_format = format
274
result = format.initialize(bzrdir, shared=shared)
275
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
277
return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
278
external_lookup, result._format.network_name()))
281
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
283
def _find(self, path):
284
"""try to find a repository from path upwards
286
This operates precisely like 'bzrdir.find_repository'.
288
:return: (relpath, rich_root, tree_ref, external_lookup, network_name).
289
All are strings, relpath is a / prefixed path, the next three are
290
either 'yes' or 'no', and the last is a repository format network
292
:raises errors.NoRepositoryPresent: When there is no repository
295
bzrdir = BzrDir.open_from_transport(
296
self.transport_from_client_path(path))
297
repository = bzrdir.find_repository()
298
path = self._repo_relpath(bzrdir.root_transport, repository)
299
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
301
network_name = repository._format.network_name()
302
return path, rich_root, tree_ref, external_lookup, network_name
305
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
308
"""try to find a repository from path upwards
310
This operates precisely like 'bzrdir.find_repository'.
312
If a bzrdir is not present, an exception is propagated
313
rather than 'no branch' because these are different conditions.
315
This is the initial version of this method introduced with the smart
316
server. Modern clients will try the V2 method that adds support for the
317
supports_external_lookups attribute.
319
:return: norepository or ok, relpath.
322
path, rich_root, tree_ref, external_lookup, name = self._find(path)
323
return SuccessfulSmartServerResponse(('ok', path, rich_root, tree_ref))
324
except errors.NoRepositoryPresent:
325
return FailedSmartServerResponse(('norepository', ))
328
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
331
"""try to find a repository from path upwards
333
This operates precisely like 'bzrdir.find_repository'.
335
If a bzrdir is not present, an exception is propagated
336
rather than 'no branch' because these are different conditions.
338
This is the second edition of this method introduced in bzr 1.3, which
339
returns information about the supports_external_lookups format
342
:return: norepository or ok, relpath, rich_root, tree_ref,
346
path, rich_root, tree_ref, external_lookup, name = self._find(path)
347
return SuccessfulSmartServerResponse(
348
('ok', path, rich_root, tree_ref, external_lookup))
349
except errors.NoRepositoryPresent:
350
return FailedSmartServerResponse(('norepository', ))
353
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
356
"""try to find a repository from path upwards
358
This operates precisely like 'bzrdir.find_repository'.
360
If a bzrdir is not present, an exception is propogated
361
rather than 'no branch' because these are different conditions.
363
This is the third edition of this method introduced in bzr 1.13, which
364
returns information about the network name of the repository format.
366
:return: norepository or ok, relpath, rich_root, tree_ref,
367
external_lookup, network_name.
370
path, rich_root, tree_ref, external_lookup, name = self._find(path)
371
return SuccessfulSmartServerResponse(
372
('ok', path, rich_root, tree_ref, external_lookup, name))
373
except errors.NoRepositoryPresent:
374
return FailedSmartServerResponse(('norepository', ))
377
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
379
def do_bzrdir_request(self):
380
"""Get the configuration bytes for a config file in bzrdir.
382
The body is not utf8 decoded - it is the literal bytestream from disk.
384
config = self._bzrdir._get_config()
388
content = config._get_config_file().read()
389
return SuccessfulSmartServerResponse((), content)
392
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
395
"""Initialize a bzrdir at path.
397
The default format of the server is used.
398
:return: SmartServerResponse(('ok', ))
400
target_transport = self.transport_from_client_path(path)
401
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
402
return SuccessfulSmartServerResponse(('ok', ))
405
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
407
def parse_NoneTrueFalse(self, arg):
414
raise AssertionError("invalid arg %r" % arg)
416
def parse_NoneString(self, arg):
419
def _serialize_NoneTrueFalse(self, arg):
426
def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
427
force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
428
make_working_trees, shared_repo):
429
"""Initialize a bzrdir at path as per
430
BzrDirFormat.initialize_on_transport_ex.
432
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
434
:return: return SuccessfulSmartServerResponse((repo_path, rich_root,
435
tree_ref, external_lookup, repo_network_name,
436
repo_bzrdir_network_name, bzrdir_format_network_name,
437
NoneTrueFalse(stacking), final_stack, final_stack_pwd,
440
target_transport = self.transport_from_client_path(path)
441
format = network_format_registry.get(bzrdir_network_name)
442
use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
443
create_prefix = self.parse_NoneTrueFalse(create_prefix)
444
force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
445
stacked_on = self.parse_NoneString(stacked_on)
446
stack_on_pwd = self.parse_NoneString(stack_on_pwd)
447
make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
448
shared_repo = self.parse_NoneTrueFalse(shared_repo)
449
if stack_on_pwd == '.':
450
stack_on_pwd = target_transport.base
451
repo_format_name = self.parse_NoneString(repo_format_name)
452
repo, bzrdir, stacking, repository_policy = \
453
format.initialize_on_transport_ex(target_transport,
454
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
455
force_new_repo=force_new_repo, stacked_on=stacked_on,
456
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
457
make_working_trees=make_working_trees, shared_repo=shared_repo)
461
rich_root = tree_ref = external_lookup = ''
462
repo_bzrdir_name = ''
464
final_stack_pwd = None
467
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
470
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
472
repo_name = repo._format.network_name()
473
repo_bzrdir_name = repo.bzrdir._format.network_name()
474
final_stack = repository_policy._stack_on
475
final_stack_pwd = repository_policy._stack_on_pwd
476
# It is returned locked, but we need to do the lock to get the lock
479
repo_lock_token = repo.lock_write().repository_token or ''
481
repo.leave_lock_in_place()
483
final_stack = final_stack or ''
484
final_stack_pwd = final_stack_pwd or ''
486
# We want this to be relative to the bzrdir.
488
final_stack_pwd = urlutils.relative_url(
489
target_transport.base, final_stack_pwd)
491
# Can't meaningfully return a root path.
492
if final_stack.startswith('/'):
493
client_path = self._root_client_path + final_stack[1:]
494
final_stack = urlutils.relative_url(
495
self._root_client_path, client_path)
496
final_stack_pwd = '.'
498
return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
499
external_lookup, repo_name, repo_bzrdir_name,
500
bzrdir._format.network_name(),
501
self._serialize_NoneTrueFalse(stacking), final_stack,
502
final_stack_pwd, repo_lock_token))
505
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
507
def do_bzrdir_request(self):
508
"""open a branch at path and return the branch reference or branch."""
510
reference_url = self._bzrdir.get_branch_reference()
511
if reference_url is None:
512
return SuccessfulSmartServerResponse(('ok', ''))
514
return SuccessfulSmartServerResponse(('ok', reference_url))
515
except errors.NotBranchError, e:
516
return FailedSmartServerResponse(('nobranch',))
519
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
521
def do_bzrdir_request(self):
522
"""open a branch at path and return the reference or format."""
524
reference_url = self._bzrdir.get_branch_reference()
525
if reference_url is None:
526
br = self._bzrdir.open_branch(ignore_fallbacks=True)
527
format = br._format.network_name()
528
return SuccessfulSmartServerResponse(('branch', format))
530
return SuccessfulSmartServerResponse(('ref', reference_url))
531
except errors.NotBranchError, e:
532
return FailedSmartServerResponse(('nobranch',))
535
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
537
def do_bzrdir_request(self):
538
"""Open a branch at path and return the reference or format.
540
This version introduced in 2.1.
542
Differences to SmartServerRequestOpenBranchV2:
543
* can return 2-element ('nobranch', extra), where 'extra' is a string
544
with an explanation like 'location is a repository'. Previously
545
a 'nobranch' response would never have more than one element.
548
reference_url = self._bzrdir.get_branch_reference()
549
if reference_url is None:
550
br = self._bzrdir.open_branch(ignore_fallbacks=True)
551
format = br._format.network_name()
552
return SuccessfulSmartServerResponse(('branch', format))
554
return SuccessfulSmartServerResponse(('ref', reference_url))
555
except errors.NotBranchError, e:
556
# Stringify the exception so that its .detail attribute will be
562
if detail.startswith(': '):
565
return FailedSmartServerResponse(resp)