1
# Copyright (C) 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
"""ControlDir is the basic control directory class.
19
The ControlDir class is the base for the control directory used
20
by all bzr and foreign formats. For the ".bzr" implementation,
21
see bzrlib.bzrdir.BzrDir.
24
from bzrlib.lazy_import import lazy_import
25
lazy_import(globals(), """
29
revision as _mod_revision,
32
from bzrlib.push import (
35
from bzrlib.trace import (
38
from bzrlib.transport import (
45
class ControlDir(object):
46
"""A control directory."""
48
def can_convert_format(self):
49
"""Return true if this bzrdir is one whose format we can convert from."""
52
def list_branches(self):
53
"""Return a sequence of all branches local to this control directory.
57
return [self.open_branch()]
58
except (errors.NotBranchError, errors.NoRepositoryPresent):
61
def is_control_filename(self, filename):
62
"""True if filename is the name of a path which is reserved for bzrdir's.
64
:param filename: A filename within the root transport of this bzrdir.
66
This is true IF and ONLY IF the filename is part of the namespace reserved
67
for bzr control dirs. Currently this is the '.bzr' directory in the root
68
of the root_transport. it is expected that plugins will need to extend
69
this in the future - for instance to make bzr talk with svn working
72
raise NotImplementedError(self.is_control_filename)
74
def needs_format_conversion(self, format=None):
75
"""Return true if this bzrdir needs convert_format run on it.
77
For instance, if the repository format is out of date but the
78
branch and working tree are not, this should return True.
80
:param format: Optional parameter indicating a specific desired
81
format we plan to arrive at.
83
raise NotImplementedError(self.needs_format_conversion)
85
def destroy_repository(self):
86
"""Destroy the repository in this BzrDir"""
87
raise NotImplementedError(self.destroy_repository)
89
def create_branch(self, name=None):
90
"""Create a branch in this BzrDir.
92
:param name: Name of the colocated branch to create, None for
95
The bzrdir's format will control what branch format is created.
96
For more control see BranchFormatXX.create(a_bzrdir).
98
raise NotImplementedError(self.create_branch)
100
def destroy_branch(self, name=None):
101
"""Destroy a branch in this BzrDir.
103
:param name: Name of the branch to destroy, None for the default
106
raise NotImplementedError(self.destroy_branch)
108
def create_workingtree(self, revision_id=None, from_branch=None,
109
accelerator_tree=None, hardlink=False):
110
"""Create a working tree at this BzrDir.
112
:param revision_id: create it as of this revision id.
113
:param from_branch: override bzrdir branch (for lightweight checkouts)
114
:param accelerator_tree: A tree which can be used for retrieving file
115
contents more quickly than the revision tree, i.e. a workingtree.
116
The revision tree will be used for cases where accelerator_tree's
117
content is different.
119
raise NotImplementedError(self.create_workingtree)
121
def destroy_workingtree(self):
122
"""Destroy the working tree at this BzrDir.
124
Formats that do not support this may raise UnsupportedOperation.
126
raise NotImplementedError(self.destroy_workingtree)
128
def destroy_workingtree_metadata(self):
129
"""Destroy the control files for the working tree at this BzrDir.
131
The contents of working tree files are not affected.
132
Formats that do not support this may raise UnsupportedOperation.
134
raise NotImplementedError(self.destroy_workingtree_metadata)
136
def get_branch_reference(self, name=None):
137
"""Return the referenced URL for the branch in this bzrdir.
139
:param name: Optional colocated branch name
140
:raises NotBranchError: If there is no Branch.
141
:raises NoColocatedBranchSupport: If a branch name was specified
142
but colocated branches are not supported.
143
:return: The URL the branch in this bzrdir references if it is a
144
reference branch, or None for regular branches.
147
raise errors.NoColocatedBranchSupport(self)
150
def get_branch_transport(self, branch_format, name=None):
151
"""Get the transport for use by branch format in this BzrDir.
153
Note that bzr dirs that do not support format strings will raise
154
IncompatibleFormat if the branch format they are given has
155
a format string, and vice versa.
157
If branch_format is None, the transport is returned with no
158
checking. If it is not None, then the returned transport is
159
guaranteed to point to an existing directory ready for use.
161
raise NotImplementedError(self.get_branch_transport)
163
def get_repository_transport(self, repository_format):
164
"""Get the transport for use by repository format in this BzrDir.
166
Note that bzr dirs that do not support format strings will raise
167
IncompatibleFormat if the repository format they are given has
168
a format string, and vice versa.
170
If repository_format is None, the transport is returned with no
171
checking. If it is not None, then the returned transport is
172
guaranteed to point to an existing directory ready for use.
174
raise NotImplementedError(self.get_repository_transport)
176
def get_workingtree_transport(self, tree_format):
177
"""Get the transport for use by workingtree format in this BzrDir.
179
Note that bzr dirs that do not support format strings will raise
180
IncompatibleFormat if the workingtree format they are given has a
181
format string, and vice versa.
183
If workingtree_format is None, the transport is returned with no
184
checking. If it is not None, then the returned transport is
185
guaranteed to point to an existing directory ready for use.
187
raise NotImplementedError(self.get_workingtree_transport)
189
def open_branch(self, name=None, unsupported=False,
190
ignore_fallbacks=False):
191
"""Open the branch object at this BzrDir if one is present.
193
If unsupported is True, then no longer supported branch formats can
196
TODO: static convenience version of this?
198
raise NotImplementedError(self.open_branch)
200
def open_repository(self, _unsupported=False):
201
"""Open the repository object at this BzrDir if one is present.
203
This will not follow the Branch object pointer - it's strictly a direct
204
open facility. Most client code should use open_branch().repository to
207
:param _unsupported: a private parameter, not part of the api.
208
TODO: static convenience version of this?
210
raise NotImplementedError(self.open_repository)
212
def find_repository(self):
213
"""Find the repository that should be used.
215
This does not require a branch as we use it to find the repo for
216
new branches as well as to hook existing branches up to their
219
raise NotImplementedError(self.find_repository)
221
def open_workingtree(self, _unsupported=False,
222
recommend_upgrade=True, from_branch=None):
223
"""Open the workingtree object at this BzrDir if one is present.
225
:param recommend_upgrade: Optional keyword parameter, when True (the
226
default), emit through the ui module a recommendation that the user
227
upgrade the working tree when the workingtree being opened is old
228
(but still fully supported).
229
:param from_branch: override bzrdir branch (for lightweight checkouts)
231
raise NotImplementedError(self.open_workingtree)
233
def has_branch(self, name=None):
234
"""Tell if this bzrdir contains a branch.
236
Note: if you're going to open the branch, you should just go ahead
237
and try, and not ask permission first. (This method just opens the
238
branch and discards it, and that's somewhat expensive.)
241
self.open_branch(name)
243
except errors.NotBranchError:
246
def has_workingtree(self):
247
"""Tell if this bzrdir contains a working tree.
249
This will still raise an exception if the bzrdir has a workingtree that
250
is remote & inaccessible.
252
Note: if you're going to open the working tree, you should just go ahead
253
and try, and not ask permission first. (This method just opens the
254
workingtree and discards it, and that's somewhat expensive.)
257
self.open_workingtree(recommend_upgrade=False)
259
except errors.NoWorkingTree:
262
def cloning_metadir(self, require_stacking=False):
263
"""Produce a metadir suitable for cloning or sprouting with.
265
These operations may produce workingtrees (yes, even though they're
266
"cloning" something that doesn't have a tree), so a viable workingtree
267
format must be selected.
269
:require_stacking: If True, non-stackable formats will be upgraded
270
to similar stackable formats.
271
:returns: a BzrDirFormat with all component formats either set
272
appropriately or set to None if that component should not be
275
raise NotImplementedError(self.cloning_metadir)
277
def checkout_metadir(self):
278
"""Produce a metadir suitable for checkouts of this controldir."""
279
return self.cloning_metadir()
281
def sprout(self, url, revision_id=None, force_new_repo=False,
282
recurse='down', possible_transports=None,
283
accelerator_tree=None, hardlink=False, stacked=False,
284
source_branch=None, create_tree_if_local=True):
285
"""Create a copy of this bzrdir prepared for use as a new line of
288
If url's last component does not exist, it will be created.
290
Attributes related to the identity of the source branch like
291
branch nickname will be cleaned, a working tree is created
292
whether one existed before or not; and a local branch is always
295
if revision_id is not None, then the clone operation may tune
296
itself to download less data.
297
:param accelerator_tree: A tree which can be used for retrieving file
298
contents more quickly than the revision tree, i.e. a workingtree.
299
The revision tree will be used for cases where accelerator_tree's
300
content is different.
301
:param hardlink: If true, hard-link files from accelerator_tree,
303
:param stacked: If true, create a stacked branch referring to the
304
location of this control directory.
305
:param create_tree_if_local: If true, a working-tree will be created
306
when working locally.
308
target_transport = get_transport(url, possible_transports)
309
target_transport.ensure_base()
310
cloning_format = self.cloning_metadir(stacked)
311
# Create/update the result branch
312
result = cloning_format.initialize_on_transport(target_transport)
313
# if a stacked branch wasn't requested, we don't create one
314
# even if the origin was stacked
315
stacked_branch_url = None
316
if source_branch is not None:
318
stacked_branch_url = self.root_transport.base
319
source_repository = source_branch.repository
322
source_branch = self.open_branch()
323
source_repository = source_branch.repository
325
stacked_branch_url = self.root_transport.base
326
except errors.NotBranchError:
329
source_repository = self.open_repository()
330
except errors.NoRepositoryPresent:
331
source_repository = None
332
repository_policy = result.determine_repository_policy(
333
force_new_repo, stacked_branch_url, require_stacking=stacked)
334
result_repo, is_new_repo = repository_policy.acquire_repository()
335
is_stacked = stacked or (len(result_repo._fallback_repositories) != 0)
336
if is_new_repo and revision_id is not None and not is_stacked:
337
fetch_spec = graph.PendingAncestryResult(
338
[revision_id], source_repository)
341
if source_repository is not None:
342
# Fetch while stacked to prevent unstacked fetch from
344
if fetch_spec is None:
345
result_repo.fetch(source_repository, revision_id=revision_id)
347
result_repo.fetch(source_repository, fetch_spec=fetch_spec)
349
if source_branch is None:
350
# this is for sprouting a bzrdir without a branch; is that
352
# Not especially, but it's part of the contract.
353
result_branch = result.create_branch()
355
result_branch = source_branch.sprout(result,
356
revision_id=revision_id, repository_policy=repository_policy)
357
mutter("created new branch %r" % (result_branch,))
359
# Create/update the result working tree
360
if (create_tree_if_local and
361
isinstance(target_transport, local.LocalTransport) and
362
(result_repo is None or result_repo.make_working_trees())):
363
wt = result.create_workingtree(accelerator_tree=accelerator_tree,
367
if wt.path2id('') is None:
369
wt.set_root_id(self.open_workingtree.get_root_id())
370
except errors.NoWorkingTree:
376
if recurse == 'down':
378
basis = wt.basis_tree()
380
subtrees = basis.iter_references()
381
elif result_branch is not None:
382
basis = result_branch.basis_tree()
384
subtrees = basis.iter_references()
385
elif source_branch is not None:
386
basis = source_branch.basis_tree()
388
subtrees = basis.iter_references()
393
for path, file_id in subtrees:
394
target = urlutils.join(url, urlutils.escape(path))
395
sublocation = source_branch.reference_parent(file_id, path)
396
sublocation.bzrdir.sprout(target,
397
basis.get_reference_revision(file_id, path),
398
force_new_repo=force_new_repo, recurse=recurse,
401
if basis is not None:
405
def push_branch(self, source, revision_id=None, overwrite=False,
406
remember=False, create_prefix=False):
407
"""Push the source branch into this BzrDir."""
409
# If we can open a branch, use its direct repository, otherwise see
410
# if there is a repository without a branch.
412
br_to = self.open_branch()
413
except errors.NotBranchError:
414
# Didn't find a branch, can we find a repository?
415
repository_to = self.find_repository()
417
# Found a branch, so we must have found a repository
418
repository_to = br_to.repository
420
push_result = PushResult()
421
push_result.source_branch = source
423
# We have a repository but no branch, copy the revisions, and then
425
repository_to.fetch(source.repository, revision_id=revision_id)
426
br_to = source.clone(self, revision_id=revision_id)
427
if source.get_push_location() is None or remember:
428
source.set_push_location(br_to.base)
429
push_result.stacked_on = None
430
push_result.branch_push_result = None
431
push_result.old_revno = None
432
push_result.old_revid = _mod_revision.NULL_REVISION
433
push_result.target_branch = br_to
434
push_result.master_branch = None
435
push_result.workingtree_updated = False
437
# We have successfully opened the branch, remember if necessary:
438
if source.get_push_location() is None or remember:
439
source.set_push_location(br_to.base)
441
tree_to = self.open_workingtree()
442
except errors.NotLocalUrl:
443
push_result.branch_push_result = source.push(br_to,
444
overwrite, stop_revision=revision_id)
445
push_result.workingtree_updated = False
446
except errors.NoWorkingTree:
447
push_result.branch_push_result = source.push(br_to,
448
overwrite, stop_revision=revision_id)
449
push_result.workingtree_updated = None # Not applicable
453
push_result.branch_push_result = source.push(
454
tree_to.branch, overwrite, stop_revision=revision_id)
458
push_result.workingtree_updated = True
459
push_result.old_revno = push_result.branch_push_result.old_revno
460
push_result.old_revid = push_result.branch_push_result.old_revid
461
push_result.target_branch = \
462
push_result.branch_push_result.target_branch
466
class ControlDirFormat(object):
467
"""An encapsulation of the initialization and open routines for a format.
469
Formats provide three things:
470
* An initialization routine,
474
Formats are placed in a dict by their format string for reference
475
during bzrdir opening. These should be subclasses of BzrDirFormat
478
Once a format is deprecated, just deprecate the initialize and open
479
methods on the format class. Do not deprecate the object, as the
480
object will be created every system load.
482
:cvar colocated_branches: Whether this formats supports colocated branches.
485
_default_format = None
486
"""The default format used for new control directories."""
489
"""The registered control formats - .bzr, ....
491
This is a list of ControlDirFormat objects.
495
"""The registered control server formats, e.g. RemoteBzrDirs.
497
This is a list of ControlDirFormat objects.
500
colocated_branches = False
501
"""Whether co-located branches are supported for this control dir format.
504
def get_format_description(self):
505
"""Return the short description for this format."""
506
raise NotImplementedError(self.get_format_description)
508
def get_converter(self, format=None):
509
"""Return the converter to use to convert bzrdirs needing converts.
511
This returns a bzrlib.bzrdir.Converter object.
513
This should return the best upgrader to step this format towards the
514
current default format. In the case of plugins we can/should provide
515
some means for them to extend the range of returnable converters.
517
:param format: Optional format to override the default format of the
520
raise NotImplementedError(self.get_converter)
522
def is_supported(self):
523
"""Is this format supported?
525
Supported formats must be initializable and openable.
526
Unsupported formats may not support initialization or committing or
527
some other features depending on the reason for not being supported.
531
def same_model(self, target_format):
532
return (self.repository_format.rich_root_data ==
533
target_format.rich_root_data)
536
def register_format(klass, format):
537
"""Register a format that does not use '.bzr' for its control dir.
539
TODO: This should be pulled up into a 'ControlDirFormat' base class
540
which BzrDirFormat can inherit from, and renamed to register_format
541
there. It has been done without that for now for simplicity of
544
klass._formats.append(format)
547
def register_server_format(klass, format):
548
"""Register a control format for client-server environments.
550
These formats will be tried before ones registered with
551
register_format. This gives implementations that decide to the
552
chance to grab it before anything looks at the contents of the format
555
klass._server_formats.append(format)
559
return self.get_format_description().rstrip()
562
def unregister_format(klass, format):
563
klass._formats.remove(format)
566
def known_formats(klass):
567
"""Return all the known formats.
569
return set(klass._formats)
572
def find_format(klass, transport, _server_formats=True):
573
"""Return the format present at transport."""
575
_probers = server_probers = probers
578
for prober_kls in _probers:
579
prober = prober_kls()
581
return prober.probe_transport(transport)
582
except errors.NotBranchError:
583
# this format does not find a control dir here.
585
raise errors.NotBranchError(path=transport.base)
587
def initialize(self, url, possible_transports=None):
588
"""Create a control dir at this url and return an opened copy.
590
While not deprecated, this method is very specific and its use will
591
lead to many round trips to setup a working environment. See
592
initialize_on_transport_ex for a [nearly] all-in-one method.
594
Subclasses should typically override initialize_on_transport
595
instead of this method.
597
return self.initialize_on_transport(get_transport(url,
598
possible_transports))
599
def initialize_on_transport(self, transport):
600
"""Initialize a new bzrdir in the base directory of a Transport."""
601
raise NotImplementedError(self.initialize_on_transport)
603
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
604
create_prefix=False, force_new_repo=False, stacked_on=None,
605
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
606
shared_repo=False, vfs_only=False):
607
"""Create this format on transport.
609
The directory to initialize will be created.
611
:param force_new_repo: Do not use a shared repository for the target,
612
even if one is available.
613
:param create_prefix: Create any missing directories leading up to
615
:param use_existing_dir: Use an existing directory if one exists.
616
:param stacked_on: A url to stack any created branch on, None to follow
617
any target stacking policy.
618
:param stack_on_pwd: If stack_on is relative, the location it is
620
:param repo_format_name: If non-None, a repository will be
621
made-or-found. Should none be found, or if force_new_repo is True
622
the repo_format_name is used to select the format of repository to
624
:param make_working_trees: Control the setting of make_working_trees
625
for a new shared repository when one is made. None to use whatever
626
default the format has.
627
:param shared_repo: Control whether made repositories are shared or
629
:param vfs_only: If True do not attempt to use a smart server
630
:return: repo, bzrdir, require_stacking, repository_policy. repo is
631
None if none was created or found, bzrdir is always valid.
632
require_stacking is the result of examining the stacked_on
633
parameter and any stacking policy found for the target.
635
raise NotImplementedError(self.initialize_on_transport_ex)
637
def network_name(self):
638
"""A simple byte string uniquely identifying this format for RPC calls.
640
Bzr control formats use this disk format string to identify the format
641
over the wire. Its possible that other control formats have more
642
complex detection requirements, so we permit them to use any unique and
643
immutable string they desire.
645
raise NotImplementedError(self.network_name)
647
def open(self, transport, _found=False):
648
"""Return an instance of this format for the dir transport points at.
650
raise NotImplementedError(self.open)
653
def _set_default_format(klass, format):
654
"""Set default format (for testing behavior of defaults only)"""
655
klass._default_format = format
658
def get_default_format(klass):
659
"""Return the current default format."""
660
return klass._default_format
663
class Prober(object):
665
def probe_transport(self, transport):
666
"""Return the controldir style format present in a directory."""
667
raise NotImplementedError(self.probe_transport)