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: remove unittest dependency; put that stuff inside the test suite
25
# TODO: Can we move specific formats into separate modules to make this file
28
from cStringIO import StringIO
32
from bzrlib.lazy_import import lazy_import
33
lazy_import(globals(), """
34
from copy import deepcopy
35
from stat import S_ISDIR
45
revision as _mod_revision,
53
from bzrlib.osutils import (
58
from bzrlib.smart.client import SmartClient
59
from bzrlib.store.revision.text import TextRevisionStore
60
from bzrlib.store.text import TextStore
61
from bzrlib.store.versioned import WeaveStore
62
from bzrlib.transactions import WriteTransaction
63
from bzrlib.transport import get_transport
64
from bzrlib.weave import Weave
67
from bzrlib.trace import mutter, note
68
from bzrlib.transport.local import LocalTransport
72
"""A .bzr control diretory.
74
BzrDir instances let you create or open any of the things that can be
75
found within .bzr - checkouts, branches and repositories.
78
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
80
a transport connected to the directory this bzr was opened from.
84
"""Invoke break_lock on the first object in the bzrdir.
86
If there is a tree, the tree is opened and break_lock() called.
87
Otherwise, branch is tried, and finally repository.
89
# XXX: This seems more like a UI function than something that really
90
# belongs in this class.
92
thing_to_unlock = self.open_workingtree()
93
except (errors.NotLocalUrl, errors.NoWorkingTree):
95
thing_to_unlock = self.open_branch()
96
except errors.NotBranchError:
98
thing_to_unlock = self.open_repository()
99
except errors.NoRepositoryPresent:
101
thing_to_unlock.break_lock()
103
def can_convert_format(self):
104
"""Return true if this bzrdir is one whose format we can convert from."""
107
def check_conversion_target(self, target_format):
108
target_repo_format = target_format.repository_format
109
source_repo_format = self._format.repository_format
110
source_repo_format.check_conversion_target(target_repo_format)
113
def _check_supported(format, allow_unsupported):
114
"""Check whether format is a supported format.
116
If allow_unsupported is True, this is a no-op.
118
if not allow_unsupported and not format.is_supported():
119
# see open_downlevel to open legacy branches.
120
raise errors.UnsupportedFormatError(format=format)
122
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
123
"""Clone this bzrdir and its contents to url verbatim.
125
If urls last component does not exist, it will be created.
127
if revision_id is not None, then the clone operation may tune
128
itself to download less data.
129
:param force_new_repo: Do not use a shared repository for the target
130
even if one is available.
133
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
134
result = self._format.initialize(url)
136
local_repo = self.find_repository()
137
except errors.NoRepositoryPresent:
140
# may need to copy content in
142
result_repo = local_repo.clone(
144
revision_id=revision_id,
146
result_repo.set_make_working_trees(local_repo.make_working_trees())
149
result_repo = result.find_repository()
150
# fetch content this dir needs.
152
# XXX FIXME RBC 20060214 need tests for this when the basis
154
result_repo.fetch(basis_repo, revision_id=revision_id)
155
result_repo.fetch(local_repo, revision_id=revision_id)
156
except errors.NoRepositoryPresent:
157
# needed to make one anyway.
158
result_repo = local_repo.clone(
160
revision_id=revision_id,
162
result_repo.set_make_working_trees(local_repo.make_working_trees())
163
# 1 if there is a branch present
164
# make sure its content is available in the target repository
167
self.open_branch().clone(result, revision_id=revision_id)
168
except errors.NotBranchError:
171
self.open_workingtree().clone(result, basis=basis_tree)
172
except (errors.NoWorkingTree, errors.NotLocalUrl):
176
def _get_basis_components(self, basis):
177
"""Retrieve the basis components that are available at basis."""
179
return None, None, None
181
basis_tree = basis.open_workingtree()
182
basis_branch = basis_tree.branch
183
basis_repo = basis_branch.repository
184
except (errors.NoWorkingTree, errors.NotLocalUrl):
187
basis_branch = basis.open_branch()
188
basis_repo = basis_branch.repository
189
except errors.NotBranchError:
192
basis_repo = basis.open_repository()
193
except errors.NoRepositoryPresent:
195
return basis_repo, basis_branch, basis_tree
197
# TODO: This should be given a Transport, and should chdir up; otherwise
198
# this will open a new connection.
199
def _make_tail(self, url):
200
head, tail = urlutils.split(url)
201
if tail and tail != '.':
202
t = get_transport(head)
205
except errors.FileExists:
208
# TODO: Should take a Transport
210
def create(cls, base, format=None):
211
"""Create a new BzrDir at the url 'base'.
213
This will call the current default formats initialize with base
214
as the only parameter.
216
:param format: If supplied, the format of branch to create. If not
217
supplied, the default is used.
219
if cls is not BzrDir:
220
raise AssertionError("BzrDir.create always creates the default"
221
" format, not one of %r" % cls)
222
head, tail = urlutils.split(base)
223
if tail and tail != '.':
224
t = get_transport(head)
227
except errors.FileExists:
230
format = BzrDirFormat.get_default_format()
231
return format.initialize(safe_unicode(base))
233
def create_branch(self):
234
"""Create a branch in this BzrDir.
236
The bzrdirs format will control what branch format is created.
237
For more control see BranchFormatXX.create(a_bzrdir).
239
raise NotImplementedError(self.create_branch)
242
def create_branch_and_repo(base, force_new_repo=False, format=None):
243
"""Create a new BzrDir, Branch and Repository at the url 'base'.
245
This will use the current default BzrDirFormat, and use whatever
246
repository format that that uses via bzrdir.create_branch and
247
create_repository. If a shared repository is available that is used
250
The created Branch object is returned.
252
:param base: The URL to create the branch at.
253
:param force_new_repo: If True a new repository is always created.
255
bzrdir = BzrDir.create(base, format)
256
bzrdir._find_or_create_repository(force_new_repo)
257
return bzrdir.create_branch()
259
def _find_or_create_repository(self, force_new_repo):
260
"""Create a new repository if needed, returning the repository."""
262
return self.create_repository()
264
return self.find_repository()
265
except errors.NoRepositoryPresent:
266
return self.create_repository()
269
def create_branch_convenience(base, force_new_repo=False,
270
force_new_tree=None, format=None):
271
"""Create a new BzrDir, Branch and Repository at the url 'base'.
273
This is a convenience function - it will use an existing repository
274
if possible, can be told explicitly whether to create a working tree or
277
This will use the current default BzrDirFormat, and use whatever
278
repository format that that uses via bzrdir.create_branch and
279
create_repository. If a shared repository is available that is used
280
preferentially. Whatever repository is used, its tree creation policy
283
The created Branch object is returned.
284
If a working tree cannot be made due to base not being a file:// url,
285
no error is raised unless force_new_tree is True, in which case no
286
data is created on disk and NotLocalUrl is raised.
288
:param base: The URL to create the branch at.
289
:param force_new_repo: If True a new repository is always created.
290
:param force_new_tree: If True or False force creation of a tree or
291
prevent such creation respectively.
292
:param format: Override for the for the bzrdir format to create
295
# check for non local urls
296
t = get_transport(safe_unicode(base))
297
if not isinstance(t, LocalTransport):
298
raise errors.NotLocalUrl(base)
299
bzrdir = BzrDir.create(base, format)
300
repo = bzrdir._find_or_create_repository(force_new_repo)
301
result = bzrdir.create_branch()
302
if force_new_tree or (repo.make_working_trees() and
303
force_new_tree is None):
305
bzrdir.create_workingtree()
306
except errors.NotLocalUrl:
311
def create_repository(base, shared=False, format=None):
312
"""Create a new BzrDir and Repository at the url 'base'.
314
If no format is supplied, this will default to the current default
315
BzrDirFormat by default, and use whatever repository format that that
316
uses for bzrdirformat.create_repository.
318
:param shared: Create a shared repository rather than a standalone
320
The Repository object is returned.
322
This must be overridden as an instance method in child classes, where
323
it should take no parameters and construct whatever repository format
324
that child class desires.
326
bzrdir = BzrDir.create(base, format)
327
return bzrdir.create_repository(shared)
330
def create_standalone_workingtree(base, format=None):
331
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
333
'base' must be a local path or a file:// url.
335
This will use the current default BzrDirFormat, and use whatever
336
repository format that that uses for bzrdirformat.create_workingtree,
337
create_branch and create_repository.
339
:return: The WorkingTree object.
341
t = get_transport(safe_unicode(base))
342
if not isinstance(t, LocalTransport):
343
raise errors.NotLocalUrl(base)
344
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
346
format=format).bzrdir
347
return bzrdir.create_workingtree()
349
def create_workingtree(self, revision_id=None):
350
"""Create a working tree at this BzrDir.
352
revision_id: create it as of this revision id.
354
raise NotImplementedError(self.create_workingtree)
356
def retire_bzrdir(self):
357
"""Permanently disable the bzrdir.
359
This is done by renaming it to give the user some ability to recover
360
if there was a problem.
362
This will have horrible consequences if anyone has anything locked or
365
for i in xrange(10000):
367
to_path = '.bzr.retired.%d' % i
368
self.root_transport.rename('.bzr', to_path)
369
note("renamed %s to %s"
370
% (self.root_transport.abspath('.bzr'), to_path))
372
except (errors.TransportError, IOError, errors.PathError):
375
def destroy_workingtree(self):
376
"""Destroy the working tree at this BzrDir.
378
Formats that do not support this may raise UnsupportedOperation.
380
raise NotImplementedError(self.destroy_workingtree)
382
def destroy_workingtree_metadata(self):
383
"""Destroy the control files for the working tree at this BzrDir.
385
The contents of working tree files are not affected.
386
Formats that do not support this may raise UnsupportedOperation.
388
raise NotImplementedError(self.destroy_workingtree_metadata)
390
def find_repository(self):
391
"""Find the repository that should be used for a_bzrdir.
393
This does not require a branch as we use it to find the repo for
394
new branches as well as to hook existing branches up to their
398
return self.open_repository()
399
except errors.NoRepositoryPresent:
401
next_transport = self.root_transport.clone('..')
403
# find the next containing bzrdir
405
found_bzrdir = BzrDir.open_containing_from_transport(
407
except errors.NotBranchError:
409
raise errors.NoRepositoryPresent(self)
410
# does it have a repository ?
412
repository = found_bzrdir.open_repository()
413
except errors.NoRepositoryPresent:
414
next_transport = found_bzrdir.root_transport.clone('..')
415
if (found_bzrdir.root_transport.base == next_transport.base):
416
# top of the file system
420
if ((found_bzrdir.root_transport.base ==
421
self.root_transport.base) or repository.is_shared()):
424
raise errors.NoRepositoryPresent(self)
425
raise errors.NoRepositoryPresent(self)
427
def get_branch_reference(self):
428
"""Return the referenced URL for the branch in this bzrdir.
430
:raises NotBranchError: If there is no Branch.
431
:return: The URL the branch in this bzrdir references if it is a
432
reference branch, or None for regular branches.
436
def get_branch_transport(self, branch_format):
437
"""Get the transport for use by branch format in this BzrDir.
439
Note that bzr dirs that do not support format strings will raise
440
IncompatibleFormat if the branch format they are given has
441
a format string, and vice versa.
443
If branch_format is None, the transport is returned with no
444
checking. if it is not None, then the returned transport is
445
guaranteed to point to an existing directory ready for use.
447
raise NotImplementedError(self.get_branch_transport)
449
def get_repository_transport(self, repository_format):
450
"""Get the transport for use by repository format in this BzrDir.
452
Note that bzr dirs that do not support format strings will raise
453
IncompatibleFormat if the repository format they are given has
454
a format string, and vice versa.
456
If repository_format is None, the transport is returned with no
457
checking. if it is not None, then the returned transport is
458
guaranteed to point to an existing directory ready for use.
460
raise NotImplementedError(self.get_repository_transport)
462
def get_workingtree_transport(self, tree_format):
463
"""Get the transport for use by workingtree format in this BzrDir.
465
Note that bzr dirs that do not support format strings will raise
466
IncompatibleFormat if the workingtree format they are given has a
467
format string, and vice versa.
469
If workingtree_format is None, the transport is returned with no
470
checking. if it is not None, then the returned transport is
471
guaranteed to point to an existing directory ready for use.
473
raise NotImplementedError(self.get_workingtree_transport)
475
def __init__(self, _transport, _format):
476
"""Initialize a Bzr control dir object.
478
Only really common logic should reside here, concrete classes should be
479
made with varying behaviours.
481
:param _format: the format that is creating this BzrDir instance.
482
:param _transport: the transport this dir is based at.
484
self._format = _format
485
self.transport = _transport.clone('.bzr')
486
self.root_transport = _transport
488
def is_control_filename(self, filename):
489
"""True if filename is the name of a path which is reserved for bzrdir's.
491
:param filename: A filename within the root transport of this bzrdir.
493
This is true IF and ONLY IF the filename is part of the namespace reserved
494
for bzr control dirs. Currently this is the '.bzr' directory in the root
495
of the root_transport. it is expected that plugins will need to extend
496
this in the future - for instance to make bzr talk with svn working
499
# this might be better on the BzrDirFormat class because it refers to
500
# all the possible bzrdir disk formats.
501
# This method is tested via the workingtree is_control_filename tests-
502
# it was extracted from WorkingTree.is_control_filename. If the methods
503
# contract is extended beyond the current trivial implementation please
504
# add new tests for it to the appropriate place.
505
return filename == '.bzr' or filename.startswith('.bzr/')
507
def needs_format_conversion(self, format=None):
508
"""Return true if this bzrdir needs convert_format run on it.
510
For instance, if the repository format is out of date but the
511
branch and working tree are not, this should return True.
513
:param format: Optional parameter indicating a specific desired
514
format we plan to arrive at.
516
raise NotImplementedError(self.needs_format_conversion)
519
def open_unsupported(base):
520
"""Open a branch which is not supported."""
521
return BzrDir.open(base, _unsupported=True)
524
def open(base, _unsupported=False):
525
"""Open an existing bzrdir, rooted at 'base' (url)
527
_unsupported is a private parameter to the BzrDir class.
529
t = get_transport(base)
530
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
533
def open_from_transport(transport, _unsupported=False):
534
"""Open a bzrdir within a particular directory.
536
:param transport: Transport containing the bzrdir.
537
:param _unsupported: private.
539
format = BzrDirFormat.find_format(transport)
540
BzrDir._check_supported(format, _unsupported)
541
return format.open(transport, _found=True)
543
def open_branch(self, unsupported=False):
544
"""Open the branch object at this BzrDir if one is present.
546
If unsupported is True, then no longer supported branch formats can
549
TODO: static convenience version of this?
551
raise NotImplementedError(self.open_branch)
554
def open_containing(url):
555
"""Open an existing branch which contains url.
557
:param url: url to search from.
558
See open_containing_from_transport for more detail.
560
return BzrDir.open_containing_from_transport(get_transport(url))
563
def open_containing_from_transport(a_transport):
564
"""Open an existing branch which contains a_transport.base
566
This probes for a branch at a_transport, and searches upwards from there.
568
Basically we keep looking up until we find the control directory or
569
run into the root. If there isn't one, raises NotBranchError.
570
If there is one and it is either an unrecognised format or an unsupported
571
format, UnknownFormatError or UnsupportedFormatError are raised.
572
If there is one, it is returned, along with the unused portion of url.
574
:return: The BzrDir that contains the path, and a Unicode path
575
for the rest of the URL.
577
# this gets the normalised url back. I.e. '.' -> the full path.
578
url = a_transport.base
581
result = BzrDir.open_from_transport(a_transport)
582
return result, urlutils.unescape(a_transport.relpath(url))
583
except errors.NotBranchError, e:
586
new_t = a_transport.clone('..')
587
except errors.InvalidURLJoin:
588
# reached the root, whatever that may be
589
raise errors.NotBranchError(path=url)
590
if new_t.base == a_transport.base:
591
# reached the root, whatever that may be
592
raise errors.NotBranchError(path=url)
596
def open_containing_tree_or_branch(klass, location):
597
"""Return the branch and working tree contained by a location.
599
Returns (tree, branch, relpath).
600
If there is no tree at containing the location, tree will be None.
601
If there is no branch containing the location, an exception will be
603
relpath is the portion of the path that is contained by the branch.
605
bzrdir, relpath = klass.open_containing(location)
607
tree = bzrdir.open_workingtree()
608
except (errors.NoWorkingTree, errors.NotLocalUrl):
610
branch = bzrdir.open_branch()
613
return tree, branch, relpath
615
def open_repository(self, _unsupported=False):
616
"""Open the repository object at this BzrDir if one is present.
618
This will not follow the Branch object pointer - its strictly a direct
619
open facility. Most client code should use open_branch().repository to
622
_unsupported is a private parameter, not part of the api.
623
TODO: static convenience version of this?
625
raise NotImplementedError(self.open_repository)
627
def open_workingtree(self, _unsupported=False):
628
"""Open the workingtree object at this BzrDir if one is present.
630
TODO: static convenience version of this?
632
raise NotImplementedError(self.open_workingtree)
634
def has_branch(self):
635
"""Tell if this bzrdir contains a branch.
637
Note: if you're going to open the branch, you should just go ahead
638
and try, and not ask permission first. (This method just opens the
639
branch and discards it, and that's somewhat expensive.)
644
except errors.NotBranchError:
647
def has_workingtree(self):
648
"""Tell if this bzrdir contains a working tree.
650
This will still raise an exception if the bzrdir has a workingtree that
651
is remote & inaccessible.
653
Note: if you're going to open the working tree, you should just go ahead
654
and try, and not ask permission first. (This method just opens the
655
workingtree and discards it, and that's somewhat expensive.)
658
self.open_workingtree()
660
except errors.NoWorkingTree:
663
def _cloning_metadir(self):
664
"""Produce a metadir suitable for cloning with"""
665
def related_repository(bzrdir):
667
branch = bzrdir.open_branch()
668
return branch.repository
669
except errors.NotBranchError:
671
return bzrdir.open_repository()
672
result_format = self._format.__class__()
674
source_repository = related_repository(self)
675
except errors.NoRepositoryPresent:
676
source_repository = None
678
repo_format = source_repository._format
679
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
680
result_format.repository_format = repo_format
682
tree = self.open_workingtree()
683
except (errors.NoWorkingTree, errors.NotLocalUrl):
684
result_format.workingtree_format = None
686
result_format.workingtree_format = tree._format.__class__()
687
return result_format, source_repository
689
def cloning_metadir(self):
690
"""Produce a metadir suitable for cloning or sprouting with.
692
These operations may produce workingtrees (yes, even though they're
693
"cloning" something that doesn't have a tree, so a viable workingtree
694
format must be selected.
696
format, repository = self._cloning_metadir()
697
if format._workingtree_format is None:
698
if repository is None:
700
tree_format = repository._format._matchingbzrdir.workingtree_format
701
format.workingtree_format = tree_format.__class__()
704
def checkout_metadir(self):
705
return self.cloning_metadir()
707
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
709
"""Create a copy of this bzrdir prepared for use as a new line of
712
If urls last component does not exist, it will be created.
714
Attributes related to the identity of the source branch like
715
branch nickname will be cleaned, a working tree is created
716
whether one existed before or not; and a local branch is always
719
if revision_id is not None, then the clone operation may tune
720
itself to download less data.
723
cloning_format = self.cloning_metadir()
724
result = cloning_format.initialize(url)
725
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
727
source_branch = self.open_branch()
728
source_repository = source_branch.repository
729
except errors.NotBranchError:
732
source_repository = self.open_repository()
733
except errors.NoRepositoryPresent:
734
# copy the entire basis one if there is one
735
# but there is no repository.
736
source_repository = basis_repo
741
result_repo = result.find_repository()
742
except errors.NoRepositoryPresent:
744
if source_repository is None and result_repo is not None:
746
elif source_repository is None and result_repo is None:
747
# no repo available, make a new one
748
result.create_repository()
749
elif source_repository is not None and result_repo is None:
750
# have source, and want to make a new target repo
751
# we don't clone the repo because that preserves attributes
752
# like is_shared(), and we have not yet implemented a
753
# repository sprout().
754
result_repo = result.create_repository()
755
if result_repo is not None:
756
# fetch needed content into target.
758
# XXX FIXME RBC 20060214 need tests for this when the basis
760
result_repo.fetch(basis_repo, revision_id=revision_id)
761
if source_repository is not None:
762
result_repo.fetch(source_repository, revision_id=revision_id)
763
if source_branch is not None:
764
source_branch.sprout(result, revision_id=revision_id)
766
result.create_branch()
767
# TODO: jam 20060426 we probably need a test in here in the
768
# case that the newly sprouted branch is a remote one
769
if result_repo is None or result_repo.make_working_trees():
770
wt = result.create_workingtree()
773
if wt.path2id('') is None:
775
wt.set_root_id(self.open_workingtree.get_root_id())
776
except errors.NoWorkingTree:
782
if recurse == 'down':
784
basis = wt.basis_tree()
786
subtrees = basis.iter_references()
787
recurse_branch = wt.branch
788
elif source_branch is not None:
789
basis = source_branch.basis_tree()
791
subtrees = basis.iter_references()
792
recurse_branch = source_branch
797
for path, file_id in subtrees:
798
target = urlutils.join(url, urlutils.escape(path))
799
sublocation = source_branch.reference_parent(file_id, path)
800
sublocation.bzrdir.sprout(target,
801
basis.get_reference_revision(file_id, path),
802
force_new_repo=force_new_repo, recurse=recurse)
804
if basis is not None:
809
class BzrDirPreSplitOut(BzrDir):
810
"""A common class for the all-in-one formats."""
812
def __init__(self, _transport, _format):
813
"""See BzrDir.__init__."""
814
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
815
assert self._format._lock_class == lockable_files.TransportLock
816
assert self._format._lock_file_name == 'branch-lock'
817
self._control_files = lockable_files.LockableFiles(
818
self.get_branch_transport(None),
819
self._format._lock_file_name,
820
self._format._lock_class)
822
def break_lock(self):
823
"""Pre-splitout bzrdirs do not suffer from stale locks."""
824
raise NotImplementedError(self.break_lock)
826
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
827
"""See BzrDir.clone()."""
828
from bzrlib.workingtree import WorkingTreeFormat2
830
result = self._format._initialize_for_clone(url)
831
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
832
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
833
from_branch = self.open_branch()
834
from_branch.clone(result, revision_id=revision_id)
836
self.open_workingtree().clone(result, basis=basis_tree)
837
except errors.NotLocalUrl:
838
# make a new one, this format always has to have one.
840
WorkingTreeFormat2().initialize(result)
841
except errors.NotLocalUrl:
842
# but we cannot do it for remote trees.
843
to_branch = result.open_branch()
844
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
847
def create_branch(self):
848
"""See BzrDir.create_branch."""
849
return self.open_branch()
851
def create_repository(self, shared=False):
852
"""See BzrDir.create_repository."""
854
raise errors.IncompatibleFormat('shared repository', self._format)
855
return self.open_repository()
857
def create_workingtree(self, revision_id=None):
858
"""See BzrDir.create_workingtree."""
859
# this looks buggy but is not -really-
860
# clone and sprout will have set the revision_id
861
# and that will have set it for us, its only
862
# specific uses of create_workingtree in isolation
863
# that can do wonky stuff here, and that only
864
# happens for creating checkouts, which cannot be
865
# done on this format anyway. So - acceptable wart.
866
result = self.open_workingtree()
867
if revision_id is not None:
868
if revision_id == _mod_revision.NULL_REVISION:
869
result.set_parent_ids([])
871
result.set_parent_ids([revision_id])
874
def destroy_workingtree(self):
875
"""See BzrDir.destroy_workingtree."""
876
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
878
def destroy_workingtree_metadata(self):
879
"""See BzrDir.destroy_workingtree_metadata."""
880
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
883
def get_branch_transport(self, branch_format):
884
"""See BzrDir.get_branch_transport()."""
885
if branch_format is None:
886
return self.transport
888
branch_format.get_format_string()
889
except NotImplementedError:
890
return self.transport
891
raise errors.IncompatibleFormat(branch_format, self._format)
893
def get_repository_transport(self, repository_format):
894
"""See BzrDir.get_repository_transport()."""
895
if repository_format is None:
896
return self.transport
898
repository_format.get_format_string()
899
except NotImplementedError:
900
return self.transport
901
raise errors.IncompatibleFormat(repository_format, self._format)
903
def get_workingtree_transport(self, workingtree_format):
904
"""See BzrDir.get_workingtree_transport()."""
905
if workingtree_format is None:
906
return self.transport
908
workingtree_format.get_format_string()
909
except NotImplementedError:
910
return self.transport
911
raise errors.IncompatibleFormat(workingtree_format, self._format)
913
def needs_format_conversion(self, format=None):
914
"""See BzrDir.needs_format_conversion()."""
915
# if the format is not the same as the system default,
916
# an upgrade is needed.
918
format = BzrDirFormat.get_default_format()
919
return not isinstance(self._format, format.__class__)
921
def open_branch(self, unsupported=False):
922
"""See BzrDir.open_branch."""
923
from bzrlib.branch import BzrBranchFormat4
924
format = BzrBranchFormat4()
925
self._check_supported(format, unsupported)
926
return format.open(self, _found=True)
928
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
929
"""See BzrDir.sprout()."""
930
from bzrlib.workingtree import WorkingTreeFormat2
932
result = self._format._initialize_for_clone(url)
933
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
935
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
936
except errors.NoRepositoryPresent:
939
self.open_branch().sprout(result, revision_id=revision_id)
940
except errors.NotBranchError:
942
# we always want a working tree
943
WorkingTreeFormat2().initialize(result)
947
class BzrDir4(BzrDirPreSplitOut):
948
"""A .bzr version 4 control object.
950
This is a deprecated format and may be removed after sept 2006.
953
def create_repository(self, shared=False):
954
"""See BzrDir.create_repository."""
955
return self._format.repository_format.initialize(self, shared)
957
def needs_format_conversion(self, format=None):
958
"""Format 4 dirs are always in need of conversion."""
961
def open_repository(self):
962
"""See BzrDir.open_repository."""
963
from bzrlib.repofmt.weaverepo import RepositoryFormat4
964
return RepositoryFormat4().open(self, _found=True)
967
class BzrDir5(BzrDirPreSplitOut):
968
"""A .bzr version 5 control object.
970
This is a deprecated format and may be removed after sept 2006.
973
def open_repository(self):
974
"""See BzrDir.open_repository."""
975
from bzrlib.repofmt.weaverepo import RepositoryFormat5
976
return RepositoryFormat5().open(self, _found=True)
978
def open_workingtree(self, _unsupported=False):
979
"""See BzrDir.create_workingtree."""
980
from bzrlib.workingtree import WorkingTreeFormat2
981
return WorkingTreeFormat2().open(self, _found=True)
984
class BzrDir6(BzrDirPreSplitOut):
985
"""A .bzr version 6 control object.
987
This is a deprecated format and may be removed after sept 2006.
990
def open_repository(self):
991
"""See BzrDir.open_repository."""
992
from bzrlib.repofmt.weaverepo import RepositoryFormat6
993
return RepositoryFormat6().open(self, _found=True)
995
def open_workingtree(self, _unsupported=False):
996
"""See BzrDir.create_workingtree."""
997
from bzrlib.workingtree import WorkingTreeFormat2
998
return WorkingTreeFormat2().open(self, _found=True)
1001
class BzrDirMeta1(BzrDir):
1002
"""A .bzr meta version 1 control object.
1004
This is the first control object where the
1005
individual aspects are really split out: there are separate repository,
1006
workingtree and branch subdirectories and any subset of the three can be
1007
present within a BzrDir.
1010
def can_convert_format(self):
1011
"""See BzrDir.can_convert_format()."""
1014
def create_branch(self):
1015
"""See BzrDir.create_branch."""
1016
return self._format.get_branch_format().initialize(self)
1018
def create_repository(self, shared=False):
1019
"""See BzrDir.create_repository."""
1020
return self._format.repository_format.initialize(self, shared)
1022
def create_workingtree(self, revision_id=None):
1023
"""See BzrDir.create_workingtree."""
1024
from bzrlib.workingtree import WorkingTreeFormat
1025
return self._format.workingtree_format.initialize(self, revision_id)
1027
def destroy_workingtree(self):
1028
"""See BzrDir.destroy_workingtree."""
1029
wt = self.open_workingtree()
1030
repository = wt.branch.repository
1031
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1032
wt.revert([], old_tree=empty)
1033
self.destroy_workingtree_metadata()
1035
def destroy_workingtree_metadata(self):
1036
self.transport.delete_tree('checkout')
1038
def find_branch_format(self):
1039
"""Find the branch 'format' for this bzrdir.
1041
This might be a synthetic object for e.g. RemoteBranch and SVN.
1043
from bzrlib.branch import BranchFormat
1044
return BranchFormat.find_format(self)
1046
def _get_mkdir_mode(self):
1047
"""Figure out the mode to use when creating a bzrdir subdir."""
1048
temp_control = lockable_files.LockableFiles(self.transport, '',
1049
lockable_files.TransportLock)
1050
return temp_control._dir_mode
1052
def get_branch_reference(self):
1053
"""See BzrDir.get_branch_reference()."""
1054
from bzrlib.branch import BranchFormat
1055
format = BranchFormat.find_format(self)
1056
return format.get_reference(self)
1058
def get_branch_transport(self, branch_format):
1059
"""See BzrDir.get_branch_transport()."""
1060
if branch_format is None:
1061
return self.transport.clone('branch')
1063
branch_format.get_format_string()
1064
except NotImplementedError:
1065
raise errors.IncompatibleFormat(branch_format, self._format)
1067
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1068
except errors.FileExists:
1070
return self.transport.clone('branch')
1072
def get_repository_transport(self, repository_format):
1073
"""See BzrDir.get_repository_transport()."""
1074
if repository_format is None:
1075
return self.transport.clone('repository')
1077
repository_format.get_format_string()
1078
except NotImplementedError:
1079
raise errors.IncompatibleFormat(repository_format, self._format)
1081
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1082
except errors.FileExists:
1084
return self.transport.clone('repository')
1086
def get_workingtree_transport(self, workingtree_format):
1087
"""See BzrDir.get_workingtree_transport()."""
1088
if workingtree_format is None:
1089
return self.transport.clone('checkout')
1091
workingtree_format.get_format_string()
1092
except NotImplementedError:
1093
raise errors.IncompatibleFormat(workingtree_format, self._format)
1095
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1096
except errors.FileExists:
1098
return self.transport.clone('checkout')
1100
def needs_format_conversion(self, format=None):
1101
"""See BzrDir.needs_format_conversion()."""
1103
format = BzrDirFormat.get_default_format()
1104
if not isinstance(self._format, format.__class__):
1105
# it is not a meta dir format, conversion is needed.
1107
# we might want to push this down to the repository?
1109
if not isinstance(self.open_repository()._format,
1110
format.repository_format.__class__):
1111
# the repository needs an upgrade.
1113
except errors.NoRepositoryPresent:
1116
if not isinstance(self.open_branch()._format,
1117
format.get_branch_format().__class__):
1118
# the branch needs an upgrade.
1120
except errors.NotBranchError:
1123
if not isinstance(self.open_workingtree()._format,
1124
format.workingtree_format.__class__):
1125
# the workingtree needs an upgrade.
1127
except (errors.NoWorkingTree, errors.NotLocalUrl):
1131
def open_branch(self, unsupported=False):
1132
"""See BzrDir.open_branch."""
1133
format = self.find_branch_format()
1134
self._check_supported(format, unsupported)
1135
return format.open(self, _found=True)
1137
def open_repository(self, unsupported=False):
1138
"""See BzrDir.open_repository."""
1139
from bzrlib.repository import RepositoryFormat
1140
format = RepositoryFormat.find_format(self)
1141
self._check_supported(format, unsupported)
1142
return format.open(self, _found=True)
1144
def open_workingtree(self, unsupported=False):
1145
"""See BzrDir.open_workingtree."""
1146
from bzrlib.workingtree import WorkingTreeFormat
1147
format = WorkingTreeFormat.find_format(self)
1148
self._check_supported(format, unsupported)
1149
return format.open(self, _found=True)
1152
class BzrDirFormat(object):
1153
"""An encapsulation of the initialization and open routines for a format.
1155
Formats provide three things:
1156
* An initialization routine,
1160
Formats are placed in an dict by their format string for reference
1161
during bzrdir opening. These should be subclasses of BzrDirFormat
1164
Once a format is deprecated, just deprecate the initialize and open
1165
methods on the format class. Do not deprecate the object, as the
1166
object will be created every system load.
1169
_default_format = None
1170
"""The default format used for new .bzr dirs."""
1173
"""The known formats."""
1175
_control_formats = []
1176
"""The registered control formats - .bzr, ....
1178
This is a list of BzrDirFormat objects.
1181
_lock_file_name = 'branch-lock'
1183
# _lock_class must be set in subclasses to the lock type, typ.
1184
# TransportLock or LockDir
1187
def find_format(klass, transport):
1188
"""Return the format present at transport."""
1189
for format in klass._control_formats:
1191
return format.probe_transport(transport)
1192
except errors.NotBranchError:
1193
# this format does not find a control dir here.
1195
raise errors.NotBranchError(path=transport.base)
1198
def probe_transport(klass, transport):
1199
"""Return the .bzrdir style format present in a directory."""
1201
format_string = transport.get(".bzr/branch-format").read()
1202
except errors.NoSuchFile:
1203
raise errors.NotBranchError(path=transport.base)
1206
return klass._formats[format_string]
1208
raise errors.UnknownFormatError(format=format_string)
1211
def get_default_format(klass):
1212
"""Return the current default format."""
1213
return klass._default_format
1215
def get_format_string(self):
1216
"""Return the ASCII format string that identifies this format."""
1217
raise NotImplementedError(self.get_format_string)
1219
def get_format_description(self):
1220
"""Return the short description for this format."""
1221
raise NotImplementedError(self.get_format_description)
1223
def get_converter(self, format=None):
1224
"""Return the converter to use to convert bzrdirs needing converts.
1226
This returns a bzrlib.bzrdir.Converter object.
1228
This should return the best upgrader to step this format towards the
1229
current default format. In the case of plugins we can/should provide
1230
some means for them to extend the range of returnable converters.
1232
:param format: Optional format to override the default format of the
1235
raise NotImplementedError(self.get_converter)
1237
def initialize(self, url):
1238
"""Create a bzr control dir at this url and return an opened copy.
1240
Subclasses should typically override initialize_on_transport
1241
instead of this method.
1243
return self.initialize_on_transport(get_transport(url))
1245
def initialize_on_transport(self, transport):
1246
"""Initialize a new bzrdir in the base directory of a Transport."""
1247
# Since we don't have a .bzr directory, inherit the
1248
# mode from the root directory
1249
temp_control = lockable_files.LockableFiles(transport,
1250
'', lockable_files.TransportLock)
1251
temp_control._transport.mkdir('.bzr',
1252
# FIXME: RBC 20060121 don't peek under
1254
mode=temp_control._dir_mode)
1255
file_mode = temp_control._file_mode
1257
mutter('created control directory in ' + transport.base)
1258
control = transport.clone('.bzr')
1259
utf8_files = [('README',
1260
"This is a Bazaar-NG control directory.\n"
1261
"Do not change any files in this directory.\n"),
1262
('branch-format', self.get_format_string()),
1264
# NB: no need to escape relative paths that are url safe.
1265
control_files = lockable_files.LockableFiles(control,
1266
self._lock_file_name, self._lock_class)
1267
control_files.create_lock()
1268
control_files.lock_write()
1270
for file, content in utf8_files:
1271
control_files.put_utf8(file, content)
1273
control_files.unlock()
1274
return self.open(transport, _found=True)
1276
def is_supported(self):
1277
"""Is this format supported?
1279
Supported formats must be initializable and openable.
1280
Unsupported formats may not support initialization or committing or
1281
some other features depending on the reason for not being supported.
1285
def same_model(self, target_format):
1286
return (self.repository_format.rich_root_data ==
1287
target_format.rich_root_data)
1290
def known_formats(klass):
1291
"""Return all the known formats.
1293
Concrete formats should override _known_formats.
1295
# There is double indirection here to make sure that control
1296
# formats used by more than one dir format will only be probed
1297
# once. This can otherwise be quite expensive for remote connections.
1299
for format in klass._control_formats:
1300
result.update(format._known_formats())
1304
def _known_formats(klass):
1305
"""Return the known format instances for this control format."""
1306
return set(klass._formats.values())
1308
def open(self, transport, _found=False):
1309
"""Return an instance of this format for the dir transport points at.
1311
_found is a private parameter, do not use it.
1314
found_format = BzrDirFormat.find_format(transport)
1315
if not isinstance(found_format, self.__class__):
1316
raise AssertionError("%s was asked to open %s, but it seems to need "
1318
% (self, transport, found_format))
1319
return self._open(transport)
1321
def _open(self, transport):
1322
"""Template method helper for opening BzrDirectories.
1324
This performs the actual open and any additional logic or parameter
1327
raise NotImplementedError(self._open)
1330
def register_format(klass, format):
1331
klass._formats[format.get_format_string()] = format
1334
def register_control_format(klass, format):
1335
"""Register a format that does not use '.bzrdir' for its control dir.
1337
TODO: This should be pulled up into a 'ControlDirFormat' base class
1338
which BzrDirFormat can inherit from, and renamed to register_format
1339
there. It has been done without that for now for simplicity of
1342
klass._control_formats.append(format)
1345
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1346
def set_default_format(klass, format):
1347
klass._set_default_format(format)
1350
def _set_default_format(klass, format):
1351
"""Set default format (for testing behavior of defaults only)"""
1352
klass._default_format = format
1355
return self.get_format_string()[:-1]
1358
def unregister_format(klass, format):
1359
assert klass._formats[format.get_format_string()] is format
1360
del klass._formats[format.get_format_string()]
1363
def unregister_control_format(klass, format):
1364
klass._control_formats.remove(format)
1367
# register BzrDirFormat as a control format
1368
BzrDirFormat.register_control_format(BzrDirFormat)
1371
class BzrDirFormat4(BzrDirFormat):
1372
"""Bzr dir format 4.
1374
This format is a combined format for working tree, branch and repository.
1376
- Format 1 working trees [always]
1377
- Format 4 branches [always]
1378
- Format 4 repositories [always]
1380
This format is deprecated: it indexes texts using a text it which is
1381
removed in format 5; write support for this format has been removed.
1384
_lock_class = lockable_files.TransportLock
1386
def get_format_string(self):
1387
"""See BzrDirFormat.get_format_string()."""
1388
return "Bazaar-NG branch, format 0.0.4\n"
1390
def get_format_description(self):
1391
"""See BzrDirFormat.get_format_description()."""
1392
return "All-in-one format 4"
1394
def get_converter(self, format=None):
1395
"""See BzrDirFormat.get_converter()."""
1396
# there is one and only one upgrade path here.
1397
return ConvertBzrDir4To5()
1399
def initialize_on_transport(self, transport):
1400
"""Format 4 branches cannot be created."""
1401
raise errors.UninitializableFormat(self)
1403
def is_supported(self):
1404
"""Format 4 is not supported.
1406
It is not supported because the model changed from 4 to 5 and the
1407
conversion logic is expensive - so doing it on the fly was not
1412
def _open(self, transport):
1413
"""See BzrDirFormat._open."""
1414
return BzrDir4(transport, self)
1416
def __return_repository_format(self):
1417
"""Circular import protection."""
1418
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1419
return RepositoryFormat4()
1420
repository_format = property(__return_repository_format)
1423
class BzrDirFormat5(BzrDirFormat):
1424
"""Bzr control format 5.
1426
This format is a combined format for working tree, branch and repository.
1428
- Format 2 working trees [always]
1429
- Format 4 branches [always]
1430
- Format 5 repositories [always]
1431
Unhashed stores in the repository.
1434
_lock_class = lockable_files.TransportLock
1436
def get_format_string(self):
1437
"""See BzrDirFormat.get_format_string()."""
1438
return "Bazaar-NG branch, format 5\n"
1440
def get_format_description(self):
1441
"""See BzrDirFormat.get_format_description()."""
1442
return "All-in-one format 5"
1444
def get_converter(self, format=None):
1445
"""See BzrDirFormat.get_converter()."""
1446
# there is one and only one upgrade path here.
1447
return ConvertBzrDir5To6()
1449
def _initialize_for_clone(self, url):
1450
return self.initialize_on_transport(get_transport(url), _cloning=True)
1452
def initialize_on_transport(self, transport, _cloning=False):
1453
"""Format 5 dirs always have working tree, branch and repository.
1455
Except when they are being cloned.
1457
from bzrlib.branch import BzrBranchFormat4
1458
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1459
from bzrlib.workingtree import WorkingTreeFormat2
1460
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1461
RepositoryFormat5().initialize(result, _internal=True)
1463
branch = BzrBranchFormat4().initialize(result)
1465
WorkingTreeFormat2().initialize(result)
1466
except errors.NotLocalUrl:
1467
# Even though we can't access the working tree, we need to
1468
# create its control files.
1469
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1472
def _open(self, transport):
1473
"""See BzrDirFormat._open."""
1474
return BzrDir5(transport, self)
1476
def __return_repository_format(self):
1477
"""Circular import protection."""
1478
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1479
return RepositoryFormat5()
1480
repository_format = property(__return_repository_format)
1483
class BzrDirFormat6(BzrDirFormat):
1484
"""Bzr control format 6.
1486
This format is a combined format for working tree, branch and repository.
1488
- Format 2 working trees [always]
1489
- Format 4 branches [always]
1490
- Format 6 repositories [always]
1493
_lock_class = lockable_files.TransportLock
1495
def get_format_string(self):
1496
"""See BzrDirFormat.get_format_string()."""
1497
return "Bazaar-NG branch, format 6\n"
1499
def get_format_description(self):
1500
"""See BzrDirFormat.get_format_description()."""
1501
return "All-in-one format 6"
1503
def get_converter(self, format=None):
1504
"""See BzrDirFormat.get_converter()."""
1505
# there is one and only one upgrade path here.
1506
return ConvertBzrDir6ToMeta()
1508
def _initialize_for_clone(self, url):
1509
return self.initialize_on_transport(get_transport(url), _cloning=True)
1511
def initialize_on_transport(self, transport, _cloning=False):
1512
"""Format 6 dirs always have working tree, branch and repository.
1514
Except when they are being cloned.
1516
from bzrlib.branch import BzrBranchFormat4
1517
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1518
from bzrlib.workingtree import WorkingTreeFormat2
1519
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1520
RepositoryFormat6().initialize(result, _internal=True)
1522
branch = BzrBranchFormat4().initialize(result)
1524
WorkingTreeFormat2().initialize(result)
1525
except errors.NotLocalUrl:
1526
# Even though we can't access the working tree, we need to
1527
# create its control files.
1528
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1531
def _open(self, transport):
1532
"""See BzrDirFormat._open."""
1533
return BzrDir6(transport, self)
1535
def __return_repository_format(self):
1536
"""Circular import protection."""
1537
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1538
return RepositoryFormat6()
1539
repository_format = property(__return_repository_format)
1542
class BzrDirMetaFormat1(BzrDirFormat):
1543
"""Bzr meta control format 1
1545
This is the first format with split out working tree, branch and repository
1548
- Format 3 working trees [optional]
1549
- Format 5 branches [optional]
1550
- Format 7 repositories [optional]
1553
_lock_class = lockdir.LockDir
1556
self._workingtree_format = None
1557
self._branch_format = None
1559
def __eq__(self, other):
1560
if other.__class__ is not self.__class__:
1562
if other.repository_format != self.repository_format:
1564
if other.workingtree_format != self.workingtree_format:
1568
def __ne__(self, other):
1569
return not self == other
1571
def get_branch_format(self):
1572
if self._branch_format is None:
1573
from bzrlib.branch import BranchFormat
1574
self._branch_format = BranchFormat.get_default_format()
1575
return self._branch_format
1577
def set_branch_format(self, format):
1578
self._branch_format = format
1580
def get_converter(self, format=None):
1581
"""See BzrDirFormat.get_converter()."""
1583
format = BzrDirFormat.get_default_format()
1584
if not isinstance(self, format.__class__):
1585
# converting away from metadir is not implemented
1586
raise NotImplementedError(self.get_converter)
1587
return ConvertMetaToMeta(format)
1589
def get_format_string(self):
1590
"""See BzrDirFormat.get_format_string()."""
1591
return "Bazaar-NG meta directory, format 1\n"
1593
def get_format_description(self):
1594
"""See BzrDirFormat.get_format_description()."""
1595
return "Meta directory format 1"
1597
def _open(self, transport):
1598
"""See BzrDirFormat._open."""
1599
return BzrDirMeta1(transport, self)
1601
def __return_repository_format(self):
1602
"""Circular import protection."""
1603
if getattr(self, '_repository_format', None):
1604
return self._repository_format
1605
from bzrlib.repository import RepositoryFormat
1606
return RepositoryFormat.get_default_format()
1608
def __set_repository_format(self, value):
1609
"""Allow changint the repository format for metadir formats."""
1610
self._repository_format = value
1612
repository_format = property(__return_repository_format, __set_repository_format)
1614
def __get_workingtree_format(self):
1615
if self._workingtree_format is None:
1616
from bzrlib.workingtree import WorkingTreeFormat
1617
self._workingtree_format = WorkingTreeFormat.get_default_format()
1618
return self._workingtree_format
1620
def __set_workingtree_format(self, wt_format):
1621
self._workingtree_format = wt_format
1623
workingtree_format = property(__get_workingtree_format,
1624
__set_workingtree_format)
1627
BzrDirFormat.register_format(BzrDirFormat4())
1628
BzrDirFormat.register_format(BzrDirFormat5())
1629
BzrDirFormat.register_format(BzrDirFormat6())
1630
__default_format = BzrDirMetaFormat1()
1631
BzrDirFormat.register_format(__default_format)
1632
BzrDirFormat._default_format = __default_format
1635
class BzrDirTestProviderAdapter(object):
1636
"""A tool to generate a suite testing multiple bzrdir formats at once.
1638
This is done by copying the test once for each transport and injecting
1639
the transport_server, transport_readonly_server, and bzrdir_format
1640
classes into each copy. Each copy is also given a new id() to make it
1644
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1646
"""Create an object to adapt tests.
1648
:param vfs_server: A factory to create a Transport Server which has
1649
all the VFS methods working, and is writable.
1651
self._vfs_factory = vfs_factory
1652
self._transport_server = transport_server
1653
self._transport_readonly_server = transport_readonly_server
1654
self._formats = formats
1656
def adapt(self, test):
1657
result = unittest.TestSuite()
1658
for format in self._formats:
1659
new_test = deepcopy(test)
1660
new_test.vfs_transport_factory = self._vfs_factory
1661
new_test.transport_server = self._transport_server
1662
new_test.transport_readonly_server = self._transport_readonly_server
1663
new_test.bzrdir_format = format
1664
def make_new_test_id():
1665
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1666
return lambda: new_id
1667
new_test.id = make_new_test_id()
1668
result.addTest(new_test)
1672
class Converter(object):
1673
"""Converts a disk format object from one format to another."""
1675
def convert(self, to_convert, pb):
1676
"""Perform the conversion of to_convert, giving feedback via pb.
1678
:param to_convert: The disk object to convert.
1679
:param pb: a progress bar to use for progress information.
1682
def step(self, message):
1683
"""Update the pb by a step."""
1685
self.pb.update(message, self.count, self.total)
1688
class ConvertBzrDir4To5(Converter):
1689
"""Converts format 4 bzr dirs to format 5."""
1692
super(ConvertBzrDir4To5, self).__init__()
1693
self.converted_revs = set()
1694
self.absent_revisions = set()
1698
def convert(self, to_convert, pb):
1699
"""See Converter.convert()."""
1700
self.bzrdir = to_convert
1702
self.pb.note('starting upgrade from format 4 to 5')
1703
if isinstance(self.bzrdir.transport, LocalTransport):
1704
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1705
self._convert_to_weaves()
1706
return BzrDir.open(self.bzrdir.root_transport.base)
1708
def _convert_to_weaves(self):
1709
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1712
stat = self.bzrdir.transport.stat('weaves')
1713
if not S_ISDIR(stat.st_mode):
1714
self.bzrdir.transport.delete('weaves')
1715
self.bzrdir.transport.mkdir('weaves')
1716
except errors.NoSuchFile:
1717
self.bzrdir.transport.mkdir('weaves')
1718
# deliberately not a WeaveFile as we want to build it up slowly.
1719
self.inv_weave = Weave('inventory')
1720
# holds in-memory weaves for all files
1721
self.text_weaves = {}
1722
self.bzrdir.transport.delete('branch-format')
1723
self.branch = self.bzrdir.open_branch()
1724
self._convert_working_inv()
1725
rev_history = self.branch.revision_history()
1726
# to_read is a stack holding the revisions we still need to process;
1727
# appending to it adds new highest-priority revisions
1728
self.known_revisions = set(rev_history)
1729
self.to_read = rev_history[-1:]
1731
rev_id = self.to_read.pop()
1732
if (rev_id not in self.revisions
1733
and rev_id not in self.absent_revisions):
1734
self._load_one_rev(rev_id)
1736
to_import = self._make_order()
1737
for i, rev_id in enumerate(to_import):
1738
self.pb.update('converting revision', i, len(to_import))
1739
self._convert_one_rev(rev_id)
1741
self._write_all_weaves()
1742
self._write_all_revs()
1743
self.pb.note('upgraded to weaves:')
1744
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1745
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1746
self.pb.note(' %6d texts', self.text_count)
1747
self._cleanup_spare_files_after_format4()
1748
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1750
def _cleanup_spare_files_after_format4(self):
1751
# FIXME working tree upgrade foo.
1752
for n in 'merged-patches', 'pending-merged-patches':
1754
## assert os.path.getsize(p) == 0
1755
self.bzrdir.transport.delete(n)
1756
except errors.NoSuchFile:
1758
self.bzrdir.transport.delete_tree('inventory-store')
1759
self.bzrdir.transport.delete_tree('text-store')
1761
def _convert_working_inv(self):
1762
inv = xml4.serializer_v4.read_inventory(
1763
self.branch.control_files.get('inventory'))
1764
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1765
# FIXME inventory is a working tree change.
1766
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1768
def _write_all_weaves(self):
1769
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1770
weave_transport = self.bzrdir.transport.clone('weaves')
1771
weaves = WeaveStore(weave_transport, prefixed=False)
1772
transaction = WriteTransaction()
1776
for file_id, file_weave in self.text_weaves.items():
1777
self.pb.update('writing weave', i, len(self.text_weaves))
1778
weaves._put_weave(file_id, file_weave, transaction)
1780
self.pb.update('inventory', 0, 1)
1781
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1782
self.pb.update('inventory', 1, 1)
1786
def _write_all_revs(self):
1787
"""Write all revisions out in new form."""
1788
self.bzrdir.transport.delete_tree('revision-store')
1789
self.bzrdir.transport.mkdir('revision-store')
1790
revision_transport = self.bzrdir.transport.clone('revision-store')
1792
_revision_store = TextRevisionStore(TextStore(revision_transport,
1796
transaction = WriteTransaction()
1797
for i, rev_id in enumerate(self.converted_revs):
1798
self.pb.update('write revision', i, len(self.converted_revs))
1799
_revision_store.add_revision(self.revisions[rev_id], transaction)
1803
def _load_one_rev(self, rev_id):
1804
"""Load a revision object into memory.
1806
Any parents not either loaded or abandoned get queued to be
1808
self.pb.update('loading revision',
1809
len(self.revisions),
1810
len(self.known_revisions))
1811
if not self.branch.repository.has_revision(rev_id):
1813
self.pb.note('revision {%s} not present in branch; '
1814
'will be converted as a ghost',
1816
self.absent_revisions.add(rev_id)
1818
rev = self.branch.repository._revision_store.get_revision(rev_id,
1819
self.branch.repository.get_transaction())
1820
for parent_id in rev.parent_ids:
1821
self.known_revisions.add(parent_id)
1822
self.to_read.append(parent_id)
1823
self.revisions[rev_id] = rev
1825
def _load_old_inventory(self, rev_id):
1826
assert rev_id not in self.converted_revs
1827
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1828
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1829
inv.revision_id = rev_id
1830
rev = self.revisions[rev_id]
1831
if rev.inventory_sha1:
1832
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1833
'inventory sha mismatch for {%s}' % rev_id
1836
def _load_updated_inventory(self, rev_id):
1837
assert rev_id in self.converted_revs
1838
inv_xml = self.inv_weave.get_text(rev_id)
1839
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1842
def _convert_one_rev(self, rev_id):
1843
"""Convert revision and all referenced objects to new format."""
1844
rev = self.revisions[rev_id]
1845
inv = self._load_old_inventory(rev_id)
1846
present_parents = [p for p in rev.parent_ids
1847
if p not in self.absent_revisions]
1848
self._convert_revision_contents(rev, inv, present_parents)
1849
self._store_new_weave(rev, inv, present_parents)
1850
self.converted_revs.add(rev_id)
1852
def _store_new_weave(self, rev, inv, present_parents):
1853
# the XML is now updated with text versions
1855
entries = inv.iter_entries()
1857
for path, ie in entries:
1858
assert getattr(ie, 'revision', None) is not None, \
1859
'no revision on {%s} in {%s}' % \
1860
(file_id, rev.revision_id)
1861
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1862
new_inv_sha1 = sha_string(new_inv_xml)
1863
self.inv_weave.add_lines(rev.revision_id,
1865
new_inv_xml.splitlines(True))
1866
rev.inventory_sha1 = new_inv_sha1
1868
def _convert_revision_contents(self, rev, inv, present_parents):
1869
"""Convert all the files within a revision.
1871
Also upgrade the inventory to refer to the text revision ids."""
1872
rev_id = rev.revision_id
1873
mutter('converting texts of revision {%s}',
1875
parent_invs = map(self._load_updated_inventory, present_parents)
1876
entries = inv.iter_entries()
1878
for path, ie in entries:
1879
self._convert_file_version(rev, ie, parent_invs)
1881
def _convert_file_version(self, rev, ie, parent_invs):
1882
"""Convert one version of one file.
1884
The file needs to be added into the weave if it is a merge
1885
of >=2 parents or if it's changed from its parent.
1887
file_id = ie.file_id
1888
rev_id = rev.revision_id
1889
w = self.text_weaves.get(file_id)
1892
self.text_weaves[file_id] = w
1893
text_changed = False
1894
previous_entries = ie.find_previous_heads(parent_invs,
1898
for old_revision in previous_entries:
1899
# if this fails, its a ghost ?
1900
assert old_revision in self.converted_revs, \
1901
"Revision {%s} not in converted_revs" % old_revision
1902
self.snapshot_ie(previous_entries, ie, w, rev_id)
1904
assert getattr(ie, 'revision', None) is not None
1906
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1907
# TODO: convert this logic, which is ~= snapshot to
1908
# a call to:. This needs the path figured out. rather than a work_tree
1909
# a v4 revision_tree can be given, or something that looks enough like
1910
# one to give the file content to the entry if it needs it.
1911
# and we need something that looks like a weave store for snapshot to
1913
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1914
if len(previous_revisions) == 1:
1915
previous_ie = previous_revisions.values()[0]
1916
if ie._unchanged(previous_ie):
1917
ie.revision = previous_ie.revision
1920
text = self.branch.repository.text_store.get(ie.text_id)
1921
file_lines = text.readlines()
1922
assert sha_strings(file_lines) == ie.text_sha1
1923
assert sum(map(len, file_lines)) == ie.text_size
1924
w.add_lines(rev_id, previous_revisions, file_lines)
1925
self.text_count += 1
1927
w.add_lines(rev_id, previous_revisions, [])
1928
ie.revision = rev_id
1930
def _make_order(self):
1931
"""Return a suitable order for importing revisions.
1933
The order must be such that an revision is imported after all
1934
its (present) parents.
1936
todo = set(self.revisions.keys())
1937
done = self.absent_revisions.copy()
1940
# scan through looking for a revision whose parents
1942
for rev_id in sorted(list(todo)):
1943
rev = self.revisions[rev_id]
1944
parent_ids = set(rev.parent_ids)
1945
if parent_ids.issubset(done):
1946
# can take this one now
1947
order.append(rev_id)
1953
class ConvertBzrDir5To6(Converter):
1954
"""Converts format 5 bzr dirs to format 6."""
1956
def convert(self, to_convert, pb):
1957
"""See Converter.convert()."""
1958
self.bzrdir = to_convert
1960
self.pb.note('starting upgrade from format 5 to 6')
1961
self._convert_to_prefixed()
1962
return BzrDir.open(self.bzrdir.root_transport.base)
1964
def _convert_to_prefixed(self):
1965
from bzrlib.store import TransportStore
1966
self.bzrdir.transport.delete('branch-format')
1967
for store_name in ["weaves", "revision-store"]:
1968
self.pb.note("adding prefixes to %s" % store_name)
1969
store_transport = self.bzrdir.transport.clone(store_name)
1970
store = TransportStore(store_transport, prefixed=True)
1971
for urlfilename in store_transport.list_dir('.'):
1972
filename = urlutils.unescape(urlfilename)
1973
if (filename.endswith(".weave") or
1974
filename.endswith(".gz") or
1975
filename.endswith(".sig")):
1976
file_id = os.path.splitext(filename)[0]
1979
prefix_dir = store.hash_prefix(file_id)
1980
# FIXME keep track of the dirs made RBC 20060121
1982
store_transport.move(filename, prefix_dir + '/' + filename)
1983
except errors.NoSuchFile: # catches missing dirs strangely enough
1984
store_transport.mkdir(prefix_dir)
1985
store_transport.move(filename, prefix_dir + '/' + filename)
1986
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1989
class ConvertBzrDir6ToMeta(Converter):
1990
"""Converts format 6 bzr dirs to metadirs."""
1992
def convert(self, to_convert, pb):
1993
"""See Converter.convert()."""
1994
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1995
from bzrlib.branch import BzrBranchFormat5
1996
self.bzrdir = to_convert
1999
self.total = 20 # the steps we know about
2000
self.garbage_inventories = []
2002
self.pb.note('starting upgrade from format 6 to metadir')
2003
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2004
# its faster to move specific files around than to open and use the apis...
2005
# first off, nuke ancestry.weave, it was never used.
2007
self.step('Removing ancestry.weave')
2008
self.bzrdir.transport.delete('ancestry.weave')
2009
except errors.NoSuchFile:
2011
# find out whats there
2012
self.step('Finding branch files')
2013
last_revision = self.bzrdir.open_branch().last_revision()
2014
bzrcontents = self.bzrdir.transport.list_dir('.')
2015
for name in bzrcontents:
2016
if name.startswith('basis-inventory.'):
2017
self.garbage_inventories.append(name)
2018
# create new directories for repository, working tree and branch
2019
self.dir_mode = self.bzrdir._control_files._dir_mode
2020
self.file_mode = self.bzrdir._control_files._file_mode
2021
repository_names = [('inventory.weave', True),
2022
('revision-store', True),
2024
self.step('Upgrading repository ')
2025
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2026
self.make_lock('repository')
2027
# we hard code the formats here because we are converting into
2028
# the meta format. The meta format upgrader can take this to a
2029
# future format within each component.
2030
self.put_format('repository', RepositoryFormat7())
2031
for entry in repository_names:
2032
self.move_entry('repository', entry)
2034
self.step('Upgrading branch ')
2035
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2036
self.make_lock('branch')
2037
self.put_format('branch', BzrBranchFormat5())
2038
branch_files = [('revision-history', True),
2039
('branch-name', True),
2041
for entry in branch_files:
2042
self.move_entry('branch', entry)
2044
checkout_files = [('pending-merges', True),
2045
('inventory', True),
2046
('stat-cache', False)]
2047
# If a mandatory checkout file is not present, the branch does not have
2048
# a functional checkout. Do not create a checkout in the converted
2050
for name, mandatory in checkout_files:
2051
if mandatory and name not in bzrcontents:
2052
has_checkout = False
2056
if not has_checkout:
2057
self.pb.note('No working tree.')
2058
# If some checkout files are there, we may as well get rid of them.
2059
for name, mandatory in checkout_files:
2060
if name in bzrcontents:
2061
self.bzrdir.transport.delete(name)
2063
from bzrlib.workingtree import WorkingTreeFormat3
2064
self.step('Upgrading working tree')
2065
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2066
self.make_lock('checkout')
2068
'checkout', WorkingTreeFormat3())
2069
self.bzrdir.transport.delete_multi(
2070
self.garbage_inventories, self.pb)
2071
for entry in checkout_files:
2072
self.move_entry('checkout', entry)
2073
if last_revision is not None:
2074
self.bzrdir._control_files.put_utf8(
2075
'checkout/last-revision', last_revision)
2076
self.bzrdir._control_files.put_utf8(
2077
'branch-format', BzrDirMetaFormat1().get_format_string())
2078
return BzrDir.open(self.bzrdir.root_transport.base)
2080
def make_lock(self, name):
2081
"""Make a lock for the new control dir name."""
2082
self.step('Make %s lock' % name)
2083
ld = lockdir.LockDir(self.bzrdir.transport,
2085
file_modebits=self.file_mode,
2086
dir_modebits=self.dir_mode)
2089
def move_entry(self, new_dir, entry):
2090
"""Move then entry name into new_dir."""
2092
mandatory = entry[1]
2093
self.step('Moving %s' % name)
2095
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2096
except errors.NoSuchFile:
2100
def put_format(self, dirname, format):
2101
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2104
class ConvertMetaToMeta(Converter):
2105
"""Converts the components of metadirs."""
2107
def __init__(self, target_format):
2108
"""Create a metadir to metadir converter.
2110
:param target_format: The final metadir format that is desired.
2112
self.target_format = target_format
2114
def convert(self, to_convert, pb):
2115
"""See Converter.convert()."""
2116
self.bzrdir = to_convert
2120
self.step('checking repository format')
2122
repo = self.bzrdir.open_repository()
2123
except errors.NoRepositoryPresent:
2126
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2127
from bzrlib.repository import CopyConverter
2128
self.pb.note('starting repository conversion')
2129
converter = CopyConverter(self.target_format.repository_format)
2130
converter.convert(repo, pb)
2132
branch = self.bzrdir.open_branch()
2133
except errors.NotBranchError:
2136
# TODO: conversions of Branch and Tree should be done by
2137
# InterXFormat lookups
2138
# Avoid circular imports
2139
from bzrlib import branch as _mod_branch
2140
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2141
self.target_format.get_branch_format().__class__ is
2142
_mod_branch.BzrBranchFormat6):
2143
branch_converter = _mod_branch.Converter5to6()
2144
branch_converter.convert(branch)
2146
tree = self.bzrdir.open_workingtree()
2147
except (errors.NoWorkingTree, errors.NotLocalUrl):
2150
# TODO: conversions of Branch and Tree should be done by
2151
# InterXFormat lookups
2152
if (isinstance(tree, workingtree.WorkingTree3) and
2153
not isinstance(tree, workingtree_4.WorkingTree4) and
2154
isinstance(self.target_format.workingtree_format,
2155
workingtree_4.WorkingTreeFormat4)):
2156
workingtree_4.Converter3to4().convert(tree)
2160
# This is not in remote.py because it's small, and needs to be registered.
2161
# Putting it in remote.py creates a circular import problem.
2162
# we can make it a lazy object if the control formats is turned into something
2164
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2165
"""Format representing bzrdirs accessed via a smart server"""
2167
def get_format_description(self):
2168
return 'bzr remote bzrdir'
2171
def probe_transport(klass, transport):
2172
"""Return a RemoteBzrDirFormat object if it looks possible."""
2174
transport.get_smart_client()
2175
except (NotImplementedError, AttributeError,
2176
errors.TransportNotPossible):
2177
# no smart server, so not a branch for this format type.
2178
raise errors.NotBranchError(path=transport.base)
2182
def initialize_on_transport(self, transport):
2184
# hand off the request to the smart server
2185
medium = transport.get_smart_medium()
2186
except errors.NoSmartMedium:
2187
# TODO: lookup the local format from a server hint.
2188
local_dir_format = BzrDirMetaFormat1()
2189
return local_dir_format.initialize_on_transport(transport)
2190
client = SmartClient(medium)
2191
path = client.remote_path_from_transport(transport)
2192
response = SmartClient(medium).call('BzrDirFormat.initialize', path)
2193
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2194
return remote.RemoteBzrDir(transport)
2196
def _open(self, transport):
2197
return remote.RemoteBzrDir(transport)
2199
def __eq__(self, other):
2200
if not isinstance(other, RemoteBzrDirFormat):
2202
return self.get_format_description() == other.get_format_description()
2205
# We can't use register_control_format because it adds it at a lower priority
2206
# than the existing branches, whereas this should take priority.
2207
BzrDirFormat._control_formats.insert(0, RemoteBzrDirFormat)
2210
class BzrDirFormatInfo(object):
2212
def __init__(self, native, deprecated, hidden):
2213
self.deprecated = deprecated
2214
self.native = native
2215
self.hidden = hidden
2218
class BzrDirFormatRegistry(registry.Registry):
2219
"""Registry of user-selectable BzrDir subformats.
2221
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2222
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2225
def register_metadir(self, key,
2226
repository_format, help, native=True, deprecated=False,
2230
"""Register a metadir subformat.
2232
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2233
by the Repository format.
2235
:param repository_format: The fully-qualified repository format class
2237
:param branch_format: Fully-qualified branch format class name as
2239
:param tree_format: Fully-qualified tree format class name as
2242
# This should be expanded to support setting WorkingTree and Branch
2243
# formats, once BzrDirMetaFormat1 supports that.
2244
def _load(full_name):
2245
mod_name, factory_name = full_name.rsplit('.', 1)
2247
mod = __import__(mod_name, globals(), locals(),
2249
except ImportError, e:
2250
raise ImportError('failed to load %s: %s' % (full_name, e))
2252
factory = getattr(mod, factory_name)
2253
except AttributeError:
2254
raise AttributeError('no factory %s in module %r'
2259
bd = BzrDirMetaFormat1()
2260
if branch_format is not None:
2261
bd.set_branch_format(_load(branch_format))
2262
if tree_format is not None:
2263
bd.workingtree_format = _load(tree_format)
2264
if repository_format is not None:
2265
bd.repository_format = _load(repository_format)
2267
self.register(key, helper, help, native, deprecated, hidden)
2269
def register(self, key, factory, help, native=True, deprecated=False,
2271
"""Register a BzrDirFormat factory.
2273
The factory must be a callable that takes one parameter: the key.
2274
It must produce an instance of the BzrDirFormat when called.
2276
This function mainly exists to prevent the info object from being
2279
registry.Registry.register(self, key, factory, help,
2280
BzrDirFormatInfo(native, deprecated, hidden))
2282
def register_lazy(self, key, module_name, member_name, help, native=True,
2283
deprecated=False, hidden=False):
2284
registry.Registry.register_lazy(self, key, module_name, member_name,
2285
help, BzrDirFormatInfo(native, deprecated, hidden))
2287
def set_default(self, key):
2288
"""Set the 'default' key to be a clone of the supplied key.
2290
This method must be called once and only once.
2292
registry.Registry.register(self, 'default', self.get(key),
2293
self.get_help(key), info=self.get_info(key))
2295
def set_default_repository(self, key):
2296
"""Set the FormatRegistry default and Repository default.
2298
This is a transitional method while Repository.set_default_format
2301
if 'default' in self:
2302
self.remove('default')
2303
self.set_default(key)
2304
format = self.get('default')()
2305
assert isinstance(format, BzrDirMetaFormat1)
2307
def make_bzrdir(self, key):
2308
return self.get(key)()
2310
def help_topic(self, topic):
2311
output = textwrap.dedent("""\
2312
Bazaar directory formats
2313
------------------------
2315
These formats can be used for creating branches, working trees, and
2319
default_help = self.get_help('default')
2321
for key in self.keys():
2322
if key == 'default':
2324
help = self.get_help(key)
2325
if help == default_help:
2326
default_realkey = key
2328
help_pairs.append((key, help))
2330
def wrapped(key, help, info):
2332
help = '(native) ' + help
2333
return ' %s:\n%s\n\n' % (key,
2334
textwrap.fill(help, initial_indent=' ',
2335
subsequent_indent=' '))
2336
output += wrapped('%s/default' % default_realkey, default_help,
2337
self.get_info('default'))
2338
deprecated_pairs = []
2339
for key, help in help_pairs:
2340
info = self.get_info(key)
2343
elif info.deprecated:
2344
deprecated_pairs.append((key, help))
2346
output += wrapped(key, help, info)
2347
if len(deprecated_pairs) > 0:
2348
output += "Deprecated formats\n------------------\n\n"
2349
for key, help in deprecated_pairs:
2350
info = self.get_info(key)
2351
output += wrapped(key, help, info)
2356
format_registry = BzrDirFormatRegistry()
2357
format_registry.register('weave', BzrDirFormat6,
2358
'Pre-0.8 format. Slower than knit and does not'
2359
' support checkouts or shared repositories.',
2361
format_registry.register_metadir('knit',
2362
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2363
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2364
branch_format='bzrlib.branch.BzrBranchFormat5',
2365
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2366
format_registry.register_metadir('metaweave',
2367
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2368
'Transitional format in 0.8. Slower than knit.',
2369
branch_format='bzrlib.branch.BzrBranchFormat5',
2370
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2372
format_registry.register_metadir('dirstate',
2373
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2374
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2375
'above when accessed over the network.',
2376
branch_format='bzrlib.branch.BzrBranchFormat5',
2377
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2378
# directly from workingtree_4 triggers a circular import.
2379
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2381
format_registry.register_metadir('dirstate-tags',
2382
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2383
help='New in 0.15: Fast local operations and improved scaling for '
2384
'network operations. Additionally adds support for tags.'
2385
' Incompatible with bzr < 0.15.',
2386
branch_format='bzrlib.branch.BzrBranchFormat6',
2387
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2389
format_registry.register_metadir('dirstate-with-subtree',
2390
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2391
help='New in 0.15: Fast local operations and improved scaling for '
2392
'network operations. Additionally adds support for versioning nested '
2393
'bzr branches. Incompatible with bzr < 0.15.',
2394
branch_format='bzrlib.branch.BzrBranchFormat6',
2395
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2398
format_registry.set_default('dirstate')