1
# Copyright (C) 2005, 2006, 2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
# TODO: Can we move specific formats into separate modules to make this file
26
from cStringIO import StringIO
29
from bzrlib.lazy_import import lazy_import
30
lazy_import(globals(), """
31
from stat import S_ISDIR
33
from warnings import warn
43
revision as _mod_revision,
52
from bzrlib.osutils import (
56
from bzrlib.smart.client import _SmartClient
57
from bzrlib.smart import protocol
58
from bzrlib.store.revision.text import TextRevisionStore
59
from bzrlib.store.text import TextStore
60
from bzrlib.store.versioned import WeaveStore
61
from bzrlib.transactions import WriteTransaction
62
from bzrlib.transport import (
63
do_catching_redirections,
66
from bzrlib.weave import Weave
69
from bzrlib.trace import (
73
from bzrlib.transport.local import LocalTransport
74
from bzrlib.symbol_versioning import (
82
"""A .bzr control diretory.
84
BzrDir instances let you create or open any of the things that can be
85
found within .bzr - checkouts, branches and repositories.
88
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
90
a transport connected to the directory this bzr was opened from.
94
"""Invoke break_lock on the first object in the bzrdir.
96
If there is a tree, the tree is opened and break_lock() called.
97
Otherwise, branch is tried, and finally repository.
99
# XXX: This seems more like a UI function than something that really
100
# belongs in this class.
102
thing_to_unlock = self.open_workingtree()
103
except (errors.NotLocalUrl, errors.NoWorkingTree):
105
thing_to_unlock = self.open_branch()
106
except errors.NotBranchError:
108
thing_to_unlock = self.open_repository()
109
except errors.NoRepositoryPresent:
111
thing_to_unlock.break_lock()
113
def can_convert_format(self):
114
"""Return true if this bzrdir is one whose format we can convert from."""
117
def check_conversion_target(self, target_format):
118
target_repo_format = target_format.repository_format
119
source_repo_format = self._format.repository_format
120
source_repo_format.check_conversion_target(target_repo_format)
123
def _check_supported(format, allow_unsupported,
124
recommend_upgrade=True,
126
"""Give an error or warning on old formats.
128
:param format: may be any kind of format - workingtree, branch,
131
:param allow_unsupported: If true, allow opening
132
formats that are strongly deprecated, and which may
133
have limited functionality.
135
:param recommend_upgrade: If true (default), warn
136
the user through the ui object that they may wish
137
to upgrade the object.
139
# TODO: perhaps move this into a base Format class; it's not BzrDir
140
# specific. mbp 20070323
141
if not allow_unsupported and not format.is_supported():
142
# see open_downlevel to open legacy branches.
143
raise errors.UnsupportedFormatError(format=format)
144
if recommend_upgrade \
145
and getattr(format, 'upgrade_recommended', False):
146
ui.ui_factory.recommend_upgrade(
147
format.get_format_description(),
150
def clone(self, url, revision_id=None, force_new_repo=False):
151
"""Clone this bzrdir and its contents to url verbatim.
153
If urls last component does not exist, it will be created.
155
if revision_id is not None, then the clone operation may tune
156
itself to download less data.
157
:param force_new_repo: Do not use a shared repository for the target
158
even if one is available.
160
return self.clone_on_transport(get_transport(url),
161
revision_id=revision_id,
162
force_new_repo=force_new_repo)
164
def clone_on_transport(self, transport, revision_id=None,
165
force_new_repo=False):
166
"""Clone this bzrdir and its contents to transport verbatim.
168
If the target directory does not exist, it will be created.
170
if revision_id is not None, then the clone operation may tune
171
itself to download less data.
172
:param force_new_repo: Do not use a shared repository for the target
173
even if one is available.
175
transport.ensure_base()
176
result = self._format.initialize_on_transport(transport)
178
local_repo = self.find_repository()
179
except errors.NoRepositoryPresent:
182
# may need to copy content in
184
result_repo = local_repo.clone(
186
revision_id=revision_id)
187
result_repo.set_make_working_trees(local_repo.make_working_trees())
190
result_repo = result.find_repository()
191
# fetch content this dir needs.
192
result_repo.fetch(local_repo, revision_id=revision_id)
193
except errors.NoRepositoryPresent:
194
# needed to make one anyway.
195
result_repo = local_repo.clone(
197
revision_id=revision_id)
198
result_repo.set_make_working_trees(local_repo.make_working_trees())
199
# 1 if there is a branch present
200
# make sure its content is available in the target repository
203
self.open_branch().clone(result, revision_id=revision_id)
204
except errors.NotBranchError:
207
self.open_workingtree().clone(result)
208
except (errors.NoWorkingTree, errors.NotLocalUrl):
212
# TODO: This should be given a Transport, and should chdir up; otherwise
213
# this will open a new connection.
214
def _make_tail(self, url):
215
t = get_transport(url)
219
def create(cls, base, format=None, possible_transports=None):
220
"""Create a new BzrDir at the url 'base'.
222
This will call the current default formats initialize with base
223
as the only parameter.
225
:param format: If supplied, the format of branch to create. If not
226
supplied, the default is used.
227
:param possible_transports: If supplied, a list of transports that
228
can be reused to share a remote connection.
230
if cls is not BzrDir:
231
raise AssertionError("BzrDir.create always creates the default"
232
" format, not one of %r" % cls)
233
t = get_transport(base, possible_transports)
236
format = BzrDirFormat.get_default_format()
237
return format.initialize(base, possible_transports)
239
def destroy_repository(self):
240
"""Destroy the repository in this BzrDir"""
241
raise NotImplementedError(self.destroy_repository)
243
def create_branch(self):
244
"""Create a branch in this BzrDir.
246
The bzrdirs format will control what branch format is created.
247
For more control see BranchFormatXX.create(a_bzrdir).
249
raise NotImplementedError(self.create_branch)
251
def destroy_branch(self):
252
"""Destroy the branch in this BzrDir"""
253
raise NotImplementedError(self.destroy_branch)
256
def create_branch_and_repo(base, force_new_repo=False, format=None):
257
"""Create a new BzrDir, Branch and Repository at the url 'base'.
259
This will use the current default BzrDirFormat, and use whatever
260
repository format that that uses via bzrdir.create_branch and
261
create_repository. If a shared repository is available that is used
264
The created Branch object is returned.
266
:param base: The URL to create the branch at.
267
:param force_new_repo: If True a new repository is always created.
269
bzrdir = BzrDir.create(base, format)
270
bzrdir._find_or_create_repository(force_new_repo)
271
return bzrdir.create_branch()
273
def _find_or_create_repository(self, force_new_repo):
274
"""Create a new repository if needed, returning the repository."""
276
return self.create_repository()
278
return self.find_repository()
279
except errors.NoRepositoryPresent:
280
return self.create_repository()
283
def create_branch_convenience(base, force_new_repo=False,
284
force_new_tree=None, format=None,
285
possible_transports=None):
286
"""Create a new BzrDir, Branch and Repository at the url 'base'.
288
This is a convenience function - it will use an existing repository
289
if possible, can be told explicitly whether to create a working tree or
292
This will use the current default BzrDirFormat, and use whatever
293
repository format that that uses via bzrdir.create_branch and
294
create_repository. If a shared repository is available that is used
295
preferentially. Whatever repository is used, its tree creation policy
298
The created Branch object is returned.
299
If a working tree cannot be made due to base not being a file:// url,
300
no error is raised unless force_new_tree is True, in which case no
301
data is created on disk and NotLocalUrl is raised.
303
:param base: The URL to create the branch at.
304
:param force_new_repo: If True a new repository is always created.
305
:param force_new_tree: If True or False force creation of a tree or
306
prevent such creation respectively.
307
:param format: Override for the for the bzrdir format to create.
308
:param possible_transports: An optional reusable transports list.
311
# check for non local urls
312
t = get_transport(base, possible_transports)
313
if not isinstance(t, LocalTransport):
314
raise errors.NotLocalUrl(base)
315
bzrdir = BzrDir.create(base, format, possible_transports)
316
repo = bzrdir._find_or_create_repository(force_new_repo)
317
result = bzrdir.create_branch()
318
if force_new_tree or (repo.make_working_trees() and
319
force_new_tree is None):
321
bzrdir.create_workingtree()
322
except errors.NotLocalUrl:
327
@deprecated_function(zero_ninetyone)
328
def create_repository(base, shared=False, format=None):
329
"""Create a new BzrDir and Repository at the url 'base'.
331
If no format is supplied, this will default to the current default
332
BzrDirFormat by default, and use whatever repository format that that
333
uses for bzrdirformat.create_repository.
335
:param shared: Create a shared repository rather than a standalone
337
The Repository object is returned.
339
This must be overridden as an instance method in child classes, where
340
it should take no parameters and construct whatever repository format
341
that child class desires.
343
This method is deprecated, please call create_repository on a bzrdir
346
bzrdir = BzrDir.create(base, format)
347
return bzrdir.create_repository(shared)
350
def create_standalone_workingtree(base, format=None):
351
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
353
'base' must be a local path or a file:// url.
355
This will use the current default BzrDirFormat, and use whatever
356
repository format that that uses for bzrdirformat.create_workingtree,
357
create_branch and create_repository.
359
:return: The WorkingTree object.
361
t = get_transport(base)
362
if not isinstance(t, LocalTransport):
363
raise errors.NotLocalUrl(base)
364
bzrdir = BzrDir.create_branch_and_repo(base,
366
format=format).bzrdir
367
return bzrdir.create_workingtree()
369
def create_workingtree(self, revision_id=None):
370
"""Create a working tree at this BzrDir.
372
revision_id: create it as of this revision id.
374
raise NotImplementedError(self.create_workingtree)
376
def retire_bzrdir(self):
377
"""Permanently disable the bzrdir.
379
This is done by renaming it to give the user some ability to recover
380
if there was a problem.
382
This will have horrible consequences if anyone has anything locked or
385
for i in xrange(10000):
387
to_path = '.bzr.retired.%d' % i
388
self.root_transport.rename('.bzr', to_path)
389
note("renamed %s to %s"
390
% (self.root_transport.abspath('.bzr'), to_path))
392
except (errors.TransportError, IOError, errors.PathError):
395
def destroy_workingtree(self):
396
"""Destroy the working tree at this BzrDir.
398
Formats that do not support this may raise UnsupportedOperation.
400
raise NotImplementedError(self.destroy_workingtree)
402
def destroy_workingtree_metadata(self):
403
"""Destroy the control files for the working tree at this BzrDir.
405
The contents of working tree files are not affected.
406
Formats that do not support this may raise UnsupportedOperation.
408
raise NotImplementedError(self.destroy_workingtree_metadata)
410
def find_repository(self):
411
"""Find the repository that should be used for a_bzrdir.
413
This does not require a branch as we use it to find the repo for
414
new branches as well as to hook existing branches up to their
418
return self.open_repository()
419
except errors.NoRepositoryPresent:
421
next_transport = self.root_transport.clone('..')
423
# find the next containing bzrdir
425
found_bzrdir = BzrDir.open_containing_from_transport(
427
except errors.NotBranchError:
429
raise errors.NoRepositoryPresent(self)
430
# does it have a repository ?
432
repository = found_bzrdir.open_repository()
433
except errors.NoRepositoryPresent:
434
next_transport = found_bzrdir.root_transport.clone('..')
435
if (found_bzrdir.root_transport.base == next_transport.base):
436
# top of the file system
440
if ((found_bzrdir.root_transport.base ==
441
self.root_transport.base) or repository.is_shared()):
444
raise errors.NoRepositoryPresent(self)
445
raise errors.NoRepositoryPresent(self)
447
def get_branch_reference(self):
448
"""Return the referenced URL for the branch in this bzrdir.
450
:raises NotBranchError: If there is no Branch.
451
:return: The URL the branch in this bzrdir references if it is a
452
reference branch, or None for regular branches.
456
def get_branch_transport(self, branch_format):
457
"""Get the transport for use by branch format in this BzrDir.
459
Note that bzr dirs that do not support format strings will raise
460
IncompatibleFormat if the branch format they are given has
461
a format string, and vice versa.
463
If branch_format is None, the transport is returned with no
464
checking. if it is not None, then the returned transport is
465
guaranteed to point to an existing directory ready for use.
467
raise NotImplementedError(self.get_branch_transport)
469
def get_repository_transport(self, repository_format):
470
"""Get the transport for use by repository format in this BzrDir.
472
Note that bzr dirs that do not support format strings will raise
473
IncompatibleFormat if the repository format they are given has
474
a format string, and vice versa.
476
If repository_format is None, the transport is returned with no
477
checking. if it is not None, then the returned transport is
478
guaranteed to point to an existing directory ready for use.
480
raise NotImplementedError(self.get_repository_transport)
482
def get_workingtree_transport(self, tree_format):
483
"""Get the transport for use by workingtree format in this BzrDir.
485
Note that bzr dirs that do not support format strings will raise
486
IncompatibleFormat if the workingtree format they are given has a
487
format string, and vice versa.
489
If workingtree_format is None, the transport is returned with no
490
checking. if it is not None, then the returned transport is
491
guaranteed to point to an existing directory ready for use.
493
raise NotImplementedError(self.get_workingtree_transport)
495
def __init__(self, _transport, _format):
496
"""Initialize a Bzr control dir object.
498
Only really common logic should reside here, concrete classes should be
499
made with varying behaviours.
501
:param _format: the format that is creating this BzrDir instance.
502
:param _transport: the transport this dir is based at.
504
self._format = _format
505
self.transport = _transport.clone('.bzr')
506
self.root_transport = _transport
508
def is_control_filename(self, filename):
509
"""True if filename is the name of a path which is reserved for bzrdir's.
511
:param filename: A filename within the root transport of this bzrdir.
513
This is true IF and ONLY IF the filename is part of the namespace reserved
514
for bzr control dirs. Currently this is the '.bzr' directory in the root
515
of the root_transport. it is expected that plugins will need to extend
516
this in the future - for instance to make bzr talk with svn working
519
# this might be better on the BzrDirFormat class because it refers to
520
# all the possible bzrdir disk formats.
521
# This method is tested via the workingtree is_control_filename tests-
522
# it was extracted from WorkingTree.is_control_filename. If the methods
523
# contract is extended beyond the current trivial implementation please
524
# add new tests for it to the appropriate place.
525
return filename == '.bzr' or filename.startswith('.bzr/')
527
def needs_format_conversion(self, format=None):
528
"""Return true if this bzrdir needs convert_format run on it.
530
For instance, if the repository format is out of date but the
531
branch and working tree are not, this should return True.
533
:param format: Optional parameter indicating a specific desired
534
format we plan to arrive at.
536
raise NotImplementedError(self.needs_format_conversion)
539
def open_unsupported(base):
540
"""Open a branch which is not supported."""
541
return BzrDir.open(base, _unsupported=True)
544
def open(base, _unsupported=False, possible_transports=None):
545
"""Open an existing bzrdir, rooted at 'base' (url)
547
_unsupported is a private parameter to the BzrDir class.
549
t = get_transport(base, possible_transports=possible_transports)
550
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
553
def open_from_transport(transport, _unsupported=False,
554
_server_formats=True):
555
"""Open a bzrdir within a particular directory.
557
:param transport: Transport containing the bzrdir.
558
:param _unsupported: private.
560
base = transport.base
562
def find_format(transport):
563
return transport, BzrDirFormat.find_format(
564
transport, _server_formats=_server_formats)
566
def redirected(transport, e, redirection_notice):
567
qualified_source = e.get_source_url()
568
relpath = transport.relpath(qualified_source)
569
if not e.target.endswith(relpath):
570
# Not redirected to a branch-format, not a branch
571
raise errors.NotBranchError(path=e.target)
572
target = e.target[:-len(relpath)]
573
note('%s is%s redirected to %s',
574
transport.base, e.permanently, target)
575
# Let's try with a new transport
576
qualified_target = e.get_target_url()[:-len(relpath)]
577
# FIXME: If 'transport' has a qualifier, this should
578
# be applied again to the new transport *iff* the
579
# schemes used are the same. It's a bit tricky to
580
# verify, so I'll punt for now
582
return get_transport(target)
585
transport, format = do_catching_redirections(find_format,
588
except errors.TooManyRedirections:
589
raise errors.NotBranchError(base)
591
BzrDir._check_supported(format, _unsupported)
592
return format.open(transport, _found=True)
594
def open_branch(self, unsupported=False):
595
"""Open the branch object at this BzrDir if one is present.
597
If unsupported is True, then no longer supported branch formats can
600
TODO: static convenience version of this?
602
raise NotImplementedError(self.open_branch)
605
def open_containing(url, possible_transports=None):
606
"""Open an existing branch which contains url.
608
:param url: url to search from.
609
See open_containing_from_transport for more detail.
611
transport = get_transport(url, possible_transports)
612
return BzrDir.open_containing_from_transport(transport)
615
def open_containing_from_transport(a_transport):
616
"""Open an existing branch which contains a_transport.base
618
This probes for a branch at a_transport, and searches upwards from there.
620
Basically we keep looking up until we find the control directory or
621
run into the root. If there isn't one, raises NotBranchError.
622
If there is one and it is either an unrecognised format or an unsupported
623
format, UnknownFormatError or UnsupportedFormatError are raised.
624
If there is one, it is returned, along with the unused portion of url.
626
:return: The BzrDir that contains the path, and a Unicode path
627
for the rest of the URL.
629
# this gets the normalised url back. I.e. '.' -> the full path.
630
url = a_transport.base
633
result = BzrDir.open_from_transport(a_transport)
634
return result, urlutils.unescape(a_transport.relpath(url))
635
except errors.NotBranchError, e:
638
new_t = a_transport.clone('..')
639
except errors.InvalidURLJoin:
640
# reached the root, whatever that may be
641
raise errors.NotBranchError(path=url)
642
if new_t.base == a_transport.base:
643
# reached the root, whatever that may be
644
raise errors.NotBranchError(path=url)
648
def open_containing_tree_or_branch(klass, location):
649
"""Return the branch and working tree contained by a location.
651
Returns (tree, branch, relpath).
652
If there is no tree at containing the location, tree will be None.
653
If there is no branch containing the location, an exception will be
655
relpath is the portion of the path that is contained by the branch.
657
bzrdir, relpath = klass.open_containing(location)
659
tree = bzrdir.open_workingtree()
660
except (errors.NoWorkingTree, errors.NotLocalUrl):
662
branch = bzrdir.open_branch()
665
return tree, branch, relpath
667
def open_repository(self, _unsupported=False):
668
"""Open the repository object at this BzrDir if one is present.
670
This will not follow the Branch object pointer - its strictly a direct
671
open facility. Most client code should use open_branch().repository to
674
_unsupported is a private parameter, not part of the api.
675
TODO: static convenience version of this?
677
raise NotImplementedError(self.open_repository)
679
def open_workingtree(self, _unsupported=False,
680
recommend_upgrade=True):
681
"""Open the workingtree object at this BzrDir if one is present.
683
:param recommend_upgrade: Optional keyword parameter, when True (the
684
default), emit through the ui module a recommendation that the user
685
upgrade the working tree when the workingtree being opened is old
686
(but still fully supported).
688
raise NotImplementedError(self.open_workingtree)
690
def has_branch(self):
691
"""Tell if this bzrdir contains a branch.
693
Note: if you're going to open the branch, you should just go ahead
694
and try, and not ask permission first. (This method just opens the
695
branch and discards it, and that's somewhat expensive.)
700
except errors.NotBranchError:
703
def has_workingtree(self):
704
"""Tell if this bzrdir contains a working tree.
706
This will still raise an exception if the bzrdir has a workingtree that
707
is remote & inaccessible.
709
Note: if you're going to open the working tree, you should just go ahead
710
and try, and not ask permission first. (This method just opens the
711
workingtree and discards it, and that's somewhat expensive.)
714
self.open_workingtree(recommend_upgrade=False)
716
except errors.NoWorkingTree:
719
def _cloning_metadir(self):
720
"""Produce a metadir suitable for cloning with"""
721
result_format = self._format.__class__()
724
branch = self.open_branch()
725
source_repository = branch.repository
726
except errors.NotBranchError:
728
source_repository = self.open_repository()
729
except errors.NoRepositoryPresent:
730
source_repository = None
732
# XXX TODO: This isinstance is here because we have not implemented
733
# the fix recommended in bug # 103195 - to delegate this choice the
735
repo_format = source_repository._format
736
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
737
result_format.repository_format = repo_format
739
# TODO: Couldn't we just probe for the format in these cases,
740
# rather than opening the whole tree? It would be a little
741
# faster. mbp 20070401
742
tree = self.open_workingtree(recommend_upgrade=False)
743
except (errors.NoWorkingTree, errors.NotLocalUrl):
744
result_format.workingtree_format = None
746
result_format.workingtree_format = tree._format.__class__()
747
return result_format, source_repository
749
def cloning_metadir(self):
750
"""Produce a metadir suitable for cloning or sprouting with.
752
These operations may produce workingtrees (yes, even though they're
753
"cloning" something that doesn't have a tree, so a viable workingtree
754
format must be selected.
756
format, repository = self._cloning_metadir()
757
if format._workingtree_format is None:
758
if repository is None:
760
tree_format = repository._format._matchingbzrdir.workingtree_format
761
format.workingtree_format = tree_format.__class__()
764
def checkout_metadir(self):
765
return self.cloning_metadir()
767
def sprout(self, url, revision_id=None, force_new_repo=False,
768
recurse='down', possible_transports=None):
769
"""Create a copy of this bzrdir prepared for use as a new line of
772
If urls last component does not exist, it will be created.
774
Attributes related to the identity of the source branch like
775
branch nickname will be cleaned, a working tree is created
776
whether one existed before or not; and a local branch is always
779
if revision_id is not None, then the clone operation may tune
780
itself to download less data.
782
target_transport = get_transport(url, possible_transports)
783
target_transport.ensure_base()
784
cloning_format = self.cloning_metadir()
785
result = cloning_format.initialize_on_transport(target_transport)
787
source_branch = self.open_branch()
788
source_repository = source_branch.repository
789
except errors.NotBranchError:
792
source_repository = self.open_repository()
793
except errors.NoRepositoryPresent:
794
source_repository = None
799
result_repo = result.find_repository()
800
except errors.NoRepositoryPresent:
802
if source_repository is None and result_repo is not None:
804
elif source_repository is None and result_repo is None:
805
# no repo available, make a new one
806
result.create_repository()
807
elif source_repository is not None and result_repo is None:
808
# have source, and want to make a new target repo
809
result_repo = source_repository.sprout(result,
810
revision_id=revision_id)
812
# fetch needed content into target.
813
if source_repository is not None:
815
# source_repository.copy_content_into(result_repo,
816
# revision_id=revision_id)
817
# so we can override the copy method
818
result_repo.fetch(source_repository, revision_id=revision_id)
819
if source_branch is not None:
820
source_branch.sprout(result, revision_id=revision_id)
822
result.create_branch()
823
if isinstance(target_transport, LocalTransport) and (
824
result_repo is None or result_repo.make_working_trees()):
825
wt = result.create_workingtree()
828
if wt.path2id('') is None:
830
wt.set_root_id(self.open_workingtree.get_root_id())
831
except errors.NoWorkingTree:
837
if recurse == 'down':
839
basis = wt.basis_tree()
841
subtrees = basis.iter_references()
842
recurse_branch = wt.branch
843
elif source_branch is not None:
844
basis = source_branch.basis_tree()
846
subtrees = basis.iter_references()
847
recurse_branch = source_branch
852
for path, file_id in subtrees:
853
target = urlutils.join(url, urlutils.escape(path))
854
sublocation = source_branch.reference_parent(file_id, path)
855
sublocation.bzrdir.sprout(target,
856
basis.get_reference_revision(file_id, path),
857
force_new_repo=force_new_repo, recurse=recurse)
859
if basis is not None:
864
class BzrDirPreSplitOut(BzrDir):
865
"""A common class for the all-in-one formats."""
867
def __init__(self, _transport, _format):
868
"""See BzrDir.__init__."""
869
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
870
assert self._format._lock_class == lockable_files.TransportLock
871
assert self._format._lock_file_name == 'branch-lock'
872
self._control_files = lockable_files.LockableFiles(
873
self.get_branch_transport(None),
874
self._format._lock_file_name,
875
self._format._lock_class)
877
def break_lock(self):
878
"""Pre-splitout bzrdirs do not suffer from stale locks."""
879
raise NotImplementedError(self.break_lock)
881
def clone(self, url, revision_id=None, force_new_repo=False):
882
"""See BzrDir.clone()."""
883
from bzrlib.workingtree import WorkingTreeFormat2
885
result = self._format._initialize_for_clone(url)
886
self.open_repository().clone(result, revision_id=revision_id)
887
from_branch = self.open_branch()
888
from_branch.clone(result, revision_id=revision_id)
890
self.open_workingtree().clone(result)
891
except errors.NotLocalUrl:
892
# make a new one, this format always has to have one.
894
WorkingTreeFormat2().initialize(result)
895
except errors.NotLocalUrl:
896
# but we cannot do it for remote trees.
897
to_branch = result.open_branch()
898
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
901
def create_branch(self):
902
"""See BzrDir.create_branch."""
903
return self.open_branch()
905
def destroy_branch(self):
906
"""See BzrDir.destroy_branch."""
907
raise errors.UnsupportedOperation(self.destroy_branch, self)
909
def create_repository(self, shared=False):
910
"""See BzrDir.create_repository."""
912
raise errors.IncompatibleFormat('shared repository', self._format)
913
return self.open_repository()
915
def destroy_repository(self):
916
"""See BzrDir.destroy_repository."""
917
raise errors.UnsupportedOperation(self.destroy_repository, self)
919
def create_workingtree(self, revision_id=None):
920
"""See BzrDir.create_workingtree."""
921
# this looks buggy but is not -really-
922
# because this format creates the workingtree when the bzrdir is
924
# clone and sprout will have set the revision_id
925
# and that will have set it for us, its only
926
# specific uses of create_workingtree in isolation
927
# that can do wonky stuff here, and that only
928
# happens for creating checkouts, which cannot be
929
# done on this format anyway. So - acceptable wart.
930
result = self.open_workingtree(recommend_upgrade=False)
931
if revision_id is not None:
932
if revision_id == _mod_revision.NULL_REVISION:
933
result.set_parent_ids([])
935
result.set_parent_ids([revision_id])
938
def destroy_workingtree(self):
939
"""See BzrDir.destroy_workingtree."""
940
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
942
def destroy_workingtree_metadata(self):
943
"""See BzrDir.destroy_workingtree_metadata."""
944
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
947
def get_branch_transport(self, branch_format):
948
"""See BzrDir.get_branch_transport()."""
949
if branch_format is None:
950
return self.transport
952
branch_format.get_format_string()
953
except NotImplementedError:
954
return self.transport
955
raise errors.IncompatibleFormat(branch_format, self._format)
957
def get_repository_transport(self, repository_format):
958
"""See BzrDir.get_repository_transport()."""
959
if repository_format is None:
960
return self.transport
962
repository_format.get_format_string()
963
except NotImplementedError:
964
return self.transport
965
raise errors.IncompatibleFormat(repository_format, self._format)
967
def get_workingtree_transport(self, workingtree_format):
968
"""See BzrDir.get_workingtree_transport()."""
969
if workingtree_format is None:
970
return self.transport
972
workingtree_format.get_format_string()
973
except NotImplementedError:
974
return self.transport
975
raise errors.IncompatibleFormat(workingtree_format, self._format)
977
def needs_format_conversion(self, format=None):
978
"""See BzrDir.needs_format_conversion()."""
979
# if the format is not the same as the system default,
980
# an upgrade is needed.
982
format = BzrDirFormat.get_default_format()
983
return not isinstance(self._format, format.__class__)
985
def open_branch(self, unsupported=False):
986
"""See BzrDir.open_branch."""
987
from bzrlib.branch import BzrBranchFormat4
988
format = BzrBranchFormat4()
989
self._check_supported(format, unsupported)
990
return format.open(self, _found=True)
992
def sprout(self, url, revision_id=None, force_new_repo=False,
993
possible_transports=None):
994
"""See BzrDir.sprout()."""
995
from bzrlib.workingtree import WorkingTreeFormat2
997
result = self._format._initialize_for_clone(url)
999
self.open_repository().clone(result, revision_id=revision_id)
1000
except errors.NoRepositoryPresent:
1003
self.open_branch().sprout(result, revision_id=revision_id)
1004
except errors.NotBranchError:
1006
# we always want a working tree
1007
WorkingTreeFormat2().initialize(result)
1011
class BzrDir4(BzrDirPreSplitOut):
1012
"""A .bzr version 4 control object.
1014
This is a deprecated format and may be removed after sept 2006.
1017
def create_repository(self, shared=False):
1018
"""See BzrDir.create_repository."""
1019
return self._format.repository_format.initialize(self, shared)
1021
def needs_format_conversion(self, format=None):
1022
"""Format 4 dirs are always in need of conversion."""
1025
def open_repository(self):
1026
"""See BzrDir.open_repository."""
1027
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1028
return RepositoryFormat4().open(self, _found=True)
1031
class BzrDir5(BzrDirPreSplitOut):
1032
"""A .bzr version 5 control object.
1034
This is a deprecated format and may be removed after sept 2006.
1037
def open_repository(self):
1038
"""See BzrDir.open_repository."""
1039
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1040
return RepositoryFormat5().open(self, _found=True)
1042
def open_workingtree(self, _unsupported=False,
1043
recommend_upgrade=True):
1044
"""See BzrDir.create_workingtree."""
1045
from bzrlib.workingtree import WorkingTreeFormat2
1046
wt_format = WorkingTreeFormat2()
1047
# we don't warn here about upgrades; that ought to be handled for the
1049
return wt_format.open(self, _found=True)
1052
class BzrDir6(BzrDirPreSplitOut):
1053
"""A .bzr version 6 control object.
1055
This is a deprecated format and may be removed after sept 2006.
1058
def open_repository(self):
1059
"""See BzrDir.open_repository."""
1060
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1061
return RepositoryFormat6().open(self, _found=True)
1063
def open_workingtree(self, _unsupported=False,
1064
recommend_upgrade=True):
1065
"""See BzrDir.create_workingtree."""
1066
# we don't warn here about upgrades; that ought to be handled for the
1068
from bzrlib.workingtree import WorkingTreeFormat2
1069
return WorkingTreeFormat2().open(self, _found=True)
1072
class BzrDirMeta1(BzrDir):
1073
"""A .bzr meta version 1 control object.
1075
This is the first control object where the
1076
individual aspects are really split out: there are separate repository,
1077
workingtree and branch subdirectories and any subset of the three can be
1078
present within a BzrDir.
1081
def can_convert_format(self):
1082
"""See BzrDir.can_convert_format()."""
1085
def create_branch(self):
1086
"""See BzrDir.create_branch."""
1087
return self._format.get_branch_format().initialize(self)
1089
def destroy_branch(self):
1090
"""See BzrDir.create_branch."""
1091
self.transport.delete_tree('branch')
1093
def create_repository(self, shared=False):
1094
"""See BzrDir.create_repository."""
1095
return self._format.repository_format.initialize(self, shared)
1097
def destroy_repository(self):
1098
"""See BzrDir.destroy_repository."""
1099
self.transport.delete_tree('repository')
1101
def create_workingtree(self, revision_id=None):
1102
"""See BzrDir.create_workingtree."""
1103
from bzrlib.workingtree import WorkingTreeFormat
1104
return self._format.workingtree_format.initialize(self, revision_id)
1106
def destroy_workingtree(self):
1107
"""See BzrDir.destroy_workingtree."""
1108
wt = self.open_workingtree(recommend_upgrade=False)
1109
repository = wt.branch.repository
1110
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1111
wt.revert(old_tree=empty)
1112
self.destroy_workingtree_metadata()
1114
def destroy_workingtree_metadata(self):
1115
self.transport.delete_tree('checkout')
1117
def find_branch_format(self):
1118
"""Find the branch 'format' for this bzrdir.
1120
This might be a synthetic object for e.g. RemoteBranch and SVN.
1122
from bzrlib.branch import BranchFormat
1123
return BranchFormat.find_format(self)
1125
def _get_mkdir_mode(self):
1126
"""Figure out the mode to use when creating a bzrdir subdir."""
1127
temp_control = lockable_files.LockableFiles(self.transport, '',
1128
lockable_files.TransportLock)
1129
return temp_control._dir_mode
1131
def get_branch_reference(self):
1132
"""See BzrDir.get_branch_reference()."""
1133
from bzrlib.branch import BranchFormat
1134
format = BranchFormat.find_format(self)
1135
return format.get_reference(self)
1137
def get_branch_transport(self, branch_format):
1138
"""See BzrDir.get_branch_transport()."""
1139
if branch_format is None:
1140
return self.transport.clone('branch')
1142
branch_format.get_format_string()
1143
except NotImplementedError:
1144
raise errors.IncompatibleFormat(branch_format, self._format)
1146
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1147
except errors.FileExists:
1149
return self.transport.clone('branch')
1151
def get_repository_transport(self, repository_format):
1152
"""See BzrDir.get_repository_transport()."""
1153
if repository_format is None:
1154
return self.transport.clone('repository')
1156
repository_format.get_format_string()
1157
except NotImplementedError:
1158
raise errors.IncompatibleFormat(repository_format, self._format)
1160
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1161
except errors.FileExists:
1163
return self.transport.clone('repository')
1165
def get_workingtree_transport(self, workingtree_format):
1166
"""See BzrDir.get_workingtree_transport()."""
1167
if workingtree_format is None:
1168
return self.transport.clone('checkout')
1170
workingtree_format.get_format_string()
1171
except NotImplementedError:
1172
raise errors.IncompatibleFormat(workingtree_format, self._format)
1174
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1175
except errors.FileExists:
1177
return self.transport.clone('checkout')
1179
def needs_format_conversion(self, format=None):
1180
"""See BzrDir.needs_format_conversion()."""
1182
format = BzrDirFormat.get_default_format()
1183
if not isinstance(self._format, format.__class__):
1184
# it is not a meta dir format, conversion is needed.
1186
# we might want to push this down to the repository?
1188
if not isinstance(self.open_repository()._format,
1189
format.repository_format.__class__):
1190
# the repository needs an upgrade.
1192
except errors.NoRepositoryPresent:
1195
if not isinstance(self.open_branch()._format,
1196
format.get_branch_format().__class__):
1197
# the branch needs an upgrade.
1199
except errors.NotBranchError:
1202
my_wt = self.open_workingtree(recommend_upgrade=False)
1203
if not isinstance(my_wt._format,
1204
format.workingtree_format.__class__):
1205
# the workingtree needs an upgrade.
1207
except (errors.NoWorkingTree, errors.NotLocalUrl):
1211
def open_branch(self, unsupported=False):
1212
"""See BzrDir.open_branch."""
1213
format = self.find_branch_format()
1214
self._check_supported(format, unsupported)
1215
return format.open(self, _found=True)
1217
def open_repository(self, unsupported=False):
1218
"""See BzrDir.open_repository."""
1219
from bzrlib.repository import RepositoryFormat
1220
format = RepositoryFormat.find_format(self)
1221
self._check_supported(format, unsupported)
1222
return format.open(self, _found=True)
1224
def open_workingtree(self, unsupported=False,
1225
recommend_upgrade=True):
1226
"""See BzrDir.open_workingtree."""
1227
from bzrlib.workingtree import WorkingTreeFormat
1228
format = WorkingTreeFormat.find_format(self)
1229
self._check_supported(format, unsupported,
1231
basedir=self.root_transport.base)
1232
return format.open(self, _found=True)
1235
class BzrDirFormat(object):
1236
"""An encapsulation of the initialization and open routines for a format.
1238
Formats provide three things:
1239
* An initialization routine,
1243
Formats are placed in an dict by their format string for reference
1244
during bzrdir opening. These should be subclasses of BzrDirFormat
1247
Once a format is deprecated, just deprecate the initialize and open
1248
methods on the format class. Do not deprecate the object, as the
1249
object will be created every system load.
1252
_default_format = None
1253
"""The default format used for new .bzr dirs."""
1256
"""The known formats."""
1258
_control_formats = []
1259
"""The registered control formats - .bzr, ....
1261
This is a list of BzrDirFormat objects.
1264
_control_server_formats = []
1265
"""The registered control server formats, e.g. RemoteBzrDirs.
1267
This is a list of BzrDirFormat objects.
1270
_lock_file_name = 'branch-lock'
1272
# _lock_class must be set in subclasses to the lock type, typ.
1273
# TransportLock or LockDir
1276
def find_format(klass, transport, _server_formats=True):
1277
"""Return the format present at transport."""
1279
formats = klass._control_server_formats + klass._control_formats
1281
formats = klass._control_formats
1282
for format in formats:
1284
return format.probe_transport(transport)
1285
except errors.NotBranchError:
1286
# this format does not find a control dir here.
1288
raise errors.NotBranchError(path=transport.base)
1291
def probe_transport(klass, transport):
1292
"""Return the .bzrdir style format present in a directory."""
1294
format_string = transport.get(".bzr/branch-format").read()
1295
except errors.NoSuchFile:
1296
raise errors.NotBranchError(path=transport.base)
1299
return klass._formats[format_string]
1301
raise errors.UnknownFormatError(format=format_string)
1304
def get_default_format(klass):
1305
"""Return the current default format."""
1306
return klass._default_format
1308
def get_format_string(self):
1309
"""Return the ASCII format string that identifies this format."""
1310
raise NotImplementedError(self.get_format_string)
1312
def get_format_description(self):
1313
"""Return the short description for this format."""
1314
raise NotImplementedError(self.get_format_description)
1316
def get_converter(self, format=None):
1317
"""Return the converter to use to convert bzrdirs needing converts.
1319
This returns a bzrlib.bzrdir.Converter object.
1321
This should return the best upgrader to step this format towards the
1322
current default format. In the case of plugins we can/should provide
1323
some means for them to extend the range of returnable converters.
1325
:param format: Optional format to override the default format of the
1328
raise NotImplementedError(self.get_converter)
1330
def initialize(self, url, possible_transports=None):
1331
"""Create a bzr control dir at this url and return an opened copy.
1333
Subclasses should typically override initialize_on_transport
1334
instead of this method.
1336
return self.initialize_on_transport(get_transport(url,
1337
possible_transports))
1339
def initialize_on_transport(self, transport):
1340
"""Initialize a new bzrdir in the base directory of a Transport."""
1341
# Since we don't have a .bzr directory, inherit the
1342
# mode from the root directory
1343
temp_control = lockable_files.LockableFiles(transport,
1344
'', lockable_files.TransportLock)
1345
temp_control._transport.mkdir('.bzr',
1346
# FIXME: RBC 20060121 don't peek under
1348
mode=temp_control._dir_mode)
1349
file_mode = temp_control._file_mode
1351
mutter('created control directory in ' + transport.base)
1352
control = transport.clone('.bzr')
1353
utf8_files = [('README',
1354
"This is a Bazaar-NG control directory.\n"
1355
"Do not change any files in this directory.\n"),
1356
('branch-format', self.get_format_string()),
1358
# NB: no need to escape relative paths that are url safe.
1359
control_files = lockable_files.LockableFiles(control,
1360
self._lock_file_name, self._lock_class)
1361
control_files.create_lock()
1362
control_files.lock_write()
1364
for file, content in utf8_files:
1365
control_files.put_utf8(file, content)
1367
control_files.unlock()
1368
return self.open(transport, _found=True)
1370
def is_supported(self):
1371
"""Is this format supported?
1373
Supported formats must be initializable and openable.
1374
Unsupported formats may not support initialization or committing or
1375
some other features depending on the reason for not being supported.
1379
def same_model(self, target_format):
1380
return (self.repository_format.rich_root_data ==
1381
target_format.rich_root_data)
1384
def known_formats(klass):
1385
"""Return all the known formats.
1387
Concrete formats should override _known_formats.
1389
# There is double indirection here to make sure that control
1390
# formats used by more than one dir format will only be probed
1391
# once. This can otherwise be quite expensive for remote connections.
1393
for format in klass._control_formats:
1394
result.update(format._known_formats())
1398
def _known_formats(klass):
1399
"""Return the known format instances for this control format."""
1400
return set(klass._formats.values())
1402
def open(self, transport, _found=False):
1403
"""Return an instance of this format for the dir transport points at.
1405
_found is a private parameter, do not use it.
1408
found_format = BzrDirFormat.find_format(transport)
1409
if not isinstance(found_format, self.__class__):
1410
raise AssertionError("%s was asked to open %s, but it seems to need "
1412
% (self, transport, found_format))
1413
return self._open(transport)
1415
def _open(self, transport):
1416
"""Template method helper for opening BzrDirectories.
1418
This performs the actual open and any additional logic or parameter
1421
raise NotImplementedError(self._open)
1424
def register_format(klass, format):
1425
klass._formats[format.get_format_string()] = format
1428
def register_control_format(klass, format):
1429
"""Register a format that does not use '.bzr' for its control dir.
1431
TODO: This should be pulled up into a 'ControlDirFormat' base class
1432
which BzrDirFormat can inherit from, and renamed to register_format
1433
there. It has been done without that for now for simplicity of
1436
klass._control_formats.append(format)
1439
def register_control_server_format(klass, format):
1440
"""Register a control format for client-server environments.
1442
These formats will be tried before ones registered with
1443
register_control_format. This gives implementations that decide to the
1444
chance to grab it before anything looks at the contents of the format
1447
klass._control_server_formats.append(format)
1450
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1451
def set_default_format(klass, format):
1452
klass._set_default_format(format)
1455
def _set_default_format(klass, format):
1456
"""Set default format (for testing behavior of defaults only)"""
1457
klass._default_format = format
1460
return self.get_format_string()[:-1]
1463
def unregister_format(klass, format):
1464
assert klass._formats[format.get_format_string()] is format
1465
del klass._formats[format.get_format_string()]
1468
def unregister_control_format(klass, format):
1469
klass._control_formats.remove(format)
1472
class BzrDirFormat4(BzrDirFormat):
1473
"""Bzr dir format 4.
1475
This format is a combined format for working tree, branch and repository.
1477
- Format 1 working trees [always]
1478
- Format 4 branches [always]
1479
- Format 4 repositories [always]
1481
This format is deprecated: it indexes texts using a text it which is
1482
removed in format 5; write support for this format has been removed.
1485
_lock_class = lockable_files.TransportLock
1487
def get_format_string(self):
1488
"""See BzrDirFormat.get_format_string()."""
1489
return "Bazaar-NG branch, format 0.0.4\n"
1491
def get_format_description(self):
1492
"""See BzrDirFormat.get_format_description()."""
1493
return "All-in-one format 4"
1495
def get_converter(self, format=None):
1496
"""See BzrDirFormat.get_converter()."""
1497
# there is one and only one upgrade path here.
1498
return ConvertBzrDir4To5()
1500
def initialize_on_transport(self, transport):
1501
"""Format 4 branches cannot be created."""
1502
raise errors.UninitializableFormat(self)
1504
def is_supported(self):
1505
"""Format 4 is not supported.
1507
It is not supported because the model changed from 4 to 5 and the
1508
conversion logic is expensive - so doing it on the fly was not
1513
def _open(self, transport):
1514
"""See BzrDirFormat._open."""
1515
return BzrDir4(transport, self)
1517
def __return_repository_format(self):
1518
"""Circular import protection."""
1519
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1520
return RepositoryFormat4()
1521
repository_format = property(__return_repository_format)
1524
class BzrDirFormat5(BzrDirFormat):
1525
"""Bzr control format 5.
1527
This format is a combined format for working tree, branch and repository.
1529
- Format 2 working trees [always]
1530
- Format 4 branches [always]
1531
- Format 5 repositories [always]
1532
Unhashed stores in the repository.
1535
_lock_class = lockable_files.TransportLock
1537
def get_format_string(self):
1538
"""See BzrDirFormat.get_format_string()."""
1539
return "Bazaar-NG branch, format 5\n"
1541
def get_format_description(self):
1542
"""See BzrDirFormat.get_format_description()."""
1543
return "All-in-one format 5"
1545
def get_converter(self, format=None):
1546
"""See BzrDirFormat.get_converter()."""
1547
# there is one and only one upgrade path here.
1548
return ConvertBzrDir5To6()
1550
def _initialize_for_clone(self, url):
1551
return self.initialize_on_transport(get_transport(url), _cloning=True)
1553
def initialize_on_transport(self, transport, _cloning=False):
1554
"""Format 5 dirs always have working tree, branch and repository.
1556
Except when they are being cloned.
1558
from bzrlib.branch import BzrBranchFormat4
1559
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1560
from bzrlib.workingtree import WorkingTreeFormat2
1561
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1562
RepositoryFormat5().initialize(result, _internal=True)
1564
branch = BzrBranchFormat4().initialize(result)
1566
WorkingTreeFormat2().initialize(result)
1567
except errors.NotLocalUrl:
1568
# Even though we can't access the working tree, we need to
1569
# create its control files.
1570
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1573
def _open(self, transport):
1574
"""See BzrDirFormat._open."""
1575
return BzrDir5(transport, self)
1577
def __return_repository_format(self):
1578
"""Circular import protection."""
1579
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1580
return RepositoryFormat5()
1581
repository_format = property(__return_repository_format)
1584
class BzrDirFormat6(BzrDirFormat):
1585
"""Bzr control format 6.
1587
This format is a combined format for working tree, branch and repository.
1589
- Format 2 working trees [always]
1590
- Format 4 branches [always]
1591
- Format 6 repositories [always]
1594
_lock_class = lockable_files.TransportLock
1596
def get_format_string(self):
1597
"""See BzrDirFormat.get_format_string()."""
1598
return "Bazaar-NG branch, format 6\n"
1600
def get_format_description(self):
1601
"""See BzrDirFormat.get_format_description()."""
1602
return "All-in-one format 6"
1604
def get_converter(self, format=None):
1605
"""See BzrDirFormat.get_converter()."""
1606
# there is one and only one upgrade path here.
1607
return ConvertBzrDir6ToMeta()
1609
def _initialize_for_clone(self, url):
1610
return self.initialize_on_transport(get_transport(url), _cloning=True)
1612
def initialize_on_transport(self, transport, _cloning=False):
1613
"""Format 6 dirs always have working tree, branch and repository.
1615
Except when they are being cloned.
1617
from bzrlib.branch import BzrBranchFormat4
1618
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1619
from bzrlib.workingtree import WorkingTreeFormat2
1620
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1621
RepositoryFormat6().initialize(result, _internal=True)
1623
branch = BzrBranchFormat4().initialize(result)
1625
WorkingTreeFormat2().initialize(result)
1626
except errors.NotLocalUrl:
1627
# Even though we can't access the working tree, we need to
1628
# create its control files.
1629
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1632
def _open(self, transport):
1633
"""See BzrDirFormat._open."""
1634
return BzrDir6(transport, self)
1636
def __return_repository_format(self):
1637
"""Circular import protection."""
1638
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1639
return RepositoryFormat6()
1640
repository_format = property(__return_repository_format)
1643
class BzrDirMetaFormat1(BzrDirFormat):
1644
"""Bzr meta control format 1
1646
This is the first format with split out working tree, branch and repository
1649
- Format 3 working trees [optional]
1650
- Format 5 branches [optional]
1651
- Format 7 repositories [optional]
1654
_lock_class = lockdir.LockDir
1657
self._workingtree_format = None
1658
self._branch_format = None
1660
def __eq__(self, other):
1661
if other.__class__ is not self.__class__:
1663
if other.repository_format != self.repository_format:
1665
if other.workingtree_format != self.workingtree_format:
1669
def __ne__(self, other):
1670
return not self == other
1672
def get_branch_format(self):
1673
if self._branch_format is None:
1674
from bzrlib.branch import BranchFormat
1675
self._branch_format = BranchFormat.get_default_format()
1676
return self._branch_format
1678
def set_branch_format(self, format):
1679
self._branch_format = format
1681
def get_converter(self, format=None):
1682
"""See BzrDirFormat.get_converter()."""
1684
format = BzrDirFormat.get_default_format()
1685
if not isinstance(self, format.__class__):
1686
# converting away from metadir is not implemented
1687
raise NotImplementedError(self.get_converter)
1688
return ConvertMetaToMeta(format)
1690
def get_format_string(self):
1691
"""See BzrDirFormat.get_format_string()."""
1692
return "Bazaar-NG meta directory, format 1\n"
1694
def get_format_description(self):
1695
"""See BzrDirFormat.get_format_description()."""
1696
return "Meta directory format 1"
1698
def _open(self, transport):
1699
"""See BzrDirFormat._open."""
1700
return BzrDirMeta1(transport, self)
1702
def __return_repository_format(self):
1703
"""Circular import protection."""
1704
if getattr(self, '_repository_format', None):
1705
return self._repository_format
1706
from bzrlib.repository import RepositoryFormat
1707
return RepositoryFormat.get_default_format()
1709
def __set_repository_format(self, value):
1710
"""Allow changint the repository format for metadir formats."""
1711
self._repository_format = value
1713
repository_format = property(__return_repository_format, __set_repository_format)
1715
def __get_workingtree_format(self):
1716
if self._workingtree_format is None:
1717
from bzrlib.workingtree import WorkingTreeFormat
1718
self._workingtree_format = WorkingTreeFormat.get_default_format()
1719
return self._workingtree_format
1721
def __set_workingtree_format(self, wt_format):
1722
self._workingtree_format = wt_format
1724
workingtree_format = property(__get_workingtree_format,
1725
__set_workingtree_format)
1728
# Register bzr control format
1729
BzrDirFormat.register_control_format(BzrDirFormat)
1731
# Register bzr formats
1732
BzrDirFormat.register_format(BzrDirFormat4())
1733
BzrDirFormat.register_format(BzrDirFormat5())
1734
BzrDirFormat.register_format(BzrDirFormat6())
1735
__default_format = BzrDirMetaFormat1()
1736
BzrDirFormat.register_format(__default_format)
1737
BzrDirFormat._default_format = __default_format
1740
class Converter(object):
1741
"""Converts a disk format object from one format to another."""
1743
def convert(self, to_convert, pb):
1744
"""Perform the conversion of to_convert, giving feedback via pb.
1746
:param to_convert: The disk object to convert.
1747
:param pb: a progress bar to use for progress information.
1750
def step(self, message):
1751
"""Update the pb by a step."""
1753
self.pb.update(message, self.count, self.total)
1756
class ConvertBzrDir4To5(Converter):
1757
"""Converts format 4 bzr dirs to format 5."""
1760
super(ConvertBzrDir4To5, self).__init__()
1761
self.converted_revs = set()
1762
self.absent_revisions = set()
1766
def convert(self, to_convert, pb):
1767
"""See Converter.convert()."""
1768
self.bzrdir = to_convert
1770
self.pb.note('starting upgrade from format 4 to 5')
1771
if isinstance(self.bzrdir.transport, LocalTransport):
1772
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1773
self._convert_to_weaves()
1774
return BzrDir.open(self.bzrdir.root_transport.base)
1776
def _convert_to_weaves(self):
1777
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1780
stat = self.bzrdir.transport.stat('weaves')
1781
if not S_ISDIR(stat.st_mode):
1782
self.bzrdir.transport.delete('weaves')
1783
self.bzrdir.transport.mkdir('weaves')
1784
except errors.NoSuchFile:
1785
self.bzrdir.transport.mkdir('weaves')
1786
# deliberately not a WeaveFile as we want to build it up slowly.
1787
self.inv_weave = Weave('inventory')
1788
# holds in-memory weaves for all files
1789
self.text_weaves = {}
1790
self.bzrdir.transport.delete('branch-format')
1791
self.branch = self.bzrdir.open_branch()
1792
self._convert_working_inv()
1793
rev_history = self.branch.revision_history()
1794
# to_read is a stack holding the revisions we still need to process;
1795
# appending to it adds new highest-priority revisions
1796
self.known_revisions = set(rev_history)
1797
self.to_read = rev_history[-1:]
1799
rev_id = self.to_read.pop()
1800
if (rev_id not in self.revisions
1801
and rev_id not in self.absent_revisions):
1802
self._load_one_rev(rev_id)
1804
to_import = self._make_order()
1805
for i, rev_id in enumerate(to_import):
1806
self.pb.update('converting revision', i, len(to_import))
1807
self._convert_one_rev(rev_id)
1809
self._write_all_weaves()
1810
self._write_all_revs()
1811
self.pb.note('upgraded to weaves:')
1812
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1813
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1814
self.pb.note(' %6d texts', self.text_count)
1815
self._cleanup_spare_files_after_format4()
1816
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1818
def _cleanup_spare_files_after_format4(self):
1819
# FIXME working tree upgrade foo.
1820
for n in 'merged-patches', 'pending-merged-patches':
1822
## assert os.path.getsize(p) == 0
1823
self.bzrdir.transport.delete(n)
1824
except errors.NoSuchFile:
1826
self.bzrdir.transport.delete_tree('inventory-store')
1827
self.bzrdir.transport.delete_tree('text-store')
1829
def _convert_working_inv(self):
1830
inv = xml4.serializer_v4.read_inventory(
1831
self.branch.control_files.get('inventory'))
1832
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1833
# FIXME inventory is a working tree change.
1834
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1836
def _write_all_weaves(self):
1837
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1838
weave_transport = self.bzrdir.transport.clone('weaves')
1839
weaves = WeaveStore(weave_transport, prefixed=False)
1840
transaction = WriteTransaction()
1844
for file_id, file_weave in self.text_weaves.items():
1845
self.pb.update('writing weave', i, len(self.text_weaves))
1846
weaves._put_weave(file_id, file_weave, transaction)
1848
self.pb.update('inventory', 0, 1)
1849
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1850
self.pb.update('inventory', 1, 1)
1854
def _write_all_revs(self):
1855
"""Write all revisions out in new form."""
1856
self.bzrdir.transport.delete_tree('revision-store')
1857
self.bzrdir.transport.mkdir('revision-store')
1858
revision_transport = self.bzrdir.transport.clone('revision-store')
1860
_revision_store = TextRevisionStore(TextStore(revision_transport,
1864
transaction = WriteTransaction()
1865
for i, rev_id in enumerate(self.converted_revs):
1866
self.pb.update('write revision', i, len(self.converted_revs))
1867
_revision_store.add_revision(self.revisions[rev_id], transaction)
1871
def _load_one_rev(self, rev_id):
1872
"""Load a revision object into memory.
1874
Any parents not either loaded or abandoned get queued to be
1876
self.pb.update('loading revision',
1877
len(self.revisions),
1878
len(self.known_revisions))
1879
if not self.branch.repository.has_revision(rev_id):
1881
self.pb.note('revision {%s} not present in branch; '
1882
'will be converted as a ghost',
1884
self.absent_revisions.add(rev_id)
1886
rev = self.branch.repository._revision_store.get_revision(rev_id,
1887
self.branch.repository.get_transaction())
1888
for parent_id in rev.parent_ids:
1889
self.known_revisions.add(parent_id)
1890
self.to_read.append(parent_id)
1891
self.revisions[rev_id] = rev
1893
def _load_old_inventory(self, rev_id):
1894
assert rev_id not in self.converted_revs
1895
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1896
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1897
inv.revision_id = rev_id
1898
rev = self.revisions[rev_id]
1899
if rev.inventory_sha1:
1900
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1901
'inventory sha mismatch for {%s}' % rev_id
1904
def _load_updated_inventory(self, rev_id):
1905
assert rev_id in self.converted_revs
1906
inv_xml = self.inv_weave.get_text(rev_id)
1907
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1910
def _convert_one_rev(self, rev_id):
1911
"""Convert revision and all referenced objects to new format."""
1912
rev = self.revisions[rev_id]
1913
inv = self._load_old_inventory(rev_id)
1914
present_parents = [p for p in rev.parent_ids
1915
if p not in self.absent_revisions]
1916
self._convert_revision_contents(rev, inv, present_parents)
1917
self._store_new_inv(rev, inv, present_parents)
1918
self.converted_revs.add(rev_id)
1920
def _store_new_inv(self, rev, inv, present_parents):
1921
# the XML is now updated with text versions
1923
entries = inv.iter_entries()
1925
for path, ie in entries:
1926
assert getattr(ie, 'revision', None) is not None, \
1927
'no revision on {%s} in {%s}' % \
1928
(file_id, rev.revision_id)
1929
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1930
new_inv_sha1 = sha_string(new_inv_xml)
1931
self.inv_weave.add_lines(rev.revision_id,
1933
new_inv_xml.splitlines(True))
1934
rev.inventory_sha1 = new_inv_sha1
1936
def _convert_revision_contents(self, rev, inv, present_parents):
1937
"""Convert all the files within a revision.
1939
Also upgrade the inventory to refer to the text revision ids."""
1940
rev_id = rev.revision_id
1941
mutter('converting texts of revision {%s}',
1943
parent_invs = map(self._load_updated_inventory, present_parents)
1944
entries = inv.iter_entries()
1946
for path, ie in entries:
1947
self._convert_file_version(rev, ie, parent_invs)
1949
def _convert_file_version(self, rev, ie, parent_invs):
1950
"""Convert one version of one file.
1952
The file needs to be added into the weave if it is a merge
1953
of >=2 parents or if it's changed from its parent.
1955
file_id = ie.file_id
1956
rev_id = rev.revision_id
1957
w = self.text_weaves.get(file_id)
1960
self.text_weaves[file_id] = w
1961
text_changed = False
1962
parent_candiate_entries = ie.parent_candidates(parent_invs)
1963
for old_revision in parent_candiate_entries.keys():
1964
# if this fails, its a ghost ?
1965
assert old_revision in self.converted_revs, \
1966
"Revision {%s} not in converted_revs" % old_revision
1967
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1968
# XXX: Note that this is unordered - and this is tolerable because
1969
# the previous code was also unordered.
1970
previous_entries = dict((head, parent_candiate_entries[head]) for head
1972
self.snapshot_ie(previous_entries, ie, w, rev_id)
1974
assert getattr(ie, 'revision', None) is not None
1976
def get_parents(self, revision_ids):
1977
for revision_id in revision_ids:
1978
yield self.revisions[revision_id].parent_ids
1980
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1981
# TODO: convert this logic, which is ~= snapshot to
1982
# a call to:. This needs the path figured out. rather than a work_tree
1983
# a v4 revision_tree can be given, or something that looks enough like
1984
# one to give the file content to the entry if it needs it.
1985
# and we need something that looks like a weave store for snapshot to
1987
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1988
if len(previous_revisions) == 1:
1989
previous_ie = previous_revisions.values()[0]
1990
if ie._unchanged(previous_ie):
1991
ie.revision = previous_ie.revision
1994
text = self.branch.repository.weave_store.get(ie.text_id)
1995
file_lines = text.readlines()
1996
assert sha_strings(file_lines) == ie.text_sha1
1997
assert sum(map(len, file_lines)) == ie.text_size
1998
w.add_lines(rev_id, previous_revisions, file_lines)
1999
self.text_count += 1
2001
w.add_lines(rev_id, previous_revisions, [])
2002
ie.revision = rev_id
2004
def _make_order(self):
2005
"""Return a suitable order for importing revisions.
2007
The order must be such that an revision is imported after all
2008
its (present) parents.
2010
todo = set(self.revisions.keys())
2011
done = self.absent_revisions.copy()
2014
# scan through looking for a revision whose parents
2016
for rev_id in sorted(list(todo)):
2017
rev = self.revisions[rev_id]
2018
parent_ids = set(rev.parent_ids)
2019
if parent_ids.issubset(done):
2020
# can take this one now
2021
order.append(rev_id)
2027
class ConvertBzrDir5To6(Converter):
2028
"""Converts format 5 bzr dirs to format 6."""
2030
def convert(self, to_convert, pb):
2031
"""See Converter.convert()."""
2032
self.bzrdir = to_convert
2034
self.pb.note('starting upgrade from format 5 to 6')
2035
self._convert_to_prefixed()
2036
return BzrDir.open(self.bzrdir.root_transport.base)
2038
def _convert_to_prefixed(self):
2039
from bzrlib.store import TransportStore
2040
self.bzrdir.transport.delete('branch-format')
2041
for store_name in ["weaves", "revision-store"]:
2042
self.pb.note("adding prefixes to %s" % store_name)
2043
store_transport = self.bzrdir.transport.clone(store_name)
2044
store = TransportStore(store_transport, prefixed=True)
2045
for urlfilename in store_transport.list_dir('.'):
2046
filename = urlutils.unescape(urlfilename)
2047
if (filename.endswith(".weave") or
2048
filename.endswith(".gz") or
2049
filename.endswith(".sig")):
2050
file_id = os.path.splitext(filename)[0]
2053
prefix_dir = store.hash_prefix(file_id)
2054
# FIXME keep track of the dirs made RBC 20060121
2056
store_transport.move(filename, prefix_dir + '/' + filename)
2057
except errors.NoSuchFile: # catches missing dirs strangely enough
2058
store_transport.mkdir(prefix_dir)
2059
store_transport.move(filename, prefix_dir + '/' + filename)
2060
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2063
class ConvertBzrDir6ToMeta(Converter):
2064
"""Converts format 6 bzr dirs to metadirs."""
2066
def convert(self, to_convert, pb):
2067
"""See Converter.convert()."""
2068
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2069
from bzrlib.branch import BzrBranchFormat5
2070
self.bzrdir = to_convert
2073
self.total = 20 # the steps we know about
2074
self.garbage_inventories = []
2076
self.pb.note('starting upgrade from format 6 to metadir')
2077
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2078
# its faster to move specific files around than to open and use the apis...
2079
# first off, nuke ancestry.weave, it was never used.
2081
self.step('Removing ancestry.weave')
2082
self.bzrdir.transport.delete('ancestry.weave')
2083
except errors.NoSuchFile:
2085
# find out whats there
2086
self.step('Finding branch files')
2087
last_revision = self.bzrdir.open_branch().last_revision()
2088
bzrcontents = self.bzrdir.transport.list_dir('.')
2089
for name in bzrcontents:
2090
if name.startswith('basis-inventory.'):
2091
self.garbage_inventories.append(name)
2092
# create new directories for repository, working tree and branch
2093
self.dir_mode = self.bzrdir._control_files._dir_mode
2094
self.file_mode = self.bzrdir._control_files._file_mode
2095
repository_names = [('inventory.weave', True),
2096
('revision-store', True),
2098
self.step('Upgrading repository ')
2099
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2100
self.make_lock('repository')
2101
# we hard code the formats here because we are converting into
2102
# the meta format. The meta format upgrader can take this to a
2103
# future format within each component.
2104
self.put_format('repository', RepositoryFormat7())
2105
for entry in repository_names:
2106
self.move_entry('repository', entry)
2108
self.step('Upgrading branch ')
2109
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2110
self.make_lock('branch')
2111
self.put_format('branch', BzrBranchFormat5())
2112
branch_files = [('revision-history', True),
2113
('branch-name', True),
2115
for entry in branch_files:
2116
self.move_entry('branch', entry)
2118
checkout_files = [('pending-merges', True),
2119
('inventory', True),
2120
('stat-cache', False)]
2121
# If a mandatory checkout file is not present, the branch does not have
2122
# a functional checkout. Do not create a checkout in the converted
2124
for name, mandatory in checkout_files:
2125
if mandatory and name not in bzrcontents:
2126
has_checkout = False
2130
if not has_checkout:
2131
self.pb.note('No working tree.')
2132
# If some checkout files are there, we may as well get rid of them.
2133
for name, mandatory in checkout_files:
2134
if name in bzrcontents:
2135
self.bzrdir.transport.delete(name)
2137
from bzrlib.workingtree import WorkingTreeFormat3
2138
self.step('Upgrading working tree')
2139
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2140
self.make_lock('checkout')
2142
'checkout', WorkingTreeFormat3())
2143
self.bzrdir.transport.delete_multi(
2144
self.garbage_inventories, self.pb)
2145
for entry in checkout_files:
2146
self.move_entry('checkout', entry)
2147
if last_revision is not None:
2148
self.bzrdir._control_files.put_utf8(
2149
'checkout/last-revision', last_revision)
2150
self.bzrdir._control_files.put_utf8(
2151
'branch-format', BzrDirMetaFormat1().get_format_string())
2152
return BzrDir.open(self.bzrdir.root_transport.base)
2154
def make_lock(self, name):
2155
"""Make a lock for the new control dir name."""
2156
self.step('Make %s lock' % name)
2157
ld = lockdir.LockDir(self.bzrdir.transport,
2159
file_modebits=self.file_mode,
2160
dir_modebits=self.dir_mode)
2163
def move_entry(self, new_dir, entry):
2164
"""Move then entry name into new_dir."""
2166
mandatory = entry[1]
2167
self.step('Moving %s' % name)
2169
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2170
except errors.NoSuchFile:
2174
def put_format(self, dirname, format):
2175
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2178
class ConvertMetaToMeta(Converter):
2179
"""Converts the components of metadirs."""
2181
def __init__(self, target_format):
2182
"""Create a metadir to metadir converter.
2184
:param target_format: The final metadir format that is desired.
2186
self.target_format = target_format
2188
def convert(self, to_convert, pb):
2189
"""See Converter.convert()."""
2190
self.bzrdir = to_convert
2194
self.step('checking repository format')
2196
repo = self.bzrdir.open_repository()
2197
except errors.NoRepositoryPresent:
2200
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2201
from bzrlib.repository import CopyConverter
2202
self.pb.note('starting repository conversion')
2203
converter = CopyConverter(self.target_format.repository_format)
2204
converter.convert(repo, pb)
2206
branch = self.bzrdir.open_branch()
2207
except errors.NotBranchError:
2210
# TODO: conversions of Branch and Tree should be done by
2211
# InterXFormat lookups
2212
# Avoid circular imports
2213
from bzrlib import branch as _mod_branch
2214
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2215
self.target_format.get_branch_format().__class__ is
2216
_mod_branch.BzrBranchFormat6):
2217
branch_converter = _mod_branch.Converter5to6()
2218
branch_converter.convert(branch)
2220
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2221
except (errors.NoWorkingTree, errors.NotLocalUrl):
2224
# TODO: conversions of Branch and Tree should be done by
2225
# InterXFormat lookups
2226
if (isinstance(tree, workingtree.WorkingTree3) and
2227
not isinstance(tree, workingtree_4.WorkingTree4) and
2228
isinstance(self.target_format.workingtree_format,
2229
workingtree_4.WorkingTreeFormat4)):
2230
workingtree_4.Converter3to4().convert(tree)
2234
# This is not in remote.py because it's small, and needs to be registered.
2235
# Putting it in remote.py creates a circular import problem.
2236
# we can make it a lazy object if the control formats is turned into something
2238
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2239
"""Format representing bzrdirs accessed via a smart server"""
2241
def get_format_description(self):
2242
return 'bzr remote bzrdir'
2245
def probe_transport(klass, transport):
2246
"""Return a RemoteBzrDirFormat object if it looks possible."""
2248
client = transport.get_smart_client()
2249
except (NotImplementedError, AttributeError,
2250
errors.TransportNotPossible):
2251
# no smart server, so not a branch for this format type.
2252
raise errors.NotBranchError(path=transport.base)
2254
# Send a 'hello' request in protocol version one, and decline to
2255
# open it if the server doesn't support our required version (2) so
2256
# that the VFS-based transport will do it.
2257
request = client.get_request()
2258
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2259
server_version = smart_protocol.query_version()
2260
if server_version != 2:
2261
raise errors.NotBranchError(path=transport.base)
2264
def initialize_on_transport(self, transport):
2266
# hand off the request to the smart server
2267
shared_medium = transport.get_shared_medium()
2268
except errors.NoSmartMedium:
2269
# TODO: lookup the local format from a server hint.
2270
local_dir_format = BzrDirMetaFormat1()
2271
return local_dir_format.initialize_on_transport(transport)
2272
client = _SmartClient(shared_medium)
2273
path = client.remote_path_from_transport(transport)
2274
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2276
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2277
return remote.RemoteBzrDir(transport)
2279
def _open(self, transport):
2280
return remote.RemoteBzrDir(transport)
2282
def __eq__(self, other):
2283
if not isinstance(other, RemoteBzrDirFormat):
2285
return self.get_format_description() == other.get_format_description()
2288
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2291
class BzrDirFormatInfo(object):
2293
def __init__(self, native, deprecated, hidden):
2294
self.deprecated = deprecated
2295
self.native = native
2296
self.hidden = hidden
2299
class BzrDirFormatRegistry(registry.Registry):
2300
"""Registry of user-selectable BzrDir subformats.
2302
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2303
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2306
def register_metadir(self, key,
2307
repository_format, help, native=True, deprecated=False,
2311
"""Register a metadir subformat.
2313
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2314
by the Repository format.
2316
:param repository_format: The fully-qualified repository format class
2318
:param branch_format: Fully-qualified branch format class name as
2320
:param tree_format: Fully-qualified tree format class name as
2323
# This should be expanded to support setting WorkingTree and Branch
2324
# formats, once BzrDirMetaFormat1 supports that.
2325
def _load(full_name):
2326
mod_name, factory_name = full_name.rsplit('.', 1)
2328
mod = __import__(mod_name, globals(), locals(),
2330
except ImportError, e:
2331
raise ImportError('failed to load %s: %s' % (full_name, e))
2333
factory = getattr(mod, factory_name)
2334
except AttributeError:
2335
raise AttributeError('no factory %s in module %r'
2340
bd = BzrDirMetaFormat1()
2341
if branch_format is not None:
2342
bd.set_branch_format(_load(branch_format))
2343
if tree_format is not None:
2344
bd.workingtree_format = _load(tree_format)
2345
if repository_format is not None:
2346
bd.repository_format = _load(repository_format)
2348
self.register(key, helper, help, native, deprecated, hidden)
2350
def register(self, key, factory, help, native=True, deprecated=False,
2352
"""Register a BzrDirFormat factory.
2354
The factory must be a callable that takes one parameter: the key.
2355
It must produce an instance of the BzrDirFormat when called.
2357
This function mainly exists to prevent the info object from being
2360
registry.Registry.register(self, key, factory, help,
2361
BzrDirFormatInfo(native, deprecated, hidden))
2363
def register_lazy(self, key, module_name, member_name, help, native=True,
2364
deprecated=False, hidden=False):
2365
registry.Registry.register_lazy(self, key, module_name, member_name,
2366
help, BzrDirFormatInfo(native, deprecated, hidden))
2368
def set_default(self, key):
2369
"""Set the 'default' key to be a clone of the supplied key.
2371
This method must be called once and only once.
2373
registry.Registry.register(self, 'default', self.get(key),
2374
self.get_help(key), info=self.get_info(key))
2376
def set_default_repository(self, key):
2377
"""Set the FormatRegistry default and Repository default.
2379
This is a transitional method while Repository.set_default_format
2382
if 'default' in self:
2383
self.remove('default')
2384
self.set_default(key)
2385
format = self.get('default')()
2386
assert isinstance(format, BzrDirMetaFormat1)
2388
def make_bzrdir(self, key):
2389
return self.get(key)()
2391
def help_topic(self, topic):
2392
output = textwrap.dedent("""\
2393
These formats can be used for creating branches, working trees, and
2397
default_realkey = None
2398
default_help = self.get_help('default')
2400
for key in self.keys():
2401
if key == 'default':
2403
help = self.get_help(key)
2404
if help == default_help:
2405
default_realkey = key
2407
help_pairs.append((key, help))
2409
def wrapped(key, help, info):
2411
help = '(native) ' + help
2412
return ':%s:\n%s\n\n' % (key,
2413
textwrap.fill(help, initial_indent=' ',
2414
subsequent_indent=' '))
2415
if default_realkey is not None:
2416
output += wrapped(default_realkey, '(default) %s' % default_help,
2417
self.get_info('default'))
2418
deprecated_pairs = []
2419
for key, help in help_pairs:
2420
info = self.get_info(key)
2423
elif info.deprecated:
2424
deprecated_pairs.append((key, help))
2426
output += wrapped(key, help, info)
2427
if len(deprecated_pairs) > 0:
2428
output += "Deprecated formats are shown below.\n\n"
2429
for key, help in deprecated_pairs:
2430
info = self.get_info(key)
2431
output += wrapped(key, help, info)
2436
format_registry = BzrDirFormatRegistry()
2437
format_registry.register('weave', BzrDirFormat6,
2438
'Pre-0.8 format. Slower than knit and does not'
2439
' support checkouts or shared repositories.',
2441
format_registry.register_metadir('knit',
2442
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2443
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2444
branch_format='bzrlib.branch.BzrBranchFormat5',
2445
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2446
format_registry.register_metadir('metaweave',
2447
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2448
'Transitional format in 0.8. Slower than knit.',
2449
branch_format='bzrlib.branch.BzrBranchFormat5',
2450
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2452
format_registry.register_metadir('dirstate',
2453
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2454
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2455
'above when accessed over the network.',
2456
branch_format='bzrlib.branch.BzrBranchFormat5',
2457
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2458
# directly from workingtree_4 triggers a circular import.
2459
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2461
format_registry.register_metadir('dirstate-tags',
2462
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2463
help='New in 0.15: Fast local operations and improved scaling for '
2464
'network operations. Additionally adds support for tags.'
2465
' Incompatible with bzr < 0.15.',
2466
branch_format='bzrlib.branch.BzrBranchFormat6',
2467
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2469
format_registry.register_metadir('dirstate-with-subtree',
2470
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2471
help='New in 0.15: Fast local operations and improved scaling for '
2472
'network operations. Additionally adds support for versioning nested '
2473
'bzr branches. Incompatible with bzr < 0.15.',
2474
branch_format='bzrlib.branch.BzrBranchFormat6',
2475
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2478
format_registry.set_default('dirstate-tags')