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 (
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 SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
126
def do_bzrdir_request(self, require_stacking):
127
"""Get the format that should be used when cloning from this dir.
131
:return: on success, a 3-tuple of network names for (control,
132
repository, branch) directories, where '' signifies "not present".
133
If this BzrDir contains a branch reference then this will fail with
134
BranchReference; clients should resolve branch references before
138
branch_ref = self._bzrdir.get_branch_reference()
139
except errors.NotBranchError:
141
if branch_ref is not None:
142
# The server shouldn't try to resolve references, and it quite
143
# possibly can't reach them anyway. The client needs to resolve
144
# the branch reference to determine the cloning_metadir.
145
return FailedSmartServerResponse(('BranchReference',))
146
if require_stacking == "True":
147
require_stacking = True
149
require_stacking = False
150
control_format = self._bzrdir.cloning_metadir(
151
require_stacking=require_stacking)
152
control_name = control_format.network_name()
153
# XXX: There should be a method that tells us that the format does/does
154
# not have subformats.
155
if isinstance(control_format, BzrDirMetaFormat1):
156
branch_name = ('branch',
157
control_format.get_branch_format().network_name())
158
repository_name = control_format.repository_format.network_name()
160
# Only MetaDir has delegated formats today.
161
branch_name = ('branch', '')
163
return SuccessfulSmartServerResponse((control_name, repository_name,
167
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
169
def do(self, path, network_name):
170
"""Create a branch in the bzr dir at path.
172
This operates precisely like 'bzrdir.create_branch'.
174
If a bzrdir is not present, an exception is propogated
175
rather than 'no branch' because these are different conditions (and
176
this method should only be called after establishing that a bzr dir
179
This is the initial version of this method introduced to the smart
182
:param path: The path to the bzrdir.
183
:param network_name: The network name of the branch type to create.
184
:return: (ok, network_name)
186
bzrdir = BzrDir.open_from_transport(
187
self.transport_from_client_path(path))
188
format = branch.network_format_registry.get(network_name)
189
bzrdir.branch_format = format
190
result = format.initialize(bzrdir)
191
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
192
result.repository._format)
193
branch_format = result._format.network_name()
194
repo_format = result.repository._format.network_name()
195
repo_path = self._repo_relpath(bzrdir.root_transport,
197
# branch format, repo relpath, rich_root, tree_ref, external_lookup,
199
return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
200
rich_root, tree_ref, external_lookup, repo_format))
203
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
205
def do(self, path, network_name, shared):
206
"""Create a repository in the bzr dir at path.
208
This operates precisely like 'bzrdir.create_repository'.
210
If a bzrdir is not present, an exception is propagated
211
rather than 'no branch' because these are different conditions (and
212
this method should only be called after establishing that a bzr dir
215
This is the initial version of this method introduced to the smart
218
:param path: The path to the bzrdir.
219
:param network_name: The network name of the repository type to create.
220
:param shared: The value to pass create_repository for the shared
222
:return: (ok, rich_root, tree_ref, external_lookup, network_name)
224
bzrdir = BzrDir.open_from_transport(
225
self.transport_from_client_path(path))
226
shared = shared == 'True'
227
format = repository.network_format_registry.get(network_name)
228
bzrdir.repository_format = format
229
result = format.initialize(bzrdir, shared=shared)
230
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
232
return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
233
external_lookup, result._format.network_name()))
236
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
238
def _find(self, path):
239
"""try to find a repository from path upwards
241
This operates precisely like 'bzrdir.find_repository'.
243
:return: (relpath, rich_root, tree_ref, external_lookup, network_name).
244
All are strings, relpath is a / prefixed path, the next three are
245
either 'yes' or 'no', and the last is a repository format network
247
:raises errors.NoRepositoryPresent: When there is no repository
250
bzrdir = BzrDir.open_from_transport(
251
self.transport_from_client_path(path))
252
repository = bzrdir.find_repository()
253
path = self._repo_relpath(bzrdir.root_transport, repository)
254
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
256
network_name = repository._format.network_name()
257
return path, rich_root, tree_ref, external_lookup, network_name
260
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
263
"""try to find a repository from path upwards
265
This operates precisely like 'bzrdir.find_repository'.
267
If a bzrdir is not present, an exception is propagated
268
rather than 'no branch' because these are different conditions.
270
This is the initial version of this method introduced with the smart
271
server. Modern clients will try the V2 method that adds support for the
272
supports_external_lookups attribute.
274
:return: norepository or ok, relpath.
277
path, rich_root, tree_ref, external_lookup, name = self._find(path)
278
return SuccessfulSmartServerResponse(('ok', path, rich_root, tree_ref))
279
except errors.NoRepositoryPresent:
280
return FailedSmartServerResponse(('norepository', ))
283
class SmartServerRequestFindRepositoryV2(SmartServerRequestFindRepository):
286
"""try to find a repository from path upwards
288
This operates precisely like 'bzrdir.find_repository'.
290
If a bzrdir is not present, an exception is propagated
291
rather than 'no branch' because these are different conditions.
293
This is the second edition of this method introduced in bzr 1.3, which
294
returns information about the supports_external_lookups format
297
:return: norepository or ok, relpath, rich_root, tree_ref,
301
path, rich_root, tree_ref, external_lookup, name = self._find(path)
302
return SuccessfulSmartServerResponse(
303
('ok', path, rich_root, tree_ref, external_lookup))
304
except errors.NoRepositoryPresent:
305
return FailedSmartServerResponse(('norepository', ))
308
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
311
"""try to find a repository from path upwards
313
This operates precisely like 'bzrdir.find_repository'.
315
If a bzrdir is not present, an exception is propogated
316
rather than 'no branch' because these are different conditions.
318
This is the third edition of this method introduced in bzr 1.13, which
319
returns information about the network name of the repository format.
321
:return: norepository or ok, relpath, rich_root, tree_ref,
322
external_lookup, network_name.
325
path, rich_root, tree_ref, external_lookup, name = self._find(path)
326
return SuccessfulSmartServerResponse(
327
('ok', path, rich_root, tree_ref, external_lookup, name))
328
except errors.NoRepositoryPresent:
329
return FailedSmartServerResponse(('norepository', ))
332
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
334
def do_bzrdir_request(self):
335
"""Get the configuration bytes for a config file in bzrdir.
337
The body is not utf8 decoded - it is the literal bytestream from disk.
339
config = self._bzrdir._get_config()
343
content = config._get_config_file().read()
344
return SuccessfulSmartServerResponse((), content)
347
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
350
"""Initialize a bzrdir at path.
352
The default format of the server is used.
353
:return: SmartServerResponse(('ok', ))
355
target_transport = self.transport_from_client_path(path)
356
BzrDirFormat.get_default_format().initialize_on_transport(target_transport)
357
return SuccessfulSmartServerResponse(('ok', ))
360
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
362
def parse_NoneTrueFalse(self, arg):
369
raise AssertionError("invalid arg %r" % arg)
371
def parse_NoneString(self, arg):
374
def _serialize_NoneTrueFalse(self, arg):
381
def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
382
force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
383
make_working_trees, shared_repo):
384
"""Initialize a bzrdir at path as per
385
BzrDirFormat.initialize_on_transport_ex.
387
New in 1.16. (Replaces BzrDirFormat.initialize_ex verb from 1.15).
389
:return: return SuccessfulSmartServerResponse((repo_path, rich_root,
390
tree_ref, external_lookup, repo_network_name,
391
repo_bzrdir_network_name, bzrdir_format_network_name,
392
NoneTrueFalse(stacking), final_stack, final_stack_pwd,
395
target_transport = self.transport_from_client_path(path)
396
format = network_format_registry.get(bzrdir_network_name)
397
use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
398
create_prefix = self.parse_NoneTrueFalse(create_prefix)
399
force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
400
stacked_on = self.parse_NoneString(stacked_on)
401
stack_on_pwd = self.parse_NoneString(stack_on_pwd)
402
make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
403
shared_repo = self.parse_NoneTrueFalse(shared_repo)
404
if stack_on_pwd == '.':
405
stack_on_pwd = target_transport.base
406
repo_format_name = self.parse_NoneString(repo_format_name)
407
repo, bzrdir, stacking, repository_policy = \
408
format.initialize_on_transport_ex(target_transport,
409
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
410
force_new_repo=force_new_repo, stacked_on=stacked_on,
411
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
412
make_working_trees=make_working_trees, shared_repo=shared_repo)
416
rich_root = tree_ref = external_lookup = ''
417
repo_bzrdir_name = ''
419
final_stack_pwd = None
422
repo_path = self._repo_relpath(bzrdir.root_transport, repo)
425
rich_root, tree_ref, external_lookup = self._format_to_capabilities(
427
repo_name = repo._format.network_name()
428
repo_bzrdir_name = repo.bzrdir._format.network_name()
429
final_stack = repository_policy._stack_on
430
final_stack_pwd = repository_policy._stack_on_pwd
431
# It is returned locked, but we need to do the lock to get the lock
434
repo_lock_token = repo.lock_write().repository_token or ''
436
repo.leave_lock_in_place()
438
final_stack = final_stack or ''
439
final_stack_pwd = final_stack_pwd or ''
441
# We want this to be relative to the bzrdir.
443
final_stack_pwd = urlutils.relative_url(
444
target_transport.base, final_stack_pwd)
446
# Can't meaningfully return a root path.
447
if final_stack.startswith('/'):
448
client_path = self._root_client_path + final_stack[1:]
449
final_stack = urlutils.relative_url(
450
self._root_client_path, client_path)
451
final_stack_pwd = '.'
453
return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
454
external_lookup, repo_name, repo_bzrdir_name,
455
bzrdir._format.network_name(),
456
self._serialize_NoneTrueFalse(stacking), final_stack,
457
final_stack_pwd, repo_lock_token))
460
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
462
def do_bzrdir_request(self):
463
"""open a branch at path and return the branch reference or branch."""
465
reference_url = self._bzrdir.get_branch_reference()
466
if reference_url is None:
467
return SuccessfulSmartServerResponse(('ok', ''))
469
return SuccessfulSmartServerResponse(('ok', reference_url))
470
except errors.NotBranchError, e:
471
return FailedSmartServerResponse(('nobranch',))
474
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
476
def do_bzrdir_request(self):
477
"""open a branch at path and return the reference or format."""
479
reference_url = self._bzrdir.get_branch_reference()
480
if reference_url is None:
481
br = self._bzrdir.open_branch(ignore_fallbacks=True)
482
format = br._format.network_name()
483
return SuccessfulSmartServerResponse(('branch', format))
485
return SuccessfulSmartServerResponse(('ref', reference_url))
486
except errors.NotBranchError, e:
487
return FailedSmartServerResponse(('nobranch',))
490
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
492
def do_bzrdir_request(self):
493
"""Open a branch at path and return the reference or format.
495
This version introduced in 2.1.
497
Differences to SmartServerRequestOpenBranchV2:
498
* can return 2-element ('nobranch', extra), where 'extra' is a string
499
with an explanation like 'location is a repository'. Previously
500
a 'nobranch' response would never have more than one element.
503
reference_url = self._bzrdir.get_branch_reference()
504
if reference_url is None:
505
br = self._bzrdir.open_branch(ignore_fallbacks=True)
506
format = br._format.network_name()
507
return SuccessfulSmartServerResponse(('branch', format))
509
return SuccessfulSmartServerResponse(('ref', reference_url))
510
except errors.NotBranchError, e:
511
# Stringify the exception so that its .detail attribute will be
517
if detail.startswith(': '):
520
return FailedSmartServerResponse(resp)