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
28
from bzrlib.bzrdir import (
33
from bzrlib.controldir import (
34
network_format_registry,
36
from bzrlib.smart.request import (
37
FailedSmartServerResponse,
39
SuccessfulSmartServerResponse,
43
class SmartServerRequestOpenBzrDir(SmartServerRequest):
47
t = self.transport_from_client_path(path)
48
except errors.PathNotChild:
49
# The client is trying to ask about a path that they have no access
51
# Ideally we'd return a FailedSmartServerResponse here rather than
52
# a "successful" negative, but we want to be compatibile with
53
# clients that don't anticipate errors from this method.
56
bzr_prober = BzrProber()
58
bzr_prober.probe_transport(t)
59
except (errors.NotBranchError, errors.UnknownFormatError):
63
return SuccessfulSmartServerResponse((answer,))
66
class SmartServerRequestOpenBzrDir_2_1(SmartServerRequest):
69
"""Is there a BzrDir present, and if so does it have a working tree?
74
t = self.transport_from_client_path(path)
75
except errors.PathNotChild:
76
# The client is trying to ask about a path that they have no access
78
return SuccessfulSmartServerResponse(('no',))
80
bd = BzrDir.open_from_transport(t)
81
except errors.NotBranchError:
85
if bd.has_workingtree():
89
return SuccessfulSmartServerResponse(answer)
92
class SmartServerRequestBzrDir(SmartServerRequest):
94
def do(self, path, *args):
95
"""Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`."""
97
self._bzrdir = BzrDir.open_from_transport(
98
self.transport_from_client_path(path))
99
except errors.NotBranchError, e:
100
return FailedSmartServerResponse(('nobranch',))
101
return self.do_bzrdir_request(*args)
103
def _boolean_to_yes_no(self, a_boolean):
109
def _format_to_capabilities(self, repo_format):
110
rich_root = self._boolean_to_yes_no(repo_format.rich_root_data)
111
tree_ref = self._boolean_to_yes_no(
112
repo_format.supports_tree_reference)
113
external_lookup = self._boolean_to_yes_no(
114
repo_format.supports_external_lookups)
115
return rich_root, tree_ref, external_lookup
117
def _repo_relpath(self, current_transport, repository):
118
"""Get the relative path for repository from current_transport."""
119
# the relpath of the bzrdir in the found repository gives us the
120
# path segments to pop-out.
121
relpath = repository.user_transport.relpath(
122
current_transport.base)
124
segments = ['..'] * len(relpath.split('/'))
127
return '/'.join(segments)
130
class SmartServerBzrDirRequestDestroyBranch(SmartServerRequestBzrDir):
132
def do_bzrdir_request(self, name=None):
133
"""Destroy the branch with the specified name.
136
:return: On success, 'ok'.
139
self._bzrdir.destroy_branch(name)
140
except errors.NotBranchError, e:
141
return FailedSmartServerResponse(('nobranch',))
142
return SuccessfulSmartServerResponse(('ok',))
145
class SmartServerBzrDirRequestHasWorkingTree(SmartServerRequestBzrDir):
147
def do_bzrdir_request(self, name=None):
148
"""Check whether there is a working tree present.
152
:return: If there is a working tree present, 'yes'.
155
if self._bzrdir.has_workingtree():
156
return SuccessfulSmartServerResponse(('yes', ))
158
return SuccessfulSmartServerResponse(('no', ))
161
class SmartServerBzrDirRequestDestroyRepository(SmartServerRequestBzrDir):
163
def do_bzrdir_request(self, name=None):
164
"""Destroy the repository.
168
:return: On success, 'ok'.
171
self._bzrdir.destroy_repository()
172
except errors.NoRepositoryPresent, e:
173
return FailedSmartServerResponse(('norepository',))
174
return SuccessfulSmartServerResponse(('ok',))
177
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
179
def do_bzrdir_request(self, require_stacking):
180
"""Get the format that should be used when cloning from this dir.
184
:return: on success, a 3-tuple of network names for (control,
185
repository, branch) directories, where '' signifies "not present".
186
If this BzrDir contains a branch reference then this will fail with
187
BranchReference; clients should resolve branch references before
191
branch_ref = self._bzrdir.get_branch_reference()
192
except errors.NotBranchError:
194
if branch_ref is not None:
195
# The server shouldn't try to resolve references, and it quite
196
# possibly can't reach them anyway. The client needs to resolve
197
# the branch reference to determine the cloning_metadir.
198
return FailedSmartServerResponse(('BranchReference',))
199
if require_stacking == "True":
200
require_stacking = True
202
require_stacking = False
203
control_format = self._bzrdir.cloning_metadir(
204
require_stacking=require_stacking)
205
control_name = control_format.network_name()
206
if not control_format.fixed_components:
207
branch_name = ('branch',
208
control_format.get_branch_format().network_name())
209
repository_name = control_format.repository_format.network_name()
211
# Only MetaDir has delegated formats today.
212
branch_name = ('branch', '')
214
return SuccessfulSmartServerResponse((control_name, repository_name,
218
class SmartServerBzrDirRequestCheckoutMetaDir(SmartServerRequestBzrDir):
219
"""Get the format to use for checkouts.
223
:return: on success, a 3-tuple of network names for (control,
224
repository, branch) directories, where '' signifies "not present".
225
If this BzrDir contains a branch reference then this will fail with
226
BranchReference; clients should resolve branch references before
227
calling this RPC (they should not try to create a checkout of a
231
def do_bzrdir_request(self):
233
branch_ref = self._bzrdir.get_branch_reference()
234
except errors.NotBranchError:
236
if branch_ref is not None:
237
# The server shouldn't try to resolve references, and it quite
238
# possibly can't reach them anyway. The client needs to resolve
239
# the branch reference to determine the cloning_metadir.
240
return FailedSmartServerResponse(('BranchReference',))
241
control_format = self._bzrdir.checkout_metadir()
242
control_name = control_format.network_name()
243
if not control_format.fixed_components:
244
branch_name = control_format.get_branch_format().network_name()
245
repo_name = control_format.repository_format.network_name()
249
return SuccessfulSmartServerResponse(
250
(control_name, repo_name, branch_name))
253
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
255
def do(self, path, network_name):
256
"""Create a branch in the bzr dir at path.
258
This operates precisely like 'bzrdir.create_branch'.
260
If a bzrdir is not present, an exception is propogated
261
rather than 'no branch' because these are different conditions (and
262
this method should only be called after establishing that a bzr dir
265
This is the initial version of this method introduced to the smart
268
:param path: The path to the bzrdir.
269
:param network_name: The network name of the branch type to create.
270
:return: ('ok', branch_format, repo_path, rich_root, tree_ref,
271
external_lookup, repo_format)
273
bzrdir = BzrDir.open_from_transport(
274
self.transport_from_client_path(path))
275
format = branch.network_format_registry.get(network_name)
276
bzrdir.branch_format = format
277
result = format.initialize(bzrdir, name="")
278
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
279
result.repository._format)
280
branch_format = result._format.network_name()
281
repo_format = result.repository._format.network_name()
282
repo_path = self._repo_relpath(bzrdir.root_transport,
284
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
286
return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
287
rich_root, tree_ref, external_lookup, repo_format))
290
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
292
def do(self, path, network_name, shared):
293
"""Create a repository in the bzr dir at path.
295
This operates precisely like 'bzrdir.create_repository'.
297
If a bzrdir is not present, an exception is propagated
298
rather than 'no branch' because these are different conditions (and
299
this method should only be called after establishing that a bzr dir
302
This is the initial version of this method introduced to the smart
305
:param path: The path to the bzrdir.
306
:param network_name: The network name of the repository type to create.
307
:param shared: The value to pass create_repository for the shared
309
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
311
bzrdir = BzrDir.open_from_transport(
312
self.transport_from_client_path(path))
313
shared = shared == 'True'
314
format = repository.network_format_registry.get(network_name)
315
bzrdir.repository_format = format
316
result = format.initialize(bzrdir, shared=shared)
317
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
319
return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
320
external_lookup, result._format.network_name()))
323
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
325
def _find(self, path):
326
"""try to find a repository from path upwards
328
This operates precisely like 'bzrdir.find_repository'.
330
:return: (relpath, rich_root, tree_ref, external_lookup, network_name).
331
All are strings, relpath is a / prefixed path, the next three are
332
either 'yes' or 'no', and the last is a repository format network
334
:raises errors.NoRepositoryPresent: When there is no repository
337
bzrdir = BzrDir.open_from_transport(
338
self.transport_from_client_path(path))
339
repository = bzrdir.find_repository()
340
path = self._repo_relpath(bzrdir.root_transport, repository)
341
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
343
network_name = repository._format.network_name()
344
return path, rich_root, tree_ref, external_lookup, network_name
347
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
350
"""try to find a repository from path upwards
352
This operates precisely like 'bzrdir.find_repository'.
354
If a bzrdir is not present, an exception is propagated
355
rather than 'no branch' because these are different conditions.
357
This is the initial version of this method introduced with the smart
358
server. Modern clients will try the V2 method that adds support for the
359
supports_external_lookups attribute.
361
:return: norepository or ok, relpath.
364
path, rich_root, tree_ref, external_lookup, name = self._find(path)
365
return SuccessfulSmartServerResponse(('ok', path, rich_root, tree_ref))
366
except errors.NoRepositoryPresent:
367
return FailedSmartServerResponse(('norepository', ))
370
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
373
"""try to find a repository from path upwards
375
This operates precisely like 'bzrdir.find_repository'.
377
If a bzrdir is not present, an exception is propagated
378
rather than 'no branch' because these are different conditions.
380
This is the second edition of this method introduced in bzr 1.3, which
381
returns information about the supports_external_lookups format
384
:return: norepository or ok, relpath, rich_root, tree_ref,
388
path, rich_root, tree_ref, external_lookup, name = self._find(path)
389
return SuccessfulSmartServerResponse(
390
('ok', path, rich_root, tree_ref, external_lookup))
391
except errors.NoRepositoryPresent:
392
return FailedSmartServerResponse(('norepository', ))
395
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
398
"""try to find a repository from path upwards
400
This operates precisely like 'bzrdir.find_repository'.
402
If a bzrdir is not present, an exception is propogated
403
rather than 'no branch' because these are different conditions.
405
This is the third edition of this method introduced in bzr 1.13, which
406
returns information about the network name of the repository format.
408
:return: norepository or ok, relpath, rich_root, tree_ref,
409
external_lookup, network_name.
412
path, rich_root, tree_ref, external_lookup, name = self._find(path)
413
return SuccessfulSmartServerResponse(
414
('ok', path, rich_root, tree_ref, external_lookup, name))
415
except errors.NoRepositoryPresent:
416
return FailedSmartServerResponse(('norepository', ))
419
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
421
def do_bzrdir_request(self):
422
"""Get the configuration bytes for a config file in bzrdir.
424
The body is not utf8 decoded - it is the literal bytestream from disk.
426
config = self._bzrdir._get_config()
430
content = config._get_config_file().read()
431
return SuccessfulSmartServerResponse((), content)
434
class SmartServerBzrDirRequestGetBranches(SmartServerRequestBzrDir):
436
def do_bzrdir_request(self):
437
"""Get the branches in a control directory.
439
The body is a bencoded dictionary, with values similar to the return
440
value of the open branch request.
442
branches = self._bzrdir.get_branches()
444
for name, b in branches.iteritems():
447
ret[name] = ("branch", b._format.network_name())
448
return SuccessfulSmartServerResponse(
449
("success", ), bencode.bencode(ret))
452
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
455
"""Initialize a bzrdir at path.
457
The default format of the server is used.
458
:return: SmartServerResponse(('ok', ))
460
target_transport = self.transport_from_client_path(path)
461
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
462
return SuccessfulSmartServerResponse(('ok', ))
465
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
467
def parse_NoneTrueFalse(self, arg):
474
raise AssertionError("invalid arg %r" % arg)
476
def parse_NoneString(self, arg):
479
def _serialize_NoneTrueFalse(self, arg):
486
def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
487
force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
488
make_working_trees, shared_repo):
489
"""Initialize a bzrdir at path as per
490
BzrDirFormat.initialize_on_transport_ex.
492
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
494
:return: return SuccessfulSmartServerResponse((repo_path, rich_root,
495
tree_ref, external_lookup, repo_network_name,
496
repo_bzrdir_network_name, bzrdir_format_network_name,
497
NoneTrueFalse(stacking), final_stack, final_stack_pwd,
500
target_transport = self.transport_from_client_path(path)
501
format = network_format_registry.get(bzrdir_network_name)
502
use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
503
create_prefix = self.parse_NoneTrueFalse(create_prefix)
504
force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
505
stacked_on = self.parse_NoneString(stacked_on)
506
stack_on_pwd = self.parse_NoneString(stack_on_pwd)
507
make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
508
shared_repo = self.parse_NoneTrueFalse(shared_repo)
509
if stack_on_pwd == '.':
510
stack_on_pwd = target_transport.base
511
repo_format_name = self.parse_NoneString(repo_format_name)
512
repo, bzrdir, stacking, repository_policy = \
513
format.initialize_on_transport_ex(target_transport,
514
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
515
force_new_repo=force_new_repo, stacked_on=stacked_on,
516
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
517
make_working_trees=make_working_trees, shared_repo=shared_repo)
521
rich_root = tree_ref = external_lookup = ''
522
repo_bzrdir_name = ''
524
final_stack_pwd = None
527
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
530
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
532
repo_name = repo._format.network_name()
533
repo_bzrdir_name = repo.bzrdir._format.network_name()
534
final_stack = repository_policy._stack_on
535
final_stack_pwd = repository_policy._stack_on_pwd
536
# It is returned locked, but we need to do the lock to get the lock
539
repo_lock_token = repo.lock_write().repository_token or ''
541
repo.leave_lock_in_place()
543
final_stack = final_stack or ''
544
final_stack_pwd = final_stack_pwd or ''
546
# We want this to be relative to the bzrdir.
548
final_stack_pwd = urlutils.relative_url(
549
target_transport.base, final_stack_pwd)
551
# Can't meaningfully return a root path.
552
if final_stack.startswith('/'):
553
client_path = self._root_client_path + final_stack[1:]
554
final_stack = urlutils.relative_url(
555
self._root_client_path, client_path)
556
final_stack_pwd = '.'
558
return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
559
external_lookup, repo_name, repo_bzrdir_name,
560
bzrdir._format.network_name(),
561
self._serialize_NoneTrueFalse(stacking), final_stack,
562
final_stack_pwd, repo_lock_token))
565
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
567
def do_bzrdir_request(self):
568
"""open a branch at path and return the branch reference or branch."""
570
reference_url = self._bzrdir.get_branch_reference()
571
if reference_url is None:
572
return SuccessfulSmartServerResponse(('ok', ''))
574
return SuccessfulSmartServerResponse(('ok', reference_url))
575
except errors.NotBranchError, e:
576
return FailedSmartServerResponse(('nobranch',))
579
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
581
def do_bzrdir_request(self):
582
"""open a branch at path and return the reference or format."""
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
return FailedSmartServerResponse(('nobranch',))
595
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
597
def do_bzrdir_request(self):
598
"""Open a branch at path and return the reference or format.
600
This version introduced in 2.1.
602
Differences to SmartServerRequestOpenBranchV2:
603
* can return 2-element ('nobranch', extra), where 'extra' is a string
604
with an explanation like 'location is a repository'. Previously
605
a 'nobranch' response would never have more than one element.
608
reference_url = self._bzrdir.get_branch_reference()
609
if reference_url is None:
610
br = self._bzrdir.open_branch(ignore_fallbacks=True)
611
format = br._format.network_name()
612
return SuccessfulSmartServerResponse(('branch', format))
614
return SuccessfulSmartServerResponse(('ref', reference_url))
615
except errors.NotBranchError, e:
616
# Stringify the exception so that its .detail attribute will be
622
if detail.startswith(': '):
625
return FailedSmartServerResponse(resp)