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."""
19
from __future__ import absolute_import
21
from bzrlib import branch, errors, repository, urlutils
22
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 SmartServerBzrDirRequestDestroyBranch(SmartServerRequestBzrDir):
126
def do_bzrdir_request(self, name=None):
127
"""Destroy the branch with the specified name.
130
:return: On success, 'ok'.
133
self._bzrdir.destroy_branch(name)
134
except errors.NotBranchError, e:
135
return FailedSmartServerResponse(('nobranch',))
136
return SuccessfulSmartServerResponse(('ok',))
139
class SmartServerBzrDirRequestHasWorkingTree(SmartServerRequestBzrDir):
141
def do_bzrdir_request(self, name=None):
142
"""Check whether there is a working tree present.
146
:return: If there is a working tree present, 'yes'.
149
if self._bzrdir.has_workingtree():
150
return SuccessfulSmartServerResponse(('yes', ))
152
return SuccessfulSmartServerResponse(('no', ))
155
class SmartServerBzrDirRequestDestroyRepository(SmartServerRequestBzrDir):
157
def do_bzrdir_request(self, name=None):
158
"""Destroy the repository.
162
:return: On success, 'ok'.
165
self._bzrdir.destroy_repository()
166
except errors.NoRepositoryPresent, e:
167
return FailedSmartServerResponse(('norepository',))
168
return SuccessfulSmartServerResponse(('ok',))
171
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
173
def do_bzrdir_request(self, require_stacking):
174
"""Get the format that should be used when cloning from this dir.
178
:return: on success, a 3-tuple of network names for (control,
179
repository, branch) directories, where '' signifies "not present".
180
If this BzrDir contains a branch reference then this will fail with
181
BranchReference; clients should resolve branch references before
185
branch_ref = self._bzrdir.get_branch_reference()
186
except errors.NotBranchError:
188
if branch_ref is not None:
189
# The server shouldn't try to resolve references, and it quite
190
# possibly can't reach them anyway. The client needs to resolve
191
# the branch reference to determine the cloning_metadir.
192
return FailedSmartServerResponse(('BranchReference',))
193
if require_stacking == "True":
194
require_stacking = True
196
require_stacking = False
197
control_format = self._bzrdir.cloning_metadir(
198
require_stacking=require_stacking)
199
control_name = control_format.network_name()
200
if not control_format.fixed_components:
201
branch_name = ('branch',
202
control_format.get_branch_format().network_name())
203
repository_name = control_format.repository_format.network_name()
205
# Only MetaDir has delegated formats today.
206
branch_name = ('branch', '')
208
return SuccessfulSmartServerResponse((control_name, repository_name,
212
class SmartServerBzrDirRequestCheckoutMetaDir(SmartServerRequestBzrDir):
213
"""Get the format to use for checkouts.
217
:return: on success, a 3-tuple of network names for (control,
218
repository, branch) directories, where '' signifies "not present".
219
If this BzrDir contains a branch reference then this will fail with
220
BranchReference; clients should resolve branch references before
221
calling this RPC (they should not try to create a checkout of a
225
def do_bzrdir_request(self):
227
branch_ref = self._bzrdir.get_branch_reference()
228
except errors.NotBranchError:
230
if branch_ref is not None:
231
# The server shouldn't try to resolve references, and it quite
232
# possibly can't reach them anyway. The client needs to resolve
233
# the branch reference to determine the cloning_metadir.
234
return FailedSmartServerResponse(('BranchReference',))
235
control_format = self._bzrdir.checkout_metadir()
236
control_name = control_format.network_name()
237
if not control_format.fixed_components:
238
branch_name = control_format.get_branch_format().network_name()
239
repo_name = control_format.repository_format.network_name()
243
return SuccessfulSmartServerResponse(
244
(control_name, repo_name, branch_name))
247
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
249
def do(self, path, network_name):
250
"""Create a branch in the bzr dir at path.
252
This operates precisely like 'bzrdir.create_branch'.
254
If a bzrdir is not present, an exception is propogated
255
rather than 'no branch' because these are different conditions (and
256
this method should only be called after establishing that a bzr dir
259
This is the initial version of this method introduced to the smart
262
:param path: The path to the bzrdir.
263
:param network_name: The network name of the branch type to create.
264
:return: ('ok', branch_format, repo_path, rich_root, tree_ref,
265
external_lookup, repo_format)
267
bzrdir = BzrDir.open_from_transport(
268
self.transport_from_client_path(path))
269
format = branch.network_format_registry.get(network_name)
270
bzrdir.branch_format = format
271
result = format.initialize(bzrdir)
272
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
273
result.repository._format)
274
branch_format = result._format.network_name()
275
repo_format = result.repository._format.network_name()
276
repo_path = self._repo_relpath(bzrdir.root_transport,
278
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
280
return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
281
rich_root, tree_ref, external_lookup, repo_format))
284
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
286
def do(self, path, network_name, shared):
287
"""Create a repository in the bzr dir at path.
289
This operates precisely like 'bzrdir.create_repository'.
291
If a bzrdir is not present, an exception is propagated
292
rather than 'no branch' because these are different conditions (and
293
this method should only be called after establishing that a bzr dir
296
This is the initial version of this method introduced to the smart
299
:param path: The path to the bzrdir.
300
:param network_name: The network name of the repository type to create.
301
:param shared: The value to pass create_repository for the shared
303
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
305
bzrdir = BzrDir.open_from_transport(
306
self.transport_from_client_path(path))
307
shared = shared == 'True'
308
format = repository.network_format_registry.get(network_name)
309
bzrdir.repository_format = format
310
result = format.initialize(bzrdir, shared=shared)
311
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
313
return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
314
external_lookup, result._format.network_name()))
317
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
319
def _find(self, path):
320
"""try to find a repository from path upwards
322
This operates precisely like 'bzrdir.find_repository'.
324
:return: (relpath, rich_root, tree_ref, external_lookup, network_name).
325
All are strings, relpath is a / prefixed path, the next three are
326
either 'yes' or 'no', and the last is a repository format network
328
:raises errors.NoRepositoryPresent: When there is no repository
331
bzrdir = BzrDir.open_from_transport(
332
self.transport_from_client_path(path))
333
repository = bzrdir.find_repository()
334
path = self._repo_relpath(bzrdir.root_transport, repository)
335
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
337
network_name = repository._format.network_name()
338
return path, rich_root, tree_ref, external_lookup, network_name
341
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
344
"""try to find a repository from path upwards
346
This operates precisely like 'bzrdir.find_repository'.
348
If a bzrdir is not present, an exception is propagated
349
rather than 'no branch' because these are different conditions.
351
This is the initial version of this method introduced with the smart
352
server. Modern clients will try the V2 method that adds support for the
353
supports_external_lookups attribute.
355
:return: norepository or ok, relpath.
358
path, rich_root, tree_ref, external_lookup, name = self._find(path)
359
return SuccessfulSmartServerResponse(('ok', path, rich_root, tree_ref))
360
except errors.NoRepositoryPresent:
361
return FailedSmartServerResponse(('norepository', ))
364
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
367
"""try to find a repository from path upwards
369
This operates precisely like 'bzrdir.find_repository'.
371
If a bzrdir is not present, an exception is propagated
372
rather than 'no branch' because these are different conditions.
374
This is the second edition of this method introduced in bzr 1.3, which
375
returns information about the supports_external_lookups format
378
:return: norepository or ok, relpath, rich_root, tree_ref,
382
path, rich_root, tree_ref, external_lookup, name = self._find(path)
383
return SuccessfulSmartServerResponse(
384
('ok', path, rich_root, tree_ref, external_lookup))
385
except errors.NoRepositoryPresent:
386
return FailedSmartServerResponse(('norepository', ))
389
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
392
"""try to find a repository from path upwards
394
This operates precisely like 'bzrdir.find_repository'.
396
If a bzrdir is not present, an exception is propogated
397
rather than 'no branch' because these are different conditions.
399
This is the third edition of this method introduced in bzr 1.13, which
400
returns information about the network name of the repository format.
402
:return: norepository or ok, relpath, rich_root, tree_ref,
403
external_lookup, network_name.
406
path, rich_root, tree_ref, external_lookup, name = self._find(path)
407
return SuccessfulSmartServerResponse(
408
('ok', path, rich_root, tree_ref, external_lookup, name))
409
except errors.NoRepositoryPresent:
410
return FailedSmartServerResponse(('norepository', ))
413
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
415
def do_bzrdir_request(self):
416
"""Get the configuration bytes for a config file in bzrdir.
418
The body is not utf8 decoded - it is the literal bytestream from disk.
420
config = self._bzrdir._get_config()
424
content = config._get_config_file().read()
425
return SuccessfulSmartServerResponse((), content)
428
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
431
"""Initialize a bzrdir at path.
433
The default format of the server is used.
434
:return: SmartServerResponse(('ok', ))
436
target_transport = self.transport_from_client_path(path)
437
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
438
return SuccessfulSmartServerResponse(('ok', ))
441
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
443
def parse_NoneTrueFalse(self, arg):
450
raise AssertionError("invalid arg %r" % arg)
452
def parse_NoneString(self, arg):
455
def _serialize_NoneTrueFalse(self, arg):
462
def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
463
force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
464
make_working_trees, shared_repo):
465
"""Initialize a bzrdir at path as per
466
BzrDirFormat.initialize_on_transport_ex.
468
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
470
:return: return SuccessfulSmartServerResponse((repo_path, rich_root,
471
tree_ref, external_lookup, repo_network_name,
472
repo_bzrdir_network_name, bzrdir_format_network_name,
473
NoneTrueFalse(stacking), final_stack, final_stack_pwd,
476
target_transport = self.transport_from_client_path(path)
477
format = network_format_registry.get(bzrdir_network_name)
478
use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
479
create_prefix = self.parse_NoneTrueFalse(create_prefix)
480
force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
481
stacked_on = self.parse_NoneString(stacked_on)
482
stack_on_pwd = self.parse_NoneString(stack_on_pwd)
483
make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
484
shared_repo = self.parse_NoneTrueFalse(shared_repo)
485
if stack_on_pwd == '.':
486
stack_on_pwd = target_transport.base
487
repo_format_name = self.parse_NoneString(repo_format_name)
488
repo, bzrdir, stacking, repository_policy = \
489
format.initialize_on_transport_ex(target_transport,
490
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
491
force_new_repo=force_new_repo, stacked_on=stacked_on,
492
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
493
make_working_trees=make_working_trees, shared_repo=shared_repo)
497
rich_root = tree_ref = external_lookup = ''
498
repo_bzrdir_name = ''
500
final_stack_pwd = None
503
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
506
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
508
repo_name = repo._format.network_name()
509
repo_bzrdir_name = repo.bzrdir._format.network_name()
510
final_stack = repository_policy._stack_on
511
final_stack_pwd = repository_policy._stack_on_pwd
512
# It is returned locked, but we need to do the lock to get the lock
515
repo_lock_token = repo.lock_write().repository_token or ''
517
repo.leave_lock_in_place()
519
final_stack = final_stack or ''
520
final_stack_pwd = final_stack_pwd or ''
522
# We want this to be relative to the bzrdir.
524
final_stack_pwd = urlutils.relative_url(
525
target_transport.base, final_stack_pwd)
527
# Can't meaningfully return a root path.
528
if final_stack.startswith('/'):
529
client_path = self._root_client_path + final_stack[1:]
530
final_stack = urlutils.relative_url(
531
self._root_client_path, client_path)
532
final_stack_pwd = '.'
534
return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
535
external_lookup, repo_name, repo_bzrdir_name,
536
bzrdir._format.network_name(),
537
self._serialize_NoneTrueFalse(stacking), final_stack,
538
final_stack_pwd, repo_lock_token))
541
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
543
def do_bzrdir_request(self):
544
"""open a branch at path and return the branch reference or branch."""
546
reference_url = self._bzrdir.get_branch_reference()
547
if reference_url is None:
548
return SuccessfulSmartServerResponse(('ok', ''))
550
return SuccessfulSmartServerResponse(('ok', reference_url))
551
except errors.NotBranchError, e:
552
return FailedSmartServerResponse(('nobranch',))
555
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
557
def do_bzrdir_request(self):
558
"""open a branch at path and return the reference or format."""
560
reference_url = self._bzrdir.get_branch_reference()
561
if reference_url is None:
562
br = self._bzrdir.open_branch(ignore_fallbacks=True)
563
format = br._format.network_name()
564
return SuccessfulSmartServerResponse(('branch', format))
566
return SuccessfulSmartServerResponse(('ref', reference_url))
567
except errors.NotBranchError, e:
568
return FailedSmartServerResponse(('nobranch',))
571
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
573
def do_bzrdir_request(self):
574
"""Open a branch at path and return the reference or format.
576
This version introduced in 2.1.
578
Differences to SmartServerRequestOpenBranchV2:
579
* can return 2-element ('nobranch', extra), where 'extra' is a string
580
with an explanation like 'location is a repository'. Previously
581
a 'nobranch' response would never have more than one element.
584
reference_url = self._bzrdir.get_branch_reference()
585
if reference_url is None:
586
br = self._bzrdir.open_branch(ignore_fallbacks=True)
587
format = br._format.network_name()
588
return SuccessfulSmartServerResponse(('branch', format))
590
return SuccessfulSmartServerResponse(('ref', reference_url))
591
except errors.NotBranchError, e:
592
# Stringify the exception so that its .detail attribute will be
598
if detail.startswith(': '):
601
return FailedSmartServerResponse(resp)