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 ...sixish import PY3
39
from .request import (
40
FailedSmartServerResponse,
42
SuccessfulSmartServerResponse,
46
class SmartServerRequestOpenBzrDir(SmartServerRequest):
50
t = self.transport_from_client_path(path)
51
except errors.PathNotChild:
52
# The client is trying to ask about a path that they have no access
54
# Ideally we'd return a FailedSmartServerResponse here rather than
55
# a "successful" negative, but we want to be compatibile with
56
# clients that don't anticipate errors from this method.
59
bzr_prober = BzrProber()
61
bzr_prober.probe_transport(t)
62
except (errors.NotBranchError, errors.UnknownFormatError):
66
return SuccessfulSmartServerResponse((answer,))
69
class SmartServerRequestOpenBzrDir_2_1(SmartServerRequest):
72
"""Is there a BzrDir present, and if so does it have a working tree?
77
t = self.transport_from_client_path(path)
78
except errors.PathNotChild:
79
# The client is trying to ask about a path that they have no access
81
return SuccessfulSmartServerResponse((b'no',))
83
bd = BzrDir.open_from_transport(t)
84
except errors.NotBranchError:
88
if bd.has_workingtree():
92
return SuccessfulSmartServerResponse(answer)
95
class SmartServerRequestBzrDir(SmartServerRequest):
97
def do(self, path, *args):
98
"""Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`."""
100
self._bzrdir = BzrDir.open_from_transport(
101
self.transport_from_client_path(path))
102
except errors.NotBranchError as e:
103
return FailedSmartServerResponse((b'nobranch',))
104
return self.do_bzrdir_request(*args)
106
def _boolean_to_yes_no(self, a_boolean):
112
def _format_to_capabilities(self, repo_format):
113
rich_root = self._boolean_to_yes_no(repo_format.rich_root_data)
114
tree_ref = self._boolean_to_yes_no(
115
repo_format.supports_tree_reference)
116
external_lookup = self._boolean_to_yes_no(
117
repo_format.supports_external_lookups)
118
return rich_root, tree_ref, external_lookup
120
def _repo_relpath(self, current_transport, repository):
121
"""Get the relative path for repository from current_transport."""
122
# the relpath of the bzrdir in the found repository gives us the
123
# path segments to pop-out.
124
relpath = repository.user_transport.relpath(
125
current_transport.base)
127
segments = ['..'] * len(relpath.split('/'))
130
return '/'.join(segments)
133
class SmartServerBzrDirRequestDestroyBranch(SmartServerRequestBzrDir):
135
def do_bzrdir_request(self, name=None):
136
"""Destroy the branch with the specified name.
139
:return: On success, 'ok'.
142
self._bzrdir.destroy_branch(
143
name.decode('utf-8') if name is not None else None)
144
except errors.NotBranchError as e:
145
return FailedSmartServerResponse((b'nobranch',))
146
return SuccessfulSmartServerResponse((b'ok',))
149
class SmartServerBzrDirRequestHasWorkingTree(SmartServerRequestBzrDir):
151
def do_bzrdir_request(self, name=None):
152
"""Check whether there is a working tree present.
156
:return: If there is a working tree present, 'yes'.
159
if self._bzrdir.has_workingtree():
160
return SuccessfulSmartServerResponse((b'yes', ))
162
return SuccessfulSmartServerResponse((b'no', ))
165
class SmartServerBzrDirRequestDestroyRepository(SmartServerRequestBzrDir):
167
def do_bzrdir_request(self, name=None):
168
"""Destroy the repository.
172
:return: On success, 'ok'.
175
self._bzrdir.destroy_repository()
176
except errors.NoRepositoryPresent as e:
177
return FailedSmartServerResponse((b'norepository',))
178
return SuccessfulSmartServerResponse((b'ok',))
181
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
183
def do_bzrdir_request(self, require_stacking):
184
"""Get the format that should be used when cloning from this dir.
188
:return: on success, a 3-tuple of network names for (control,
189
repository, branch) directories, where '' signifies "not present".
190
If this BzrDir contains a branch reference then this will fail with
191
BranchReference; clients should resolve branch references before
195
branch_ref = self._bzrdir.get_branch_reference()
196
except errors.NotBranchError:
198
if branch_ref is not None:
199
# The server shouldn't try to resolve references, and it quite
200
# possibly can't reach them anyway. The client needs to resolve
201
# the branch reference to determine the cloning_metadir.
202
return FailedSmartServerResponse((b'BranchReference',))
203
if require_stacking == b"True":
204
require_stacking = True
206
require_stacking = False
207
control_format = self._bzrdir.cloning_metadir(
208
require_stacking=require_stacking)
209
control_name = control_format.network_name()
210
if not control_format.fixed_components:
211
branch_name = (b'branch',
212
control_format.get_branch_format().network_name())
213
repository_name = control_format.repository_format.network_name()
215
# Only MetaDir has delegated formats today.
216
branch_name = (b'branch', b'')
217
repository_name = b''
218
return SuccessfulSmartServerResponse((control_name, repository_name,
222
class SmartServerBzrDirRequestCheckoutMetaDir(SmartServerRequestBzrDir):
223
"""Get the format to use for checkouts.
227
:return: on success, a 3-tuple of network names for (control,
228
repository, branch) directories, where '' signifies "not present".
229
If this BzrDir contains a branch reference then this will fail with
230
BranchReference; clients should resolve branch references before
231
calling this RPC (they should not try to create a checkout of a
235
def do_bzrdir_request(self):
237
branch_ref = self._bzrdir.get_branch_reference()
238
except errors.NotBranchError:
240
if branch_ref is not None:
241
# The server shouldn't try to resolve references, and it quite
242
# possibly can't reach them anyway. The client needs to resolve
243
# the branch reference to determine the cloning_metadir.
244
return FailedSmartServerResponse((b'BranchReference',))
245
control_format = self._bzrdir.checkout_metadir()
246
control_name = control_format.network_name()
247
if not control_format.fixed_components:
248
branch_name = control_format.get_branch_format().network_name()
249
repo_name = control_format.repository_format.network_name()
253
return SuccessfulSmartServerResponse(
254
(control_name, repo_name, branch_name))
257
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
259
def do(self, path, network_name):
260
"""Create a branch in the bzr dir at path.
262
This operates precisely like 'bzrdir.create_branch'.
264
If a bzrdir is not present, an exception is propogated
265
rather than 'no branch' because these are different conditions (and
266
this method should only be called after establishing that a bzr dir
269
This is the initial version of this method introduced to the smart
272
:param path: The path to the bzrdir.
273
:param network_name: The network name of the branch type to create.
274
:return: ('ok', branch_format, repo_path, rich_root, tree_ref,
275
external_lookup, repo_format)
277
bzrdir = BzrDir.open_from_transport(
278
self.transport_from_client_path(path))
279
format = branch.network_format_registry.get(network_name)
280
bzrdir.branch_format = format
281
result = format.initialize(bzrdir, name="")
282
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
283
result.repository._format)
284
branch_format = result._format.network_name()
285
repo_format = result.repository._format.network_name()
286
repo_path = self._repo_relpath(bzrdir.root_transport,
288
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
290
return SuccessfulSmartServerResponse((b'ok', branch_format, repo_path,
291
rich_root, tree_ref, external_lookup, repo_format))
294
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
296
def do(self, path, network_name, shared):
297
"""Create a repository in the bzr dir at path.
299
This operates precisely like 'bzrdir.create_repository'.
301
If a bzrdir is not present, an exception is propagated
302
rather than 'no branch' because these are different conditions (and
303
this method should only be called after establishing that a bzr dir
306
This is the initial version of this method introduced to the smart
309
:param path: The path to the bzrdir.
310
:param network_name: The network name of the repository type to create.
311
:param shared: The value to pass create_repository for the shared
313
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
315
bzrdir = BzrDir.open_from_transport(
316
self.transport_from_client_path(path))
317
shared = shared == b'True'
318
format = repository.network_format_registry.get(network_name)
319
bzrdir.repository_format = format
320
result = format.initialize(bzrdir, shared=shared)
321
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
323
return SuccessfulSmartServerResponse((b'ok', rich_root, tree_ref,
324
external_lookup, result._format.network_name()))
327
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
329
def _find(self, path):
330
"""try to find a repository from path upwards
332
This operates precisely like 'bzrdir.find_repository'.
334
:return: (relpath, rich_root, tree_ref, external_lookup, network_name).
335
All are strings, relpath is a / prefixed path, the next three are
336
either 'yes' or 'no', and the last is a repository format network
338
:raises errors.NoRepositoryPresent: When there is no repository
341
bzrdir = BzrDir.open_from_transport(
342
self.transport_from_client_path(path))
343
repository = bzrdir.find_repository()
344
path = self._repo_relpath(bzrdir.root_transport, repository)
345
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
347
network_name = repository._format.network_name()
348
return path, rich_root, tree_ref, external_lookup, network_name
351
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
354
"""try to find a repository from path upwards
356
This operates precisely like 'bzrdir.find_repository'.
358
If a bzrdir is not present, an exception is propagated
359
rather than 'no branch' because these are different conditions.
361
This is the initial version of this method introduced with the smart
362
server. Modern clients will try the V2 method that adds support for the
363
supports_external_lookups attribute.
365
:return: norepository or ok, relpath.
368
path, rich_root, tree_ref, external_lookup, name = self._find(path)
369
return SuccessfulSmartServerResponse((b'ok', path.encode('utf-8'), rich_root, tree_ref))
370
except errors.NoRepositoryPresent:
371
return FailedSmartServerResponse((b'norepository', ))
374
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
377
"""try to find a repository from path upwards
379
This operates precisely like 'bzrdir.find_repository'.
381
If a bzrdir is not present, an exception is propagated
382
rather than 'no branch' because these are different conditions.
384
This is the second edition of this method introduced in bzr 1.3, which
385
returns information about the supports_external_lookups format
388
:return: norepository or ok, relpath, rich_root, tree_ref,
392
path, rich_root, tree_ref, external_lookup, name = self._find(path)
393
return SuccessfulSmartServerResponse(
394
(b'ok', path.encode('utf-8'), rich_root, tree_ref, external_lookup))
395
except errors.NoRepositoryPresent:
396
return FailedSmartServerResponse((b'norepository', ))
399
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
402
"""try to find a repository from path upwards
404
This operates precisely like 'bzrdir.find_repository'.
406
If a bzrdir is not present, an exception is propogated
407
rather than 'no branch' because these are different conditions.
409
This is the third edition of this method introduced in bzr 1.13, which
410
returns information about the network name of the repository format.
412
:return: norepository or ok, relpath, rich_root, tree_ref,
413
external_lookup, network_name.
416
path, rich_root, tree_ref, external_lookup, name = self._find(path)
417
return SuccessfulSmartServerResponse(
418
(b'ok', path.encode('utf-8'), rich_root, tree_ref, external_lookup, name))
419
except errors.NoRepositoryPresent:
420
return FailedSmartServerResponse((b'norepository', ))
423
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
425
def do_bzrdir_request(self):
426
"""Get the configuration bytes for a config file in bzrdir.
428
The body is not utf8 decoded - it is the literal bytestream from disk.
430
config = self._bzrdir._get_config()
434
content = config._get_config_file().read()
435
return SuccessfulSmartServerResponse((), content)
438
class SmartServerBzrDirRequestGetBranches(SmartServerRequestBzrDir):
440
def do_bzrdir_request(self):
441
"""Get the branches in a control directory.
443
The body is a bencoded dictionary, with values similar to the return
444
value of the open branch request.
446
branches = self._bzrdir.get_branches()
448
for name, b in branches.items():
451
ret[name.encode('utf-8')] = (b"branch", b._format.network_name())
452
return SuccessfulSmartServerResponse(
453
(b"success", ), bencode.bencode(ret))
456
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
459
"""Initialize a bzrdir at path.
461
The default format of the server is used.
462
:return: SmartServerResponse(('ok', ))
464
target_transport = self.transport_from_client_path(path)
465
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
466
return SuccessfulSmartServerResponse((b'ok', ))
469
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
471
def parse_NoneTrueFalse(self, arg):
478
raise AssertionError("invalid arg %r" % arg)
480
def parse_NoneBytestring(self, arg):
483
def parse_NoneString(self, arg):
487
return arg.decode('utf-8')
491
def _serialize_NoneTrueFalse(self, arg):
498
def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
499
force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
500
make_working_trees, shared_repo):
501
"""Initialize a bzrdir at path as per
502
BzrDirFormat.initialize_on_transport_ex.
504
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
506
:return: return SuccessfulSmartServerResponse((repo_path, rich_root,
507
tree_ref, external_lookup, repo_network_name,
508
repo_bzrdir_network_name, bzrdir_format_network_name,
509
NoneTrueFalse(stacking), final_stack, final_stack_pwd,
512
target_transport = self.transport_from_client_path(path)
513
format = network_format_registry.get(bzrdir_network_name)
514
use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
515
create_prefix = self.parse_NoneTrueFalse(create_prefix)
516
force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
517
stacked_on = self.parse_NoneString(stacked_on)
518
stack_on_pwd = self.parse_NoneString(stack_on_pwd)
519
make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
520
shared_repo = self.parse_NoneTrueFalse(shared_repo)
521
if stack_on_pwd == b'.':
522
stack_on_pwd = target_transport.base.encode('utf-8')
523
repo_format_name = self.parse_NoneBytestring(repo_format_name)
524
repo, bzrdir, stacking, repository_policy = \
525
format.initialize_on_transport_ex(target_transport,
526
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
527
force_new_repo=force_new_repo, stacked_on=stacked_on,
528
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
529
make_working_trees=make_working_trees, shared_repo=shared_repo)
533
rich_root = tree_ref = external_lookup = b''
534
repo_bzrdir_name = b''
536
final_stack_pwd = None
537
repo_lock_token = b''
539
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
542
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
544
repo_name = repo._format.network_name()
545
repo_bzrdir_name = repo.controldir._format.network_name()
546
final_stack = repository_policy._stack_on
547
final_stack_pwd = repository_policy._stack_on_pwd
548
# It is returned locked, but we need to do the lock to get the lock
551
repo_lock_token = repo.lock_write().repository_token or b''
553
repo.leave_lock_in_place()
555
final_stack = final_stack or ''
556
final_stack_pwd = final_stack_pwd or ''
558
# We want this to be relative to the bzrdir.
560
final_stack_pwd = urlutils.relative_url(
561
target_transport.base, final_stack_pwd)
563
# Can't meaningfully return a root path.
564
if final_stack.startswith('/'):
565
client_path = self._root_client_path + final_stack[1:]
566
final_stack = urlutils.relative_url(
567
self._root_client_path, client_path)
568
final_stack_pwd = '.'
570
return SuccessfulSmartServerResponse((repo_path.encode('utf-8'),
571
rich_root, tree_ref, external_lookup, repo_name, repo_bzrdir_name,
572
bzrdir._format.network_name(),
573
self._serialize_NoneTrueFalse(
574
stacking), final_stack.encode('utf-8'),
575
final_stack_pwd.encode('utf-8'), repo_lock_token))
578
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
580
def do_bzrdir_request(self):
581
"""open a branch at path and return the branch reference or branch."""
583
reference_url = self._bzrdir.get_branch_reference()
584
if reference_url is None:
586
return SuccessfulSmartServerResponse((b'ok', reference_url.encode('utf-8')))
587
except errors.NotBranchError as e:
588
return FailedSmartServerResponse((b'nobranch',))
591
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
593
def do_bzrdir_request(self):
594
"""open a branch at path and return the reference or format."""
596
reference_url = self._bzrdir.get_branch_reference()
597
if reference_url is None:
598
br = self._bzrdir.open_branch(ignore_fallbacks=True)
599
format = br._format.network_name()
600
return SuccessfulSmartServerResponse((b'branch', format))
602
return SuccessfulSmartServerResponse((b'ref', reference_url.encode('utf-8')))
603
except errors.NotBranchError as e:
604
return FailedSmartServerResponse((b'nobranch',))
607
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
609
def do_bzrdir_request(self):
610
"""Open a branch at path and return the reference or format.
612
This version introduced in 2.1.
614
Differences to SmartServerRequestOpenBranchV2:
615
* can return 2-element ('nobranch', extra), where 'extra' is a string
616
with an explanation like 'location is a repository'. Previously
617
a 'nobranch' response would never have more than one element.
620
reference_url = self._bzrdir.get_branch_reference()
621
if reference_url is None:
622
br = self._bzrdir.open_branch(ignore_fallbacks=True)
623
format = br._format.network_name()
624
return SuccessfulSmartServerResponse((b'branch', format))
626
return SuccessfulSmartServerResponse((b'ref', reference_url.encode('utf-8')))
627
except errors.NotBranchError as e:
628
# Stringify the exception so that its .detail attribute will be
631
resp = (b'nobranch',)
634
if detail.startswith(': '):
636
resp += (detail.encode('utf-8'),)
637
return FailedSmartServerResponse(resp)