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: The Format probe_transport seems a bit redundant with just trying to
26
# open the bzrdir. -- mbp
28
# TODO: Can we move specific formats into separate modules to make this file
31
from cStringIO import StringIO
35
from bzrlib.lazy_import import lazy_import
36
lazy_import(globals(), """
37
from copy import deepcopy
38
from stat import S_ISDIR
47
revision as _mod_revision,
56
from bzrlib.osutils import (
61
from bzrlib.store.revision.text import TextRevisionStore
62
from bzrlib.store.text import TextStore
63
from bzrlib.store.versioned import WeaveStore
64
from bzrlib.transactions import WriteTransaction
65
from bzrlib.transport import (
66
do_catching_redirections,
69
from bzrlib.weave import Weave
72
from bzrlib.trace import (
76
from bzrlib.transport.local import LocalTransport
80
"""A .bzr control diretory.
82
BzrDir instances let you create or open any of the things that can be
83
found within .bzr - checkouts, branches and repositories.
86
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
88
a transport connected to the directory this bzr was opened from.
92
"""Invoke break_lock on the first object in the bzrdir.
94
If there is a tree, the tree is opened and break_lock() called.
95
Otherwise, branch is tried, and finally repository.
98
thing_to_unlock = self.open_workingtree()
99
except (errors.NotLocalUrl, errors.NoWorkingTree):
101
thing_to_unlock = self.open_branch()
102
except errors.NotBranchError:
104
thing_to_unlock = self.open_repository()
105
except errors.NoRepositoryPresent:
107
thing_to_unlock.break_lock()
109
def can_convert_format(self):
110
"""Return true if this bzrdir is one whose format we can convert from."""
113
def check_conversion_target(self, target_format):
114
target_repo_format = target_format.repository_format
115
source_repo_format = self._format.repository_format
116
source_repo_format.check_conversion_target(target_repo_format)
119
def _check_supported(format, allow_unsupported,
120
recommend_upgrade=True,
122
"""Give an error or warning on old formats.
124
:param format: may be any kind of format - workingtree, branch,
127
:param allow_unsupported: If true, allow opening
128
formats that are strongly deprecated, and which may
129
have limited functionality.
131
:param recommend_upgrade: If true (default), warn
132
the user through the ui object that they may wish
133
to upgrade the object.
135
# TODO: perhaps move this into a base Format class; it's not BzrDir
136
# specific. mbp 20070323
137
if not allow_unsupported and not format.is_supported():
138
# see open_downlevel to open legacy branches.
139
raise errors.UnsupportedFormatError(format=format)
140
if recommend_upgrade \
141
and getattr(format, 'upgrade_recommended', False):
142
ui.ui_factory.recommend_upgrade(
143
format.get_format_description(),
146
def clone(self, url, revision_id=None, force_new_repo=False):
147
"""Clone this bzrdir and its contents to url verbatim.
149
If urls last component does not exist, it will be created.
151
if revision_id is not None, then the clone operation may tune
152
itself to download less data.
153
:param force_new_repo: Do not use a shared repository for the target
154
even if one is available.
157
result = self._format.initialize(url)
159
local_repo = self.find_repository()
160
except errors.NoRepositoryPresent:
163
# may need to copy content in
165
result_repo = local_repo.clone(
167
revision_id=revision_id)
168
result_repo.set_make_working_trees(local_repo.make_working_trees())
171
result_repo = result.find_repository()
172
# fetch content this dir needs.
173
result_repo.fetch(local_repo, revision_id=revision_id)
174
except errors.NoRepositoryPresent:
175
# needed to make one anyway.
176
result_repo = local_repo.clone(
178
revision_id=revision_id)
179
result_repo.set_make_working_trees(local_repo.make_working_trees())
180
# 1 if there is a branch present
181
# make sure its content is available in the target repository
184
self.open_branch().clone(result, revision_id=revision_id)
185
except errors.NotBranchError:
188
self.open_workingtree().clone(result)
189
except (errors.NoWorkingTree, errors.NotLocalUrl):
193
# TODO: This should be given a Transport, and should chdir up; otherwise
194
# this will open a new connection.
195
def _make_tail(self, url):
196
head, tail = urlutils.split(url)
197
if tail and tail != '.':
198
t = get_transport(head)
201
except errors.FileExists:
204
# TODO: Should take a Transport
206
def create(cls, base, format=None):
207
"""Create a new BzrDir at the url 'base'.
209
This will call the current default formats initialize with base
210
as the only parameter.
212
:param format: If supplied, the format of branch to create. If not
213
supplied, the default is used.
215
if cls is not BzrDir:
216
raise AssertionError("BzrDir.create always creates the default"
217
" format, not one of %r" % cls)
218
head, tail = urlutils.split(base)
219
if tail and tail != '.':
220
t = get_transport(head)
223
except errors.FileExists:
226
format = BzrDirFormat.get_default_format()
227
return format.initialize(safe_unicode(base))
229
def create_branch(self):
230
"""Create a branch in this BzrDir.
232
The bzrdirs format will control what branch format is created.
233
For more control see BranchFormatXX.create(a_bzrdir).
235
raise NotImplementedError(self.create_branch)
238
def create_branch_and_repo(base, force_new_repo=False, format=None):
239
"""Create a new BzrDir, Branch and Repository at the url 'base'.
241
This will use the current default BzrDirFormat, and use whatever
242
repository format that that uses via bzrdir.create_branch and
243
create_repository. If a shared repository is available that is used
246
The created Branch object is returned.
248
:param base: The URL to create the branch at.
249
:param force_new_repo: If True a new repository is always created.
251
bzrdir = BzrDir.create(base, format)
252
bzrdir._find_or_create_repository(force_new_repo)
253
return bzrdir.create_branch()
255
def _find_or_create_repository(self, force_new_repo):
256
"""Create a new repository if needed, returning the repository."""
258
return self.create_repository()
260
return self.find_repository()
261
except errors.NoRepositoryPresent:
262
return self.create_repository()
265
def create_branch_convenience(base, force_new_repo=False,
266
force_new_tree=None, format=None):
267
"""Create a new BzrDir, Branch and Repository at the url 'base'.
269
This is a convenience function - it will use an existing repository
270
if possible, can be told explicitly whether to create a working tree or
273
This will use the current default BzrDirFormat, and use whatever
274
repository format that that uses via bzrdir.create_branch and
275
create_repository. If a shared repository is available that is used
276
preferentially. Whatever repository is used, its tree creation policy
279
The created Branch object is returned.
280
If a working tree cannot be made due to base not being a file:// url,
281
no error is raised unless force_new_tree is True, in which case no
282
data is created on disk and NotLocalUrl is raised.
284
:param base: The URL to create the branch at.
285
:param force_new_repo: If True a new repository is always created.
286
:param force_new_tree: If True or False force creation of a tree or
287
prevent such creation respectively.
288
:param format: Override for the for the bzrdir format to create
291
# check for non local urls
292
t = get_transport(safe_unicode(base))
293
if not isinstance(t, LocalTransport):
294
raise errors.NotLocalUrl(base)
295
bzrdir = BzrDir.create(base, format)
296
repo = bzrdir._find_or_create_repository(force_new_repo)
297
result = bzrdir.create_branch()
298
if force_new_tree or (repo.make_working_trees() and
299
force_new_tree is None):
301
bzrdir.create_workingtree()
302
except errors.NotLocalUrl:
307
def create_repository(base, shared=False, format=None):
308
"""Create a new BzrDir and Repository at the url 'base'.
310
If no format is supplied, this will default to the current default
311
BzrDirFormat by default, and use whatever repository format that that
312
uses for bzrdirformat.create_repository.
314
:param shared: Create a shared repository rather than a standalone
316
The Repository object is returned.
318
This must be overridden as an instance method in child classes, where
319
it should take no parameters and construct whatever repository format
320
that child class desires.
322
bzrdir = BzrDir.create(base, format)
323
return bzrdir.create_repository(shared)
326
def create_standalone_workingtree(base, format=None):
327
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
329
'base' must be a local path or a file:// url.
331
This will use the current default BzrDirFormat, and use whatever
332
repository format that that uses for bzrdirformat.create_workingtree,
333
create_branch and create_repository.
335
:return: The WorkingTree object.
337
t = get_transport(safe_unicode(base))
338
if not isinstance(t, LocalTransport):
339
raise errors.NotLocalUrl(base)
340
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
342
format=format).bzrdir
343
return bzrdir.create_workingtree()
345
def create_workingtree(self, revision_id=None):
346
"""Create a working tree at this BzrDir.
348
revision_id: create it as of this revision id.
350
raise NotImplementedError(self.create_workingtree)
352
def retire_bzrdir(self):
353
"""Permanently disable the bzrdir.
355
This is done by renaming it to give the user some ability to recover
356
if there was a problem.
358
This will have horrible consequences if anyone has anything locked or
361
for i in xrange(10000):
363
to_path = '.bzr.retired.%d' % i
364
self.root_transport.rename('.bzr', to_path)
365
note("renamed %s to %s"
366
% (self.root_transport.abspath('.bzr'), to_path))
368
except (errors.TransportError, IOError, errors.PathError):
371
def destroy_workingtree(self):
372
"""Destroy the working tree at this BzrDir.
374
Formats that do not support this may raise UnsupportedOperation.
376
raise NotImplementedError(self.destroy_workingtree)
378
def destroy_workingtree_metadata(self):
379
"""Destroy the control files for the working tree at this BzrDir.
381
The contents of working tree files are not affected.
382
Formats that do not support this may raise UnsupportedOperation.
384
raise NotImplementedError(self.destroy_workingtree_metadata)
386
def find_repository(self):
387
"""Find the repository that should be used for a_bzrdir.
389
This does not require a branch as we use it to find the repo for
390
new branches as well as to hook existing branches up to their
394
return self.open_repository()
395
except errors.NoRepositoryPresent:
397
next_transport = self.root_transport.clone('..')
399
# find the next containing bzrdir
401
found_bzrdir = BzrDir.open_containing_from_transport(
403
except errors.NotBranchError:
405
raise errors.NoRepositoryPresent(self)
406
# does it have a repository ?
408
repository = found_bzrdir.open_repository()
409
except errors.NoRepositoryPresent:
410
next_transport = found_bzrdir.root_transport.clone('..')
411
if (found_bzrdir.root_transport.base == next_transport.base):
412
# top of the file system
416
if ((found_bzrdir.root_transport.base ==
417
self.root_transport.base) or repository.is_shared()):
420
raise errors.NoRepositoryPresent(self)
421
raise errors.NoRepositoryPresent(self)
423
def get_branch_transport(self, branch_format):
424
"""Get the transport for use by branch format in this BzrDir.
426
Note that bzr dirs that do not support format strings will raise
427
IncompatibleFormat if the branch format they are given has
428
a format string, and vice versa.
430
If branch_format is None, the transport is returned with no
431
checking. if it is not None, then the returned transport is
432
guaranteed to point to an existing directory ready for use.
434
raise NotImplementedError(self.get_branch_transport)
436
def get_repository_transport(self, repository_format):
437
"""Get the transport for use by repository format in this BzrDir.
439
Note that bzr dirs that do not support format strings will raise
440
IncompatibleFormat if the repository format they are given has
441
a format string, and vice versa.
443
If repository_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_repository_transport)
449
def get_workingtree_transport(self, tree_format):
450
"""Get the transport for use by workingtree format in this BzrDir.
452
Note that bzr dirs that do not support format strings will raise
453
IncompatibleFormat if the workingtree format they are given has a
454
format string, and vice versa.
456
If workingtree_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_workingtree_transport)
462
def __init__(self, _transport, _format):
463
"""Initialize a Bzr control dir object.
465
Only really common logic should reside here, concrete classes should be
466
made with varying behaviours.
468
:param _format: the format that is creating this BzrDir instance.
469
:param _transport: the transport this dir is based at.
471
self._format = _format
472
self.transport = _transport.clone('.bzr')
473
self.root_transport = _transport
475
def is_control_filename(self, filename):
476
"""True if filename is the name of a path which is reserved for bzrdir's.
478
:param filename: A filename within the root transport of this bzrdir.
480
This is true IF and ONLY IF the filename is part of the namespace reserved
481
for bzr control dirs. Currently this is the '.bzr' directory in the root
482
of the root_transport. it is expected that plugins will need to extend
483
this in the future - for instance to make bzr talk with svn working
486
# this might be better on the BzrDirFormat class because it refers to
487
# all the possible bzrdir disk formats.
488
# This method is tested via the workingtree is_control_filename tests-
489
# it was extracted from WorkingTree.is_control_filename. If the methods
490
# contract is extended beyond the current trivial implementation please
491
# add new tests for it to the appropriate place.
492
return filename == '.bzr' or filename.startswith('.bzr/')
494
def needs_format_conversion(self, format=None):
495
"""Return true if this bzrdir needs convert_format run on it.
497
For instance, if the repository format is out of date but the
498
branch and working tree are not, this should return True.
500
:param format: Optional parameter indicating a specific desired
501
format we plan to arrive at.
503
raise NotImplementedError(self.needs_format_conversion)
506
def open_unsupported(base):
507
"""Open a branch which is not supported."""
508
return BzrDir.open(base, _unsupported=True)
511
def open(base, _unsupported=False):
512
"""Open an existing bzrdir, rooted at 'base' (url)
514
_unsupported is a private parameter to the BzrDir class.
516
t = get_transport(base)
517
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
520
def open_from_transport(transport, _unsupported=False):
521
"""Open a bzrdir within a particular directory.
523
:param transport: Transport containing the bzrdir.
524
:param _unsupported: private.
526
base = transport.base
528
def find_format(transport):
529
return transport, BzrDirFormat.find_format(transport)
531
def redirected(transport, e, redirection_notice):
532
qualified_source = e.get_source_url()
533
relpath = transport.relpath(qualified_source)
534
if not e.target.endswith(relpath):
535
# Not redirected to a branch-format, not a branch
536
raise errors.NotBranchError(path=e.target)
537
target = e.target[:-len(relpath)]
538
note('%s is%s redirected to %s',
539
transport.base, e.permanently, target)
540
# Let's try with a new transport
541
qualified_target = e.get_target_url()[:-len(relpath)]
542
# FIXME: If 'transport' has a qualifier, this should
543
# be applied again to the new transport *iff* the
544
# schemes used are the same. It's a bit tricky to
545
# verify, so I'll punt for now
547
return get_transport(target)
550
transport, format = do_catching_redirections(find_format,
553
except errors.TooManyRedirections:
554
raise errors.NotBranchError(base)
556
BzrDir._check_supported(format, _unsupported)
557
return format.open(transport, _found=True)
559
def open_branch(self, unsupported=False):
560
"""Open the branch object at this BzrDir if one is present.
562
If unsupported is True, then no longer supported branch formats can
565
TODO: static convenience version of this?
567
raise NotImplementedError(self.open_branch)
570
def open_containing(url):
571
"""Open an existing branch which contains url.
573
:param url: url to search from.
574
See open_containing_from_transport for more detail.
576
return BzrDir.open_containing_from_transport(get_transport(url))
579
def open_containing_from_transport(a_transport):
580
"""Open an existing branch which contains a_transport.base
582
This probes for a branch at a_transport, and searches upwards from there.
584
Basically we keep looking up until we find the control directory or
585
run into the root. If there isn't one, raises NotBranchError.
586
If there is one and it is either an unrecognised format or an unsupported
587
format, UnknownFormatError or UnsupportedFormatError are raised.
588
If there is one, it is returned, along with the unused portion of url.
590
:return: The BzrDir that contains the path, and a Unicode path
591
for the rest of the URL.
593
# this gets the normalised url back. I.e. '.' -> the full path.
594
url = a_transport.base
597
result = BzrDir.open_from_transport(a_transport)
598
return result, urlutils.unescape(a_transport.relpath(url))
599
except errors.NotBranchError, e:
601
new_t = a_transport.clone('..')
602
if new_t.base == a_transport.base:
603
# reached the root, whatever that may be
604
raise errors.NotBranchError(path=url)
608
def open_containing_tree_or_branch(klass, location):
609
"""Return the branch and working tree contained by a location.
611
Returns (tree, branch, relpath).
612
If there is no tree at containing the location, tree will be None.
613
If there is no branch containing the location, an exception will be
615
relpath is the portion of the path that is contained by the branch.
617
bzrdir, relpath = klass.open_containing(location)
619
tree = bzrdir.open_workingtree()
620
except (errors.NoWorkingTree, errors.NotLocalUrl):
622
branch = bzrdir.open_branch()
625
return tree, branch, relpath
627
def open_repository(self, _unsupported=False):
628
"""Open the repository object at this BzrDir if one is present.
630
This will not follow the Branch object pointer - its strictly a direct
631
open facility. Most client code should use open_branch().repository to
634
_unsupported is a private parameter, not part of the api.
635
TODO: static convenience version of this?
637
raise NotImplementedError(self.open_repository)
639
def open_workingtree(self, _unsupported=False,
640
recommend_upgrade=True):
641
"""Open the workingtree object at this BzrDir if one is present.
643
:param recommend_upgrade: Optional keyword parameter, when True (the
644
default), emit through the ui module a recommendation that the user
645
upgrade the working tree when the workingtree being opened is old
646
(but still fully supported).
648
raise NotImplementedError(self.open_workingtree)
650
def has_branch(self):
651
"""Tell if this bzrdir contains a branch.
653
Note: if you're going to open the branch, you should just go ahead
654
and try, and not ask permission first. (This method just opens the
655
branch and discards it, and that's somewhat expensive.)
660
except errors.NotBranchError:
663
def has_workingtree(self):
664
"""Tell if this bzrdir contains a working tree.
666
This will still raise an exception if the bzrdir has a workingtree that
667
is remote & inaccessible.
669
Note: if you're going to open the working tree, you should just go ahead
670
and try, and not ask permission first. (This method just opens the
671
workingtree and discards it, and that's somewhat expensive.)
674
self.open_workingtree(recommend_upgrade=False)
676
except errors.NoWorkingTree:
679
def _cloning_metadir(self):
680
result_format = self._format.__class__()
683
branch = self.open_branch()
684
source_repository = branch.repository
685
except errors.NotBranchError:
687
source_repository = self.open_repository()
688
result_format.repository_format = source_repository._format
689
except errors.NoRepositoryPresent:
690
source_repository = None
692
# TODO: Couldn't we just probe for the format in these cases,
693
# rather than opening the whole tree? It would be a little
694
# faster. mbp 20070401
695
tree = self.open_workingtree(recommend_upgrade=False)
696
except (errors.NoWorkingTree, errors.NotLocalUrl):
697
result_format.workingtree_format = None
699
result_format.workingtree_format = tree._format.__class__()
700
return result_format, source_repository
702
def cloning_metadir(self):
703
"""Produce a metadir suitable for cloning or sprouting with.
705
These operations may produce workingtrees (yes, even though they're
706
"cloning" something that doesn't have a tree, so a viable workingtree
707
format must be selected.
709
format, repository = self._cloning_metadir()
710
if format._workingtree_format is None:
711
if repository is None:
713
tree_format = repository._format._matchingbzrdir.workingtree_format
714
format.workingtree_format = tree_format.__class__()
717
def checkout_metadir(self):
718
return self.cloning_metadir()
720
def sprout(self, url, revision_id=None, force_new_repo=False,
722
"""Create a copy of this bzrdir prepared for use as a new line of
725
If urls last component does not exist, it will be created.
727
Attributes related to the identity of the source branch like
728
branch nickname will be cleaned, a working tree is created
729
whether one existed before or not; and a local branch is always
732
if revision_id is not None, then the clone operation may tune
733
itself to download less data.
736
cloning_format = self.cloning_metadir()
737
result = cloning_format.initialize(url)
739
source_branch = self.open_branch()
740
source_repository = source_branch.repository
741
except errors.NotBranchError:
744
source_repository = self.open_repository()
745
except errors.NoRepositoryPresent:
746
source_repository = None
751
result_repo = result.find_repository()
752
except errors.NoRepositoryPresent:
754
if source_repository is None and result_repo is not None:
756
elif source_repository is None and result_repo is None:
757
# no repo available, make a new one
758
result.create_repository()
759
elif source_repository is not None and result_repo is None:
760
# have source, and want to make a new target repo
761
# we don't clone the repo because that preserves attributes
762
# like is_shared(), and we have not yet implemented a
763
# repository sprout().
764
result_repo = result.create_repository()
765
if result_repo is not None:
766
# fetch needed content into target.
767
if source_repository is not None:
768
result_repo.fetch(source_repository, revision_id=revision_id)
769
if source_branch is not None:
770
source_branch.sprout(result, revision_id=revision_id)
772
result.create_branch()
773
# TODO: jam 20060426 we probably need a test in here in the
774
# case that the newly sprouted branch is a remote one
775
if result_repo is None or result_repo.make_working_trees():
776
wt = result.create_workingtree()
779
if wt.path2id('') is None:
781
wt.set_root_id(self.open_workingtree.get_root_id())
782
except errors.NoWorkingTree:
788
if recurse == 'down':
790
basis = wt.basis_tree()
792
subtrees = basis.iter_references()
793
recurse_branch = wt.branch
794
elif source_branch is not None:
795
basis = source_branch.basis_tree()
797
subtrees = basis.iter_references()
798
recurse_branch = source_branch
803
for path, file_id in subtrees:
804
target = urlutils.join(url, urlutils.escape(path))
805
sublocation = source_branch.reference_parent(file_id, path)
806
sublocation.bzrdir.sprout(target,
807
basis.get_reference_revision(file_id, path),
808
force_new_repo=force_new_repo, recurse=recurse)
810
if basis is not None:
815
class BzrDirPreSplitOut(BzrDir):
816
"""A common class for the all-in-one formats."""
818
def __init__(self, _transport, _format):
819
"""See BzrDir.__init__."""
820
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
821
assert self._format._lock_class == lockable_files.TransportLock
822
assert self._format._lock_file_name == 'branch-lock'
823
self._control_files = lockable_files.LockableFiles(
824
self.get_branch_transport(None),
825
self._format._lock_file_name,
826
self._format._lock_class)
828
def break_lock(self):
829
"""Pre-splitout bzrdirs do not suffer from stale locks."""
830
raise NotImplementedError(self.break_lock)
832
def clone(self, url, revision_id=None, force_new_repo=False):
833
"""See BzrDir.clone()."""
834
from bzrlib.workingtree import WorkingTreeFormat2
836
result = self._format._initialize_for_clone(url)
837
self.open_repository().clone(result, revision_id=revision_id)
838
from_branch = self.open_branch()
839
from_branch.clone(result, revision_id=revision_id)
841
self.open_workingtree().clone(result)
842
except errors.NotLocalUrl:
843
# make a new one, this format always has to have one.
845
WorkingTreeFormat2().initialize(result)
846
except errors.NotLocalUrl:
847
# but we cannot do it for remote trees.
848
to_branch = result.open_branch()
849
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
852
def create_branch(self):
853
"""See BzrDir.create_branch."""
854
return self.open_branch()
856
def create_repository(self, shared=False):
857
"""See BzrDir.create_repository."""
859
raise errors.IncompatibleFormat('shared repository', self._format)
860
return self.open_repository()
862
def create_workingtree(self, revision_id=None):
863
"""See BzrDir.create_workingtree."""
864
# this looks buggy but is not -really-
865
# because this format creates the workingtree when the bzrdir is
867
# clone and sprout will have set the revision_id
868
# and that will have set it for us, its only
869
# specific uses of create_workingtree in isolation
870
# that can do wonky stuff here, and that only
871
# happens for creating checkouts, which cannot be
872
# done on this format anyway. So - acceptable wart.
873
result = self.open_workingtree(recommend_upgrade=False)
874
if revision_id is not None:
875
if revision_id == _mod_revision.NULL_REVISION:
876
result.set_parent_ids([])
878
result.set_parent_ids([revision_id])
881
def destroy_workingtree(self):
882
"""See BzrDir.destroy_workingtree."""
883
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
885
def destroy_workingtree_metadata(self):
886
"""See BzrDir.destroy_workingtree_metadata."""
887
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
890
def get_branch_transport(self, branch_format):
891
"""See BzrDir.get_branch_transport()."""
892
if branch_format is None:
893
return self.transport
895
branch_format.get_format_string()
896
except NotImplementedError:
897
return self.transport
898
raise errors.IncompatibleFormat(branch_format, self._format)
900
def get_repository_transport(self, repository_format):
901
"""See BzrDir.get_repository_transport()."""
902
if repository_format is None:
903
return self.transport
905
repository_format.get_format_string()
906
except NotImplementedError:
907
return self.transport
908
raise errors.IncompatibleFormat(repository_format, self._format)
910
def get_workingtree_transport(self, workingtree_format):
911
"""See BzrDir.get_workingtree_transport()."""
912
if workingtree_format is None:
913
return self.transport
915
workingtree_format.get_format_string()
916
except NotImplementedError:
917
return self.transport
918
raise errors.IncompatibleFormat(workingtree_format, self._format)
920
def needs_format_conversion(self, format=None):
921
"""See BzrDir.needs_format_conversion()."""
922
# if the format is not the same as the system default,
923
# an upgrade is needed.
925
format = BzrDirFormat.get_default_format()
926
return not isinstance(self._format, format.__class__)
928
def open_branch(self, unsupported=False):
929
"""See BzrDir.open_branch."""
930
from bzrlib.branch import BzrBranchFormat4
931
format = BzrBranchFormat4()
932
self._check_supported(format, unsupported)
933
return format.open(self, _found=True)
935
def sprout(self, url, revision_id=None, force_new_repo=False):
936
"""See BzrDir.sprout()."""
937
from bzrlib.workingtree import WorkingTreeFormat2
939
result = self._format._initialize_for_clone(url)
941
self.open_repository().clone(result, revision_id=revision_id)
942
except errors.NoRepositoryPresent:
945
self.open_branch().sprout(result, revision_id=revision_id)
946
except errors.NotBranchError:
948
# we always want a working tree
949
WorkingTreeFormat2().initialize(result)
953
class BzrDir4(BzrDirPreSplitOut):
954
"""A .bzr version 4 control object.
956
This is a deprecated format and may be removed after sept 2006.
959
def create_repository(self, shared=False):
960
"""See BzrDir.create_repository."""
961
return self._format.repository_format.initialize(self, shared)
963
def needs_format_conversion(self, format=None):
964
"""Format 4 dirs are always in need of conversion."""
967
def open_repository(self):
968
"""See BzrDir.open_repository."""
969
from bzrlib.repofmt.weaverepo import RepositoryFormat4
970
return RepositoryFormat4().open(self, _found=True)
973
class BzrDir5(BzrDirPreSplitOut):
974
"""A .bzr version 5 control object.
976
This is a deprecated format and may be removed after sept 2006.
979
def open_repository(self):
980
"""See BzrDir.open_repository."""
981
from bzrlib.repofmt.weaverepo import RepositoryFormat5
982
return RepositoryFormat5().open(self, _found=True)
984
def open_workingtree(self, _unsupported=False,
985
recommend_upgrade=True):
986
"""See BzrDir.create_workingtree."""
987
from bzrlib.workingtree import WorkingTreeFormat2
988
wt_format = WorkingTreeFormat2()
989
# we don't warn here about upgrades; that ought to be handled for the
991
return wt_format.open(self, _found=True)
994
class BzrDir6(BzrDirPreSplitOut):
995
"""A .bzr version 6 control object.
997
This is a deprecated format and may be removed after sept 2006.
1000
def open_repository(self):
1001
"""See BzrDir.open_repository."""
1002
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1003
return RepositoryFormat6().open(self, _found=True)
1005
def open_workingtree(self, _unsupported=False,
1006
recommend_upgrade=True):
1007
"""See BzrDir.create_workingtree."""
1008
# we don't warn here about upgrades; that ought to be handled for the
1010
from bzrlib.workingtree import WorkingTreeFormat2
1011
return WorkingTreeFormat2().open(self, _found=True)
1014
class BzrDirMeta1(BzrDir):
1015
"""A .bzr meta version 1 control object.
1017
This is the first control object where the
1018
individual aspects are really split out: there are separate repository,
1019
workingtree and branch subdirectories and any subset of the three can be
1020
present within a BzrDir.
1023
def can_convert_format(self):
1024
"""See BzrDir.can_convert_format()."""
1027
def create_branch(self):
1028
"""See BzrDir.create_branch."""
1029
return self._format.get_branch_format().initialize(self)
1031
def create_repository(self, shared=False):
1032
"""See BzrDir.create_repository."""
1033
return self._format.repository_format.initialize(self, shared)
1035
def create_workingtree(self, revision_id=None):
1036
"""See BzrDir.create_workingtree."""
1037
from bzrlib.workingtree import WorkingTreeFormat
1038
return self._format.workingtree_format.initialize(self, revision_id)
1040
def destroy_workingtree(self):
1041
"""See BzrDir.destroy_workingtree."""
1042
wt = self.open_workingtree(recommend_upgrade=False)
1043
repository = wt.branch.repository
1044
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1045
wt.revert([], old_tree=empty)
1046
self.destroy_workingtree_metadata()
1048
def destroy_workingtree_metadata(self):
1049
self.transport.delete_tree('checkout')
1051
def _get_mkdir_mode(self):
1052
"""Figure out the mode to use when creating a bzrdir subdir."""
1053
temp_control = lockable_files.LockableFiles(self.transport, '',
1054
lockable_files.TransportLock)
1055
return temp_control._dir_mode
1057
def get_branch_transport(self, branch_format):
1058
"""See BzrDir.get_branch_transport()."""
1059
if branch_format is None:
1060
return self.transport.clone('branch')
1062
branch_format.get_format_string()
1063
except NotImplementedError:
1064
raise errors.IncompatibleFormat(branch_format, self._format)
1066
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1067
except errors.FileExists:
1069
return self.transport.clone('branch')
1071
def get_repository_transport(self, repository_format):
1072
"""See BzrDir.get_repository_transport()."""
1073
if repository_format is None:
1074
return self.transport.clone('repository')
1076
repository_format.get_format_string()
1077
except NotImplementedError:
1078
raise errors.IncompatibleFormat(repository_format, self._format)
1080
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1081
except errors.FileExists:
1083
return self.transport.clone('repository')
1085
def get_workingtree_transport(self, workingtree_format):
1086
"""See BzrDir.get_workingtree_transport()."""
1087
if workingtree_format is None:
1088
return self.transport.clone('checkout')
1090
workingtree_format.get_format_string()
1091
except NotImplementedError:
1092
raise errors.IncompatibleFormat(workingtree_format, self._format)
1094
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1095
except errors.FileExists:
1097
return self.transport.clone('checkout')
1099
def needs_format_conversion(self, format=None):
1100
"""See BzrDir.needs_format_conversion()."""
1102
format = BzrDirFormat.get_default_format()
1103
if not isinstance(self._format, format.__class__):
1104
# it is not a meta dir format, conversion is needed.
1106
# we might want to push this down to the repository?
1108
if not isinstance(self.open_repository()._format,
1109
format.repository_format.__class__):
1110
# the repository needs an upgrade.
1112
except errors.NoRepositoryPresent:
1115
if not isinstance(self.open_branch()._format,
1116
format.get_branch_format().__class__):
1117
# the branch needs an upgrade.
1119
except errors.NotBranchError:
1122
my_wt = self.open_workingtree(recommend_upgrade=False)
1123
if not isinstance(my_wt._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
from bzrlib.branch import BranchFormat
1134
format = BranchFormat.find_format(self)
1135
self._check_supported(format, unsupported)
1136
return format.open(self, _found=True)
1138
def open_repository(self, unsupported=False):
1139
"""See BzrDir.open_repository."""
1140
from bzrlib.repository import RepositoryFormat
1141
format = RepositoryFormat.find_format(self)
1142
self._check_supported(format, unsupported)
1143
return format.open(self, _found=True)
1145
def open_workingtree(self, unsupported=False,
1146
recommend_upgrade=True):
1147
"""See BzrDir.open_workingtree."""
1148
from bzrlib.workingtree import WorkingTreeFormat
1149
format = WorkingTreeFormat.find_format(self)
1150
self._check_supported(format, unsupported,
1152
basedir=self.root_transport.base)
1153
return format.open(self, _found=True)
1156
class BzrDirFormat(object):
1157
"""An encapsulation of the initialization and open routines for a format.
1159
Formats provide three things:
1160
* An initialization routine,
1164
Formats are placed in an dict by their format string for reference
1165
during bzrdir opening. These should be subclasses of BzrDirFormat
1168
Once a format is deprecated, just deprecate the initialize and open
1169
methods on the format class. Do not deprecate the object, as the
1170
object will be created every system load.
1173
_default_format = None
1174
"""The default format used for new .bzr dirs."""
1177
"""The known formats."""
1179
_control_formats = []
1180
"""The registered control formats - .bzr, ....
1182
This is a list of BzrDirFormat objects.
1185
_lock_file_name = 'branch-lock'
1187
# _lock_class must be set in subclasses to the lock type, typ.
1188
# TransportLock or LockDir
1191
def find_format(klass, transport):
1192
"""Return the format present at transport."""
1193
for format in klass._control_formats:
1195
return format.probe_transport(transport)
1196
except errors.NotBranchError:
1197
# this format does not find a control dir here.
1199
raise errors.NotBranchError(path=transport.base)
1202
def probe_transport(klass, transport):
1203
"""Return the .bzrdir style transport present at URL."""
1205
format_string = transport.get(".bzr/branch-format").read()
1206
except errors.NoSuchFile:
1207
raise errors.NotBranchError(path=transport.base)
1210
return klass._formats[format_string]
1212
raise errors.UnknownFormatError(format=format_string)
1215
def get_default_format(klass):
1216
"""Return the current default format."""
1217
return klass._default_format
1219
def get_format_string(self):
1220
"""Return the ASCII format string that identifies this format."""
1221
raise NotImplementedError(self.get_format_string)
1223
def get_format_description(self):
1224
"""Return the short description for this format."""
1225
raise NotImplementedError(self.get_format_description)
1227
def get_converter(self, format=None):
1228
"""Return the converter to use to convert bzrdirs needing converts.
1230
This returns a bzrlib.bzrdir.Converter object.
1232
This should return the best upgrader to step this format towards the
1233
current default format. In the case of plugins we can/should provide
1234
some means for them to extend the range of returnable converters.
1236
:param format: Optional format to override the default format of the
1239
raise NotImplementedError(self.get_converter)
1241
def initialize(self, url):
1242
"""Create a bzr control dir at this url and return an opened copy.
1244
Subclasses should typically override initialize_on_transport
1245
instead of this method.
1247
return self.initialize_on_transport(get_transport(url))
1249
def initialize_on_transport(self, transport):
1250
"""Initialize a new bzrdir in the base directory of a Transport."""
1251
# Since we don't have a .bzr directory, inherit the
1252
# mode from the root directory
1253
temp_control = lockable_files.LockableFiles(transport,
1254
'', lockable_files.TransportLock)
1255
temp_control._transport.mkdir('.bzr',
1256
# FIXME: RBC 20060121 don't peek under
1258
mode=temp_control._dir_mode)
1259
file_mode = temp_control._file_mode
1261
mutter('created control directory in ' + transport.base)
1262
control = transport.clone('.bzr')
1263
utf8_files = [('README',
1264
"This is a Bazaar-NG control directory.\n"
1265
"Do not change any files in this directory.\n"),
1266
('branch-format', self.get_format_string()),
1268
# NB: no need to escape relative paths that are url safe.
1269
control_files = lockable_files.LockableFiles(control,
1270
self._lock_file_name, self._lock_class)
1271
control_files.create_lock()
1272
control_files.lock_write()
1274
for file, content in utf8_files:
1275
control_files.put_utf8(file, content)
1277
control_files.unlock()
1278
return self.open(transport, _found=True)
1280
def is_supported(self):
1281
"""Is this format supported?
1283
Supported formats must be initializable and openable.
1284
Unsupported formats may not support initialization or committing or
1285
some other features depending on the reason for not being supported.
1289
def same_model(self, target_format):
1290
return (self.repository_format.rich_root_data ==
1291
target_format.rich_root_data)
1294
def known_formats(klass):
1295
"""Return all the known formats.
1297
Concrete formats should override _known_formats.
1299
# There is double indirection here to make sure that control
1300
# formats used by more than one dir format will only be probed
1301
# once. This can otherwise be quite expensive for remote connections.
1303
for format in klass._control_formats:
1304
result.update(format._known_formats())
1308
def _known_formats(klass):
1309
"""Return the known format instances for this control format."""
1310
return set(klass._formats.values())
1312
def open(self, transport, _found=False):
1313
"""Return an instance of this format for the dir transport points at.
1315
_found is a private parameter, do not use it.
1318
found_format = BzrDirFormat.find_format(transport)
1319
if not isinstance(found_format, self.__class__):
1320
raise AssertionError("%s was asked to open %s, but it seems to need "
1322
% (self, transport, found_format))
1323
return self._open(transport)
1325
def _open(self, transport):
1326
"""Template method helper for opening BzrDirectories.
1328
This performs the actual open and any additional logic or parameter
1331
raise NotImplementedError(self._open)
1334
def register_format(klass, format):
1335
klass._formats[format.get_format_string()] = format
1338
def register_control_format(klass, format):
1339
"""Register a format that does not use '.bzr' for its control dir.
1341
TODO: This should be pulled up into a 'ControlDirFormat' base class
1342
which BzrDirFormat can inherit from, and renamed to register_format
1343
there. It has been done without that for now for simplicity of
1346
klass._control_formats.append(format)
1349
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1350
def set_default_format(klass, format):
1351
klass._set_default_format(format)
1354
def _set_default_format(klass, format):
1355
"""Set default format (for testing behavior of defaults only)"""
1356
klass._default_format = format
1359
return self.get_format_string()[:-1]
1362
def unregister_format(klass, format):
1363
assert klass._formats[format.get_format_string()] is format
1364
del klass._formats[format.get_format_string()]
1367
def unregister_control_format(klass, format):
1368
klass._control_formats.remove(format)
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
# Register bzr control format
1628
BzrDirFormat.register_control_format(BzrDirFormat)
1630
# Register bzr formats
1631
BzrDirFormat.register_format(BzrDirFormat4())
1632
BzrDirFormat.register_format(BzrDirFormat5())
1633
BzrDirFormat.register_format(BzrDirFormat6())
1634
__default_format = BzrDirMetaFormat1()
1635
BzrDirFormat.register_format(__default_format)
1636
BzrDirFormat._default_format = __default_format
1639
class BzrDirTestProviderAdapter(object):
1640
"""A tool to generate a suite testing multiple bzrdir formats at once.
1642
This is done by copying the test once for each transport and injecting
1643
the transport_server, transport_readonly_server, and bzrdir_format
1644
classes into each copy. Each copy is also given a new id() to make it
1648
def __init__(self, transport_server, transport_readonly_server, formats):
1649
self._transport_server = transport_server
1650
self._transport_readonly_server = transport_readonly_server
1651
self._formats = formats
1653
def adapt(self, test):
1654
result = unittest.TestSuite()
1655
for format in self._formats:
1656
new_test = deepcopy(test)
1657
new_test.transport_server = self._transport_server
1658
new_test.transport_readonly_server = self._transport_readonly_server
1659
new_test.bzrdir_format = format
1660
def make_new_test_id():
1661
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1662
return lambda: new_id
1663
new_test.id = make_new_test_id()
1664
result.addTest(new_test)
1668
class Converter(object):
1669
"""Converts a disk format object from one format to another."""
1671
def convert(self, to_convert, pb):
1672
"""Perform the conversion of to_convert, giving feedback via pb.
1674
:param to_convert: The disk object to convert.
1675
:param pb: a progress bar to use for progress information.
1678
def step(self, message):
1679
"""Update the pb by a step."""
1681
self.pb.update(message, self.count, self.total)
1684
class ConvertBzrDir4To5(Converter):
1685
"""Converts format 4 bzr dirs to format 5."""
1688
super(ConvertBzrDir4To5, self).__init__()
1689
self.converted_revs = set()
1690
self.absent_revisions = set()
1694
def convert(self, to_convert, pb):
1695
"""See Converter.convert()."""
1696
self.bzrdir = to_convert
1698
self.pb.note('starting upgrade from format 4 to 5')
1699
if isinstance(self.bzrdir.transport, LocalTransport):
1700
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1701
self._convert_to_weaves()
1702
return BzrDir.open(self.bzrdir.root_transport.base)
1704
def _convert_to_weaves(self):
1705
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1708
stat = self.bzrdir.transport.stat('weaves')
1709
if not S_ISDIR(stat.st_mode):
1710
self.bzrdir.transport.delete('weaves')
1711
self.bzrdir.transport.mkdir('weaves')
1712
except errors.NoSuchFile:
1713
self.bzrdir.transport.mkdir('weaves')
1714
# deliberately not a WeaveFile as we want to build it up slowly.
1715
self.inv_weave = Weave('inventory')
1716
# holds in-memory weaves for all files
1717
self.text_weaves = {}
1718
self.bzrdir.transport.delete('branch-format')
1719
self.branch = self.bzrdir.open_branch()
1720
self._convert_working_inv()
1721
rev_history = self.branch.revision_history()
1722
# to_read is a stack holding the revisions we still need to process;
1723
# appending to it adds new highest-priority revisions
1724
self.known_revisions = set(rev_history)
1725
self.to_read = rev_history[-1:]
1727
rev_id = self.to_read.pop()
1728
if (rev_id not in self.revisions
1729
and rev_id not in self.absent_revisions):
1730
self._load_one_rev(rev_id)
1732
to_import = self._make_order()
1733
for i, rev_id in enumerate(to_import):
1734
self.pb.update('converting revision', i, len(to_import))
1735
self._convert_one_rev(rev_id)
1737
self._write_all_weaves()
1738
self._write_all_revs()
1739
self.pb.note('upgraded to weaves:')
1740
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1741
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1742
self.pb.note(' %6d texts', self.text_count)
1743
self._cleanup_spare_files_after_format4()
1744
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1746
def _cleanup_spare_files_after_format4(self):
1747
# FIXME working tree upgrade foo.
1748
for n in 'merged-patches', 'pending-merged-patches':
1750
## assert os.path.getsize(p) == 0
1751
self.bzrdir.transport.delete(n)
1752
except errors.NoSuchFile:
1754
self.bzrdir.transport.delete_tree('inventory-store')
1755
self.bzrdir.transport.delete_tree('text-store')
1757
def _convert_working_inv(self):
1758
inv = xml4.serializer_v4.read_inventory(
1759
self.branch.control_files.get('inventory'))
1760
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1761
# FIXME inventory is a working tree change.
1762
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1764
def _write_all_weaves(self):
1765
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1766
weave_transport = self.bzrdir.transport.clone('weaves')
1767
weaves = WeaveStore(weave_transport, prefixed=False)
1768
transaction = WriteTransaction()
1772
for file_id, file_weave in self.text_weaves.items():
1773
self.pb.update('writing weave', i, len(self.text_weaves))
1774
weaves._put_weave(file_id, file_weave, transaction)
1776
self.pb.update('inventory', 0, 1)
1777
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1778
self.pb.update('inventory', 1, 1)
1782
def _write_all_revs(self):
1783
"""Write all revisions out in new form."""
1784
self.bzrdir.transport.delete_tree('revision-store')
1785
self.bzrdir.transport.mkdir('revision-store')
1786
revision_transport = self.bzrdir.transport.clone('revision-store')
1788
_revision_store = TextRevisionStore(TextStore(revision_transport,
1792
transaction = WriteTransaction()
1793
for i, rev_id in enumerate(self.converted_revs):
1794
self.pb.update('write revision', i, len(self.converted_revs))
1795
_revision_store.add_revision(self.revisions[rev_id], transaction)
1799
def _load_one_rev(self, rev_id):
1800
"""Load a revision object into memory.
1802
Any parents not either loaded or abandoned get queued to be
1804
self.pb.update('loading revision',
1805
len(self.revisions),
1806
len(self.known_revisions))
1807
if not self.branch.repository.has_revision(rev_id):
1809
self.pb.note('revision {%s} not present in branch; '
1810
'will be converted as a ghost',
1812
self.absent_revisions.add(rev_id)
1814
rev = self.branch.repository._revision_store.get_revision(rev_id,
1815
self.branch.repository.get_transaction())
1816
for parent_id in rev.parent_ids:
1817
self.known_revisions.add(parent_id)
1818
self.to_read.append(parent_id)
1819
self.revisions[rev_id] = rev
1821
def _load_old_inventory(self, rev_id):
1822
assert rev_id not in self.converted_revs
1823
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1824
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1825
inv.revision_id = rev_id
1826
rev = self.revisions[rev_id]
1827
if rev.inventory_sha1:
1828
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1829
'inventory sha mismatch for {%s}' % rev_id
1832
def _load_updated_inventory(self, rev_id):
1833
assert rev_id in self.converted_revs
1834
inv_xml = self.inv_weave.get_text(rev_id)
1835
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1838
def _convert_one_rev(self, rev_id):
1839
"""Convert revision and all referenced objects to new format."""
1840
rev = self.revisions[rev_id]
1841
inv = self._load_old_inventory(rev_id)
1842
present_parents = [p for p in rev.parent_ids
1843
if p not in self.absent_revisions]
1844
self._convert_revision_contents(rev, inv, present_parents)
1845
self._store_new_weave(rev, inv, present_parents)
1846
self.converted_revs.add(rev_id)
1848
def _store_new_weave(self, rev, inv, present_parents):
1849
# the XML is now updated with text versions
1851
entries = inv.iter_entries()
1853
for path, ie in entries:
1854
assert getattr(ie, 'revision', None) is not None, \
1855
'no revision on {%s} in {%s}' % \
1856
(file_id, rev.revision_id)
1857
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1858
new_inv_sha1 = sha_string(new_inv_xml)
1859
self.inv_weave.add_lines(rev.revision_id,
1861
new_inv_xml.splitlines(True))
1862
rev.inventory_sha1 = new_inv_sha1
1864
def _convert_revision_contents(self, rev, inv, present_parents):
1865
"""Convert all the files within a revision.
1867
Also upgrade the inventory to refer to the text revision ids."""
1868
rev_id = rev.revision_id
1869
mutter('converting texts of revision {%s}',
1871
parent_invs = map(self._load_updated_inventory, present_parents)
1872
entries = inv.iter_entries()
1874
for path, ie in entries:
1875
self._convert_file_version(rev, ie, parent_invs)
1877
def _convert_file_version(self, rev, ie, parent_invs):
1878
"""Convert one version of one file.
1880
The file needs to be added into the weave if it is a merge
1881
of >=2 parents or if it's changed from its parent.
1883
file_id = ie.file_id
1884
rev_id = rev.revision_id
1885
w = self.text_weaves.get(file_id)
1888
self.text_weaves[file_id] = w
1889
text_changed = False
1890
previous_entries = ie.find_previous_heads(parent_invs,
1894
for old_revision in previous_entries:
1895
# if this fails, its a ghost ?
1896
assert old_revision in self.converted_revs, \
1897
"Revision {%s} not in converted_revs" % old_revision
1898
self.snapshot_ie(previous_entries, ie, w, rev_id)
1900
assert getattr(ie, 'revision', None) is not None
1902
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1903
# TODO: convert this logic, which is ~= snapshot to
1904
# a call to:. This needs the path figured out. rather than a work_tree
1905
# a v4 revision_tree can be given, or something that looks enough like
1906
# one to give the file content to the entry if it needs it.
1907
# and we need something that looks like a weave store for snapshot to
1909
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1910
if len(previous_revisions) == 1:
1911
previous_ie = previous_revisions.values()[0]
1912
if ie._unchanged(previous_ie):
1913
ie.revision = previous_ie.revision
1916
text = self.branch.repository.text_store.get(ie.text_id)
1917
file_lines = text.readlines()
1918
assert sha_strings(file_lines) == ie.text_sha1
1919
assert sum(map(len, file_lines)) == ie.text_size
1920
w.add_lines(rev_id, previous_revisions, file_lines)
1921
self.text_count += 1
1923
w.add_lines(rev_id, previous_revisions, [])
1924
ie.revision = rev_id
1926
def _make_order(self):
1927
"""Return a suitable order for importing revisions.
1929
The order must be such that an revision is imported after all
1930
its (present) parents.
1932
todo = set(self.revisions.keys())
1933
done = self.absent_revisions.copy()
1936
# scan through looking for a revision whose parents
1938
for rev_id in sorted(list(todo)):
1939
rev = self.revisions[rev_id]
1940
parent_ids = set(rev.parent_ids)
1941
if parent_ids.issubset(done):
1942
# can take this one now
1943
order.append(rev_id)
1949
class ConvertBzrDir5To6(Converter):
1950
"""Converts format 5 bzr dirs to format 6."""
1952
def convert(self, to_convert, pb):
1953
"""See Converter.convert()."""
1954
self.bzrdir = to_convert
1956
self.pb.note('starting upgrade from format 5 to 6')
1957
self._convert_to_prefixed()
1958
return BzrDir.open(self.bzrdir.root_transport.base)
1960
def _convert_to_prefixed(self):
1961
from bzrlib.store import TransportStore
1962
self.bzrdir.transport.delete('branch-format')
1963
for store_name in ["weaves", "revision-store"]:
1964
self.pb.note("adding prefixes to %s" % store_name)
1965
store_transport = self.bzrdir.transport.clone(store_name)
1966
store = TransportStore(store_transport, prefixed=True)
1967
for urlfilename in store_transport.list_dir('.'):
1968
filename = urlutils.unescape(urlfilename)
1969
if (filename.endswith(".weave") or
1970
filename.endswith(".gz") or
1971
filename.endswith(".sig")):
1972
file_id = os.path.splitext(filename)[0]
1975
prefix_dir = store.hash_prefix(file_id)
1976
# FIXME keep track of the dirs made RBC 20060121
1978
store_transport.move(filename, prefix_dir + '/' + filename)
1979
except errors.NoSuchFile: # catches missing dirs strangely enough
1980
store_transport.mkdir(prefix_dir)
1981
store_transport.move(filename, prefix_dir + '/' + filename)
1982
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1985
class ConvertBzrDir6ToMeta(Converter):
1986
"""Converts format 6 bzr dirs to metadirs."""
1988
def convert(self, to_convert, pb):
1989
"""See Converter.convert()."""
1990
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1991
from bzrlib.branch import BzrBranchFormat5
1992
self.bzrdir = to_convert
1995
self.total = 20 # the steps we know about
1996
self.garbage_inventories = []
1998
self.pb.note('starting upgrade from format 6 to metadir')
1999
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2000
# its faster to move specific files around than to open and use the apis...
2001
# first off, nuke ancestry.weave, it was never used.
2003
self.step('Removing ancestry.weave')
2004
self.bzrdir.transport.delete('ancestry.weave')
2005
except errors.NoSuchFile:
2007
# find out whats there
2008
self.step('Finding branch files')
2009
last_revision = self.bzrdir.open_branch().last_revision()
2010
bzrcontents = self.bzrdir.transport.list_dir('.')
2011
for name in bzrcontents:
2012
if name.startswith('basis-inventory.'):
2013
self.garbage_inventories.append(name)
2014
# create new directories for repository, working tree and branch
2015
self.dir_mode = self.bzrdir._control_files._dir_mode
2016
self.file_mode = self.bzrdir._control_files._file_mode
2017
repository_names = [('inventory.weave', True),
2018
('revision-store', True),
2020
self.step('Upgrading repository ')
2021
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2022
self.make_lock('repository')
2023
# we hard code the formats here because we are converting into
2024
# the meta format. The meta format upgrader can take this to a
2025
# future format within each component.
2026
self.put_format('repository', RepositoryFormat7())
2027
for entry in repository_names:
2028
self.move_entry('repository', entry)
2030
self.step('Upgrading branch ')
2031
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2032
self.make_lock('branch')
2033
self.put_format('branch', BzrBranchFormat5())
2034
branch_files = [('revision-history', True),
2035
('branch-name', True),
2037
for entry in branch_files:
2038
self.move_entry('branch', entry)
2040
checkout_files = [('pending-merges', True),
2041
('inventory', True),
2042
('stat-cache', False)]
2043
# If a mandatory checkout file is not present, the branch does not have
2044
# a functional checkout. Do not create a checkout in the converted
2046
for name, mandatory in checkout_files:
2047
if mandatory and name not in bzrcontents:
2048
has_checkout = False
2052
if not has_checkout:
2053
self.pb.note('No working tree.')
2054
# If some checkout files are there, we may as well get rid of them.
2055
for name, mandatory in checkout_files:
2056
if name in bzrcontents:
2057
self.bzrdir.transport.delete(name)
2059
from bzrlib.workingtree import WorkingTreeFormat3
2060
self.step('Upgrading working tree')
2061
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2062
self.make_lock('checkout')
2064
'checkout', WorkingTreeFormat3())
2065
self.bzrdir.transport.delete_multi(
2066
self.garbage_inventories, self.pb)
2067
for entry in checkout_files:
2068
self.move_entry('checkout', entry)
2069
if last_revision is not None:
2070
self.bzrdir._control_files.put_utf8(
2071
'checkout/last-revision', last_revision)
2072
self.bzrdir._control_files.put_utf8(
2073
'branch-format', BzrDirMetaFormat1().get_format_string())
2074
return BzrDir.open(self.bzrdir.root_transport.base)
2076
def make_lock(self, name):
2077
"""Make a lock for the new control dir name."""
2078
self.step('Make %s lock' % name)
2079
ld = lockdir.LockDir(self.bzrdir.transport,
2081
file_modebits=self.file_mode,
2082
dir_modebits=self.dir_mode)
2085
def move_entry(self, new_dir, entry):
2086
"""Move then entry name into new_dir."""
2088
mandatory = entry[1]
2089
self.step('Moving %s' % name)
2091
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2092
except errors.NoSuchFile:
2096
def put_format(self, dirname, format):
2097
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2100
class ConvertMetaToMeta(Converter):
2101
"""Converts the components of metadirs."""
2103
def __init__(self, target_format):
2104
"""Create a metadir to metadir converter.
2106
:param target_format: The final metadir format that is desired.
2108
self.target_format = target_format
2110
def convert(self, to_convert, pb):
2111
"""See Converter.convert()."""
2112
self.bzrdir = to_convert
2116
self.step('checking repository format')
2118
repo = self.bzrdir.open_repository()
2119
except errors.NoRepositoryPresent:
2122
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2123
from bzrlib.repository import CopyConverter
2124
self.pb.note('starting repository conversion')
2125
converter = CopyConverter(self.target_format.repository_format)
2126
converter.convert(repo, pb)
2128
branch = self.bzrdir.open_branch()
2129
except errors.NotBranchError:
2132
# TODO: conversions of Branch and Tree should be done by
2133
# InterXFormat lookups
2134
# Avoid circular imports
2135
from bzrlib import branch as _mod_branch
2136
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2137
self.target_format.get_branch_format().__class__ is
2138
_mod_branch.BzrBranchFormat6):
2139
branch_converter = _mod_branch.Converter5to6()
2140
branch_converter.convert(branch)
2142
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2143
except (errors.NoWorkingTree, errors.NotLocalUrl):
2146
# TODO: conversions of Branch and Tree should be done by
2147
# InterXFormat lookups
2148
if (isinstance(tree, workingtree.WorkingTree3) and
2149
not isinstance(tree, workingtree_4.WorkingTree4) and
2150
isinstance(self.target_format.workingtree_format,
2151
workingtree_4.WorkingTreeFormat4)):
2152
workingtree_4.Converter3to4().convert(tree)
2156
class BzrDirFormatInfo(object):
2158
def __init__(self, native, deprecated, hidden):
2159
self.deprecated = deprecated
2160
self.native = native
2161
self.hidden = hidden
2164
class BzrDirFormatRegistry(registry.Registry):
2165
"""Registry of user-selectable BzrDir subformats.
2167
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2168
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2171
def register_metadir(self, key,
2172
repository_format, help, native=True, deprecated=False,
2176
"""Register a metadir subformat.
2178
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2179
by the Repository format.
2181
:param repository_format: The fully-qualified repository format class
2183
:param branch_format: Fully-qualified branch format class name as
2185
:param tree_format: Fully-qualified tree format class name as
2188
# This should be expanded to support setting WorkingTree and Branch
2189
# formats, once BzrDirMetaFormat1 supports that.
2190
def _load(full_name):
2191
mod_name, factory_name = full_name.rsplit('.', 1)
2193
mod = __import__(mod_name, globals(), locals(),
2195
except ImportError, e:
2196
raise ImportError('failed to load %s: %s' % (full_name, e))
2198
factory = getattr(mod, factory_name)
2199
except AttributeError:
2200
raise AttributeError('no factory %s in module %r'
2205
bd = BzrDirMetaFormat1()
2206
if branch_format is not None:
2207
bd.set_branch_format(_load(branch_format))
2208
if tree_format is not None:
2209
bd.workingtree_format = _load(tree_format)
2210
if repository_format is not None:
2211
bd.repository_format = _load(repository_format)
2213
self.register(key, helper, help, native, deprecated, hidden)
2215
def register(self, key, factory, help, native=True, deprecated=False,
2217
"""Register a BzrDirFormat factory.
2219
The factory must be a callable that takes one parameter: the key.
2220
It must produce an instance of the BzrDirFormat when called.
2222
This function mainly exists to prevent the info object from being
2225
registry.Registry.register(self, key, factory, help,
2226
BzrDirFormatInfo(native, deprecated, hidden))
2228
def register_lazy(self, key, module_name, member_name, help, native=True,
2229
deprecated=False, hidden=False):
2230
registry.Registry.register_lazy(self, key, module_name, member_name,
2231
help, BzrDirFormatInfo(native, deprecated, hidden))
2233
def set_default(self, key):
2234
"""Set the 'default' key to be a clone of the supplied key.
2236
This method must be called once and only once.
2238
registry.Registry.register(self, 'default', self.get(key),
2239
self.get_help(key), info=self.get_info(key))
2241
def set_default_repository(self, key):
2242
"""Set the FormatRegistry default and Repository default.
2244
This is a transitional method while Repository.set_default_format
2247
if 'default' in self:
2248
self.remove('default')
2249
self.set_default(key)
2250
format = self.get('default')()
2251
assert isinstance(format, BzrDirMetaFormat1)
2253
def make_bzrdir(self, key):
2254
return self.get(key)()
2256
def help_topic(self, topic):
2257
output = textwrap.dedent("""\
2258
Bazaar directory formats
2259
------------------------
2261
These formats can be used for creating branches, working trees, and
2265
default_help = self.get_help('default')
2267
for key in self.keys():
2268
if key == 'default':
2270
help = self.get_help(key)
2271
if help == default_help:
2272
default_realkey = key
2274
help_pairs.append((key, help))
2276
def wrapped(key, help, info):
2278
help = '(native) ' + help
2279
return ' %s:\n%s\n\n' % (key,
2280
textwrap.fill(help, initial_indent=' ',
2281
subsequent_indent=' '))
2282
output += wrapped('%s/default' % default_realkey, default_help,
2283
self.get_info('default'))
2284
deprecated_pairs = []
2285
for key, help in help_pairs:
2286
info = self.get_info(key)
2289
elif info.deprecated:
2290
deprecated_pairs.append((key, help))
2292
output += wrapped(key, help, info)
2293
if len(deprecated_pairs) > 0:
2294
output += "Deprecated formats\n------------------\n\n"
2295
for key, help in deprecated_pairs:
2296
info = self.get_info(key)
2297
output += wrapped(key, help, info)
2302
format_registry = BzrDirFormatRegistry()
2303
format_registry.register('weave', BzrDirFormat6,
2304
'Pre-0.8 format. Slower than knit and does not'
2305
' support checkouts or shared repositories.',
2307
format_registry.register_metadir('knit',
2308
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2309
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2310
branch_format='bzrlib.branch.BzrBranchFormat5',
2311
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2312
format_registry.register_metadir('metaweave',
2313
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2314
'Transitional format in 0.8. Slower than knit.',
2315
branch_format='bzrlib.branch.BzrBranchFormat5',
2316
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2318
format_registry.register_metadir('dirstate',
2319
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2320
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2321
'above when accessed over the network.',
2322
branch_format='bzrlib.branch.BzrBranchFormat5',
2323
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2324
# directly from workingtree_4 triggers a circular import.
2325
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2327
format_registry.register_metadir('dirstate-tags',
2328
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2329
help='New in 0.15: Fast local operations and improved scaling for '
2330
'network operations. Additionally adds support for tags.'
2331
' Incompatible with bzr < 0.15.',
2332
branch_format='bzrlib.branch.BzrBranchFormat6',
2333
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2335
format_registry.register_metadir('dirstate-with-subtree',
2336
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2337
help='New in 0.15: Fast local operations and improved scaling for '
2338
'network operations. Additionally adds support for versioning nested '
2339
'bzr branches. Incompatible with bzr < 0.15.',
2340
branch_format='bzrlib.branch.BzrBranchFormat6',
2341
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2344
format_registry.set_default('dirstate')