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
31
from ..bzrdir import (
35
from ...controldir import (
36
network_format_registry,
38
from .request import (
39
FailedSmartServerResponse,
41
SuccessfulSmartServerResponse,
45
class SmartServerRequestOpenBzrDir(SmartServerRequest):
49
t = self.transport_from_client_path(path)
50
except errors.PathNotChild:
51
# The client is trying to ask about a path that they have no access
53
# Ideally we'd return a FailedSmartServerResponse here rather than
54
# a "successful" negative, but we want to be compatibile with
55
# clients that don't anticipate errors from this method.
58
bzr_prober = BzrProber()
60
bzr_prober.probe_transport(t)
61
except (errors.NotBranchError, errors.UnknownFormatError):
65
return SuccessfulSmartServerResponse((answer,))
68
class SmartServerRequestOpenBzrDir_2_1(SmartServerRequest):
71
"""Is there a BzrDir present, and if so does it have a working tree?
76
t = self.transport_from_client_path(path)
77
except errors.PathNotChild:
78
# The client is trying to ask about a path that they have no access
80
return SuccessfulSmartServerResponse(('no',))
82
bd = BzrDir.open_from_transport(t)
83
except errors.NotBranchError:
87
if bd.has_workingtree():
91
return SuccessfulSmartServerResponse(answer)
94
class SmartServerRequestBzrDir(SmartServerRequest):
96
def do(self, path, *args):
97
"""Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`."""
99
self._bzrdir = BzrDir.open_from_transport(
100
self.transport_from_client_path(path))
101
except errors.NotBranchError as e:
102
return FailedSmartServerResponse(('nobranch',))
103
return self.do_bzrdir_request(*args)
105
def _boolean_to_yes_no(self, a_boolean):
111
def _format_to_capabilities(self, repo_format):
112
rich_root = self._boolean_to_yes_no(repo_format.rich_root_data)
113
tree_ref = self._boolean_to_yes_no(
114
repo_format.supports_tree_reference)
115
external_lookup = self._boolean_to_yes_no(
116
repo_format.supports_external_lookups)
117
return rich_root, tree_ref, external_lookup
119
def _repo_relpath(self, current_transport, repository):
120
"""Get the relative path for repository from current_transport."""
121
# the relpath of the bzrdir in the found repository gives us the
122
# path segments to pop-out.
123
relpath = repository.user_transport.relpath(
124
current_transport.base)
126
segments = ['..'] * len(relpath.split('/'))
129
return '/'.join(segments)
132
class SmartServerBzrDirRequestDestroyBranch(SmartServerRequestBzrDir):
134
def do_bzrdir_request(self, name=None):
135
"""Destroy the branch with the specified name.
138
:return: On success, 'ok'.
141
self._bzrdir.destroy_branch(name)
142
except errors.NotBranchError as e:
143
return FailedSmartServerResponse(('nobranch',))
144
return SuccessfulSmartServerResponse(('ok',))
147
class SmartServerBzrDirRequestHasWorkingTree(SmartServerRequestBzrDir):
149
def do_bzrdir_request(self, name=None):
150
"""Check whether there is a working tree present.
154
:return: If there is a working tree present, 'yes'.
157
if self._bzrdir.has_workingtree():
158
return SuccessfulSmartServerResponse(('yes', ))
160
return SuccessfulSmartServerResponse(('no', ))
163
class SmartServerBzrDirRequestDestroyRepository(SmartServerRequestBzrDir):
165
def do_bzrdir_request(self, name=None):
166
"""Destroy the repository.
170
:return: On success, 'ok'.
173
self._bzrdir.destroy_repository()
174
except errors.NoRepositoryPresent as e:
175
return FailedSmartServerResponse(('norepository',))
176
return SuccessfulSmartServerResponse(('ok',))
179
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
181
def do_bzrdir_request(self, require_stacking):
182
"""Get the format that should be used when cloning from this dir.
186
:return: on success, a 3-tuple of network names for (control,
187
repository, branch) directories, where '' signifies "not present".
188
If this BzrDir contains a branch reference then this will fail with
189
BranchReference; clients should resolve branch references before
193
branch_ref = self._bzrdir.get_branch_reference()
194
except errors.NotBranchError:
196
if branch_ref is not None:
197
# The server shouldn't try to resolve references, and it quite
198
# possibly can't reach them anyway. The client needs to resolve
199
# the branch reference to determine the cloning_metadir.
200
return FailedSmartServerResponse(('BranchReference',))
201
if require_stacking == "True":
202
require_stacking = True
204
require_stacking = False
205
control_format = self._bzrdir.cloning_metadir(
206
require_stacking=require_stacking)
207
control_name = control_format.network_name()
208
if not control_format.fixed_components:
209
branch_name = ('branch',
210
control_format.get_branch_format().network_name())
211
repository_name = control_format.repository_format.network_name()
213
# Only MetaDir has delegated formats today.
214
branch_name = ('branch', '')
216
return SuccessfulSmartServerResponse((control_name, repository_name,
220
class SmartServerBzrDirRequestCheckoutMetaDir(SmartServerRequestBzrDir):
221
"""Get the format to use for checkouts.
225
:return: on success, a 3-tuple of network names for (control,
226
repository, branch) directories, where '' signifies "not present".
227
If this BzrDir contains a branch reference then this will fail with
228
BranchReference; clients should resolve branch references before
229
calling this RPC (they should not try to create a checkout of a
233
def do_bzrdir_request(self):
235
branch_ref = self._bzrdir.get_branch_reference()
236
except errors.NotBranchError:
238
if branch_ref is not None:
239
# The server shouldn't try to resolve references, and it quite
240
# possibly can't reach them anyway. The client needs to resolve
241
# the branch reference to determine the cloning_metadir.
242
return FailedSmartServerResponse(('BranchReference',))
243
control_format = self._bzrdir.checkout_metadir()
244
control_name = control_format.network_name()
245
if not control_format.fixed_components:
246
branch_name = control_format.get_branch_format().network_name()
247
repo_name = control_format.repository_format.network_name()
251
return SuccessfulSmartServerResponse(
252
(control_name, repo_name, branch_name))
255
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
257
def do(self, path, network_name):
258
"""Create a branch in the bzr dir at path.
260
This operates precisely like 'bzrdir.create_branch'.
262
If a bzrdir is not present, an exception is propogated
263
rather than 'no branch' because these are different conditions (and
264
this method should only be called after establishing that a bzr dir
267
This is the initial version of this method introduced to the smart
270
:param path: The path to the bzrdir.
271
:param network_name: The network name of the branch type to create.
272
:return: ('ok', branch_format, repo_path, rich_root, tree_ref,
273
external_lookup, repo_format)
275
bzrdir = BzrDir.open_from_transport(
276
self.transport_from_client_path(path))
277
format = branch.network_format_registry.get(network_name)
278
bzrdir.branch_format = format
279
result = format.initialize(bzrdir, name="")
280
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
281
result.repository._format)
282
branch_format = result._format.network_name()
283
repo_format = result.repository._format.network_name()
284
repo_path = self._repo_relpath(bzrdir.root_transport,
286
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
288
return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
289
rich_root, tree_ref, external_lookup, repo_format))
292
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
294
def do(self, path, network_name, shared):
295
"""Create a repository in the bzr dir at path.
297
This operates precisely like 'bzrdir.create_repository'.
299
If a bzrdir is not present, an exception is propagated
300
rather than 'no branch' because these are different conditions (and
301
this method should only be called after establishing that a bzr dir
304
This is the initial version of this method introduced to the smart
307
:param path: The path to the bzrdir.
308
:param network_name: The network name of the repository type to create.
309
:param shared: The value to pass create_repository for the shared
311
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
313
bzrdir = BzrDir.open_from_transport(
314
self.transport_from_client_path(path))
315
shared = shared == 'True'
316
format = repository.network_format_registry.get(network_name)
317
bzrdir.repository_format = format
318
result = format.initialize(bzrdir, shared=shared)
319
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
321
return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
322
external_lookup, result._format.network_name()))
325
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
327
def _find(self, path):
328
"""try to find a repository from path upwards
330
This operates precisely like 'bzrdir.find_repository'.
332
:return: (relpath, rich_root, tree_ref, external_lookup, network_name).
333
All are strings, relpath is a / prefixed path, the next three are
334
either 'yes' or 'no', and the last is a repository format network
336
:raises errors.NoRepositoryPresent: When there is no repository
339
bzrdir = BzrDir.open_from_transport(
340
self.transport_from_client_path(path))
341
repository = bzrdir.find_repository()
342
path = self._repo_relpath(bzrdir.root_transport, repository)
343
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
345
network_name = repository._format.network_name()
346
return path, rich_root, tree_ref, external_lookup, network_name
349
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
352
"""try to find a repository from path upwards
354
This operates precisely like 'bzrdir.find_repository'.
356
If a bzrdir is not present, an exception is propagated
357
rather than 'no branch' because these are different conditions.
359
This is the initial version of this method introduced with the smart
360
server. Modern clients will try the V2 method that adds support for the
361
supports_external_lookups attribute.
363
:return: norepository or ok, relpath.
366
path, rich_root, tree_ref, external_lookup, name = self._find(path)
367
return SuccessfulSmartServerResponse(('ok', path, rich_root, tree_ref))
368
except errors.NoRepositoryPresent:
369
return FailedSmartServerResponse(('norepository', ))
372
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
375
"""try to find a repository from path upwards
377
This operates precisely like 'bzrdir.find_repository'.
379
If a bzrdir is not present, an exception is propagated
380
rather than 'no branch' because these are different conditions.
382
This is the second edition of this method introduced in bzr 1.3, which
383
returns information about the supports_external_lookups format
386
:return: norepository or ok, relpath, rich_root, tree_ref,
390
path, rich_root, tree_ref, external_lookup, name = self._find(path)
391
return SuccessfulSmartServerResponse(
392
('ok', path, rich_root, tree_ref, external_lookup))
393
except errors.NoRepositoryPresent:
394
return FailedSmartServerResponse(('norepository', ))
397
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
400
"""try to find a repository from path upwards
402
This operates precisely like 'bzrdir.find_repository'.
404
If a bzrdir is not present, an exception is propogated
405
rather than 'no branch' because these are different conditions.
407
This is the third edition of this method introduced in bzr 1.13, which
408
returns information about the network name of the repository format.
410
:return: norepository or ok, relpath, rich_root, tree_ref,
411
external_lookup, network_name.
414
path, rich_root, tree_ref, external_lookup, name = self._find(path)
415
return SuccessfulSmartServerResponse(
416
('ok', path, rich_root, tree_ref, external_lookup, name))
417
except errors.NoRepositoryPresent:
418
return FailedSmartServerResponse(('norepository', ))
421
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
423
def do_bzrdir_request(self):
424
"""Get the configuration bytes for a config file in bzrdir.
426
The body is not utf8 decoded - it is the literal bytestream from disk.
428
config = self._bzrdir._get_config()
432
content = config._get_config_file().read()
433
return SuccessfulSmartServerResponse((), content)
436
class SmartServerBzrDirRequestGetBranches(SmartServerRequestBzrDir):
438
def do_bzrdir_request(self):
439
"""Get the branches in a control directory.
441
The body is a bencoded dictionary, with values similar to the return
442
value of the open branch request.
444
branches = self._bzrdir.get_branches()
446
for name, b in branches.items():
449
ret[name] = ("branch", b._format.network_name())
450
return SuccessfulSmartServerResponse(
451
("success", ), bencode.bencode(ret))
454
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
457
"""Initialize a bzrdir at path.
459
The default format of the server is used.
460
:return: SmartServerResponse(('ok', ))
462
target_transport = self.transport_from_client_path(path)
463
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
464
return SuccessfulSmartServerResponse(('ok', ))
467
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
469
def parse_NoneTrueFalse(self, arg):
476
raise AssertionError("invalid arg %r" % arg)
478
def parse_NoneString(self, arg):
481
def _serialize_NoneTrueFalse(self, arg):
488
def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
489
force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
490
make_working_trees, shared_repo):
491
"""Initialize a bzrdir at path as per
492
BzrDirFormat.initialize_on_transport_ex.
494
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
496
:return: return SuccessfulSmartServerResponse((repo_path, rich_root,
497
tree_ref, external_lookup, repo_network_name,
498
repo_bzrdir_network_name, bzrdir_format_network_name,
499
NoneTrueFalse(stacking), final_stack, final_stack_pwd,
502
target_transport = self.transport_from_client_path(path)
503
format = network_format_registry.get(bzrdir_network_name)
504
use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
505
create_prefix = self.parse_NoneTrueFalse(create_prefix)
506
force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
507
stacked_on = self.parse_NoneString(stacked_on)
508
stack_on_pwd = self.parse_NoneString(stack_on_pwd)
509
make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
510
shared_repo = self.parse_NoneTrueFalse(shared_repo)
511
if stack_on_pwd == '.':
512
stack_on_pwd = target_transport.base
513
repo_format_name = self.parse_NoneString(repo_format_name)
514
repo, bzrdir, stacking, repository_policy = \
515
format.initialize_on_transport_ex(target_transport,
516
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
517
force_new_repo=force_new_repo, stacked_on=stacked_on,
518
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
519
make_working_trees=make_working_trees, shared_repo=shared_repo)
523
rich_root = tree_ref = external_lookup = ''
524
repo_bzrdir_name = ''
526
final_stack_pwd = None
529
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
532
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
534
repo_name = repo._format.network_name()
535
repo_bzrdir_name = repo.controldir._format.network_name()
536
final_stack = repository_policy._stack_on
537
final_stack_pwd = repository_policy._stack_on_pwd
538
# It is returned locked, but we need to do the lock to get the lock
541
repo_lock_token = repo.lock_write().repository_token or ''
543
repo.leave_lock_in_place()
545
final_stack = final_stack or ''
546
final_stack_pwd = final_stack_pwd or ''
548
# We want this to be relative to the bzrdir.
550
final_stack_pwd = urlutils.relative_url(
551
target_transport.base, final_stack_pwd)
553
# Can't meaningfully return a root path.
554
if final_stack.startswith('/'):
555
client_path = self._root_client_path + final_stack[1:]
556
final_stack = urlutils.relative_url(
557
self._root_client_path, client_path)
558
final_stack_pwd = '.'
560
return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
561
external_lookup, repo_name, repo_bzrdir_name,
562
bzrdir._format.network_name(),
563
self._serialize_NoneTrueFalse(stacking), final_stack,
564
final_stack_pwd, repo_lock_token))
567
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
569
def do_bzrdir_request(self):
570
"""open a branch at path and return the branch reference or branch."""
572
reference_url = self._bzrdir.get_branch_reference()
573
if reference_url is None:
574
return SuccessfulSmartServerResponse(('ok', ''))
576
return SuccessfulSmartServerResponse(('ok', reference_url))
577
except errors.NotBranchError as e:
578
return FailedSmartServerResponse(('nobranch',))
581
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
583
def do_bzrdir_request(self):
584
"""open a branch at path and return the reference or format."""
586
reference_url = self._bzrdir.get_branch_reference()
587
if reference_url is None:
588
br = self._bzrdir.open_branch(ignore_fallbacks=True)
589
format = br._format.network_name()
590
return SuccessfulSmartServerResponse(('branch', format))
592
return SuccessfulSmartServerResponse(('ref', reference_url))
593
except errors.NotBranchError as e:
594
return FailedSmartServerResponse(('nobranch',))
597
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
599
def do_bzrdir_request(self):
600
"""Open a branch at path and return the reference or format.
602
This version introduced in 2.1.
604
Differences to SmartServerRequestOpenBranchV2:
605
* can return 2-element ('nobranch', extra), where 'extra' is a string
606
with an explanation like 'location is a repository'. Previously
607
a 'nobranch' response would never have more than one element.
610
reference_url = self._bzrdir.get_branch_reference()
611
if reference_url is None:
612
br = self._bzrdir.open_branch(ignore_fallbacks=True)
613
format = br._format.network_name()
614
return SuccessfulSmartServerResponse(('branch', format))
616
return SuccessfulSmartServerResponse(('ref', reference_url))
617
except errors.NotBranchError as e:
618
# Stringify the exception so that its .detail attribute will be
624
if detail.startswith(': '):
627
return FailedSmartServerResponse(resp)