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,
54
from bzrlib.osutils import (
59
from bzrlib.smart.client import _SmartClient
60
from bzrlib.store.revision.text import TextRevisionStore
61
from bzrlib.store.text import TextStore
62
from bzrlib.store.versioned import WeaveStore
63
from bzrlib.transactions import WriteTransaction
64
from bzrlib.transport import (
65
do_catching_redirections,
68
from bzrlib.weave import Weave
71
from bzrlib.trace import (
75
from bzrlib.transport.local import LocalTransport
79
"""A .bzr control diretory.
81
BzrDir instances let you create or open any of the things that can be
82
found within .bzr - checkouts, branches and repositories.
85
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
87
a transport connected to the directory this bzr was opened from.
91
"""Invoke break_lock on the first object in the bzrdir.
93
If there is a tree, the tree is opened and break_lock() called.
94
Otherwise, branch is tried, and finally repository.
96
# XXX: This seems more like a UI function than something that really
97
# belongs in this class.
99
thing_to_unlock = self.open_workingtree()
100
except (errors.NotLocalUrl, errors.NoWorkingTree):
102
thing_to_unlock = self.open_branch()
103
except errors.NotBranchError:
105
thing_to_unlock = self.open_repository()
106
except errors.NoRepositoryPresent:
108
thing_to_unlock.break_lock()
110
def can_convert_format(self):
111
"""Return true if this bzrdir is one whose format we can convert from."""
114
def check_conversion_target(self, target_format):
115
target_repo_format = target_format.repository_format
116
source_repo_format = self._format.repository_format
117
source_repo_format.check_conversion_target(target_repo_format)
120
def _check_supported(format, allow_unsupported,
121
recommend_upgrade=True,
123
"""Give an error or warning on old formats.
125
:param format: may be any kind of format - workingtree, branch,
128
:param allow_unsupported: If true, allow opening
129
formats that are strongly deprecated, and which may
130
have limited functionality.
132
:param recommend_upgrade: If true (default), warn
133
the user through the ui object that they may wish
134
to upgrade the object.
136
# TODO: perhaps move this into a base Format class; it's not BzrDir
137
# specific. mbp 20070323
138
if not allow_unsupported and not format.is_supported():
139
# see open_downlevel to open legacy branches.
140
raise errors.UnsupportedFormatError(format=format)
141
if recommend_upgrade \
142
and getattr(format, 'upgrade_recommended', False):
143
ui.ui_factory.recommend_upgrade(
144
format.get_format_description(),
147
def clone(self, url, revision_id=None, force_new_repo=False):
148
"""Clone this bzrdir and its contents to url verbatim.
150
If urls last component does not exist, it will be created.
152
if revision_id is not None, then the clone operation may tune
153
itself to download less data.
154
:param force_new_repo: Do not use a shared repository for the target
155
even if one is available.
158
result = self._format.initialize(url)
160
local_repo = self.find_repository()
161
except errors.NoRepositoryPresent:
164
# may need to copy content in
166
result_repo = local_repo.clone(
168
revision_id=revision_id)
169
result_repo.set_make_working_trees(local_repo.make_working_trees())
172
result_repo = result.find_repository()
173
# fetch content this dir needs.
174
result_repo.fetch(local_repo, revision_id=revision_id)
175
except errors.NoRepositoryPresent:
176
# needed to make one anyway.
177
result_repo = local_repo.clone(
179
revision_id=revision_id)
180
result_repo.set_make_working_trees(local_repo.make_working_trees())
181
# 1 if there is a branch present
182
# make sure its content is available in the target repository
185
self.open_branch().clone(result, revision_id=revision_id)
186
except errors.NotBranchError:
189
self.open_workingtree().clone(result)
190
except (errors.NoWorkingTree, errors.NotLocalUrl):
194
# TODO: This should be given a Transport, and should chdir up; otherwise
195
# this will open a new connection.
196
def _make_tail(self, url):
197
head, tail = urlutils.split(url)
198
if tail and tail != '.':
199
t = get_transport(head)
202
except errors.FileExists:
205
# TODO: Should take a Transport
207
def create(cls, base, format=None):
208
"""Create a new BzrDir at the url 'base'.
210
This will call the current default formats initialize with base
211
as the only parameter.
213
:param format: If supplied, the format of branch to create. If not
214
supplied, the default is used.
216
if cls is not BzrDir:
217
raise AssertionError("BzrDir.create always creates the default"
218
" format, not one of %r" % cls)
219
head, tail = urlutils.split(base)
220
if tail and tail != '.':
221
t = get_transport(head)
224
except errors.FileExists:
227
format = BzrDirFormat.get_default_format()
228
return format.initialize(safe_unicode(base))
230
def create_branch(self):
231
"""Create a branch in this BzrDir.
233
The bzrdirs format will control what branch format is created.
234
For more control see BranchFormatXX.create(a_bzrdir).
236
raise NotImplementedError(self.create_branch)
239
def create_branch_and_repo(base, force_new_repo=False, format=None):
240
"""Create a new BzrDir, Branch and Repository at the url 'base'.
242
This will use the current default BzrDirFormat, and use whatever
243
repository format that that uses via bzrdir.create_branch and
244
create_repository. If a shared repository is available that is used
247
The created Branch object is returned.
249
:param base: The URL to create the branch at.
250
:param force_new_repo: If True a new repository is always created.
252
bzrdir = BzrDir.create(base, format)
253
bzrdir._find_or_create_repository(force_new_repo)
254
return bzrdir.create_branch()
256
def _find_or_create_repository(self, force_new_repo):
257
"""Create a new repository if needed, returning the repository."""
259
return self.create_repository()
261
return self.find_repository()
262
except errors.NoRepositoryPresent:
263
return self.create_repository()
266
def create_branch_convenience(base, force_new_repo=False,
267
force_new_tree=None, format=None):
268
"""Create a new BzrDir, Branch and Repository at the url 'base'.
270
This is a convenience function - it will use an existing repository
271
if possible, can be told explicitly whether to create a working tree or
274
This will use the current default BzrDirFormat, and use whatever
275
repository format that that uses via bzrdir.create_branch and
276
create_repository. If a shared repository is available that is used
277
preferentially. Whatever repository is used, its tree creation policy
280
The created Branch object is returned.
281
If a working tree cannot be made due to base not being a file:// url,
282
no error is raised unless force_new_tree is True, in which case no
283
data is created on disk and NotLocalUrl is raised.
285
:param base: The URL to create the branch at.
286
:param force_new_repo: If True a new repository is always created.
287
:param force_new_tree: If True or False force creation of a tree or
288
prevent such creation respectively.
289
:param format: Override for the for the bzrdir format to create
292
# check for non local urls
293
t = get_transport(safe_unicode(base))
294
if not isinstance(t, LocalTransport):
295
raise errors.NotLocalUrl(base)
296
bzrdir = BzrDir.create(base, format)
297
repo = bzrdir._find_or_create_repository(force_new_repo)
298
result = bzrdir.create_branch()
299
if force_new_tree or (repo.make_working_trees() and
300
force_new_tree is None):
302
bzrdir.create_workingtree()
303
except errors.NotLocalUrl:
308
def create_repository(base, shared=False, format=None):
309
"""Create a new BzrDir and Repository at the url 'base'.
311
If no format is supplied, this will default to the current default
312
BzrDirFormat by default, and use whatever repository format that that
313
uses for bzrdirformat.create_repository.
315
:param shared: Create a shared repository rather than a standalone
317
The Repository object is returned.
319
This must be overridden as an instance method in child classes, where
320
it should take no parameters and construct whatever repository format
321
that child class desires.
323
bzrdir = BzrDir.create(base, format)
324
return bzrdir.create_repository(shared)
327
def create_standalone_workingtree(base, format=None):
328
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
330
'base' must be a local path or a file:// url.
332
This will use the current default BzrDirFormat, and use whatever
333
repository format that that uses for bzrdirformat.create_workingtree,
334
create_branch and create_repository.
336
:return: The WorkingTree object.
338
t = get_transport(safe_unicode(base))
339
if not isinstance(t, LocalTransport):
340
raise errors.NotLocalUrl(base)
341
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
343
format=format).bzrdir
344
return bzrdir.create_workingtree()
346
def create_workingtree(self, revision_id=None):
347
"""Create a working tree at this BzrDir.
349
revision_id: create it as of this revision id.
351
raise NotImplementedError(self.create_workingtree)
353
def retire_bzrdir(self):
354
"""Permanently disable the bzrdir.
356
This is done by renaming it to give the user some ability to recover
357
if there was a problem.
359
This will have horrible consequences if anyone has anything locked or
362
for i in xrange(10000):
364
to_path = '.bzr.retired.%d' % i
365
self.root_transport.rename('.bzr', to_path)
366
note("renamed %s to %s"
367
% (self.root_transport.abspath('.bzr'), to_path))
369
except (errors.TransportError, IOError, errors.PathError):
372
def destroy_workingtree(self):
373
"""Destroy the working tree at this BzrDir.
375
Formats that do not support this may raise UnsupportedOperation.
377
raise NotImplementedError(self.destroy_workingtree)
379
def destroy_workingtree_metadata(self):
380
"""Destroy the control files for the working tree at this BzrDir.
382
The contents of working tree files are not affected.
383
Formats that do not support this may raise UnsupportedOperation.
385
raise NotImplementedError(self.destroy_workingtree_metadata)
387
def find_repository(self):
388
"""Find the repository that should be used for a_bzrdir.
390
This does not require a branch as we use it to find the repo for
391
new branches as well as to hook existing branches up to their
395
return self.open_repository()
396
except errors.NoRepositoryPresent:
398
next_transport = self.root_transport.clone('..')
400
# find the next containing bzrdir
402
found_bzrdir = BzrDir.open_containing_from_transport(
404
except errors.NotBranchError:
406
raise errors.NoRepositoryPresent(self)
407
# does it have a repository ?
409
repository = found_bzrdir.open_repository()
410
except errors.NoRepositoryPresent:
411
next_transport = found_bzrdir.root_transport.clone('..')
412
if (found_bzrdir.root_transport.base == next_transport.base):
413
# top of the file system
417
if ((found_bzrdir.root_transport.base ==
418
self.root_transport.base) or repository.is_shared()):
421
raise errors.NoRepositoryPresent(self)
422
raise errors.NoRepositoryPresent(self)
424
def get_branch_reference(self):
425
"""Return the referenced URL for the branch in this bzrdir.
427
:raises NotBranchError: If there is no Branch.
428
:return: The URL the branch in this bzrdir references if it is a
429
reference branch, or None for regular branches.
433
def get_branch_transport(self, branch_format):
434
"""Get the transport for use by branch format in this BzrDir.
436
Note that bzr dirs that do not support format strings will raise
437
IncompatibleFormat if the branch format they are given has
438
a format string, and vice versa.
440
If branch_format is None, the transport is returned with no
441
checking. if it is not None, then the returned transport is
442
guaranteed to point to an existing directory ready for use.
444
raise NotImplementedError(self.get_branch_transport)
446
def get_repository_transport(self, repository_format):
447
"""Get the transport for use by repository format in this BzrDir.
449
Note that bzr dirs that do not support format strings will raise
450
IncompatibleFormat if the repository format they are given has
451
a format string, and vice versa.
453
If repository_format is None, the transport is returned with no
454
checking. if it is not None, then the returned transport is
455
guaranteed to point to an existing directory ready for use.
457
raise NotImplementedError(self.get_repository_transport)
459
def get_workingtree_transport(self, tree_format):
460
"""Get the transport for use by workingtree format in this BzrDir.
462
Note that bzr dirs that do not support format strings will raise
463
IncompatibleFormat if the workingtree format they are given has a
464
format string, and vice versa.
466
If workingtree_format is None, the transport is returned with no
467
checking. if it is not None, then the returned transport is
468
guaranteed to point to an existing directory ready for use.
470
raise NotImplementedError(self.get_workingtree_transport)
472
def __init__(self, _transport, _format):
473
"""Initialize a Bzr control dir object.
475
Only really common logic should reside here, concrete classes should be
476
made with varying behaviours.
478
:param _format: the format that is creating this BzrDir instance.
479
:param _transport: the transport this dir is based at.
481
self._format = _format
482
self.transport = _transport.clone('.bzr')
483
self.root_transport = _transport
485
def is_control_filename(self, filename):
486
"""True if filename is the name of a path which is reserved for bzrdir's.
488
:param filename: A filename within the root transport of this bzrdir.
490
This is true IF and ONLY IF the filename is part of the namespace reserved
491
for bzr control dirs. Currently this is the '.bzr' directory in the root
492
of the root_transport. it is expected that plugins will need to extend
493
this in the future - for instance to make bzr talk with svn working
496
# this might be better on the BzrDirFormat class because it refers to
497
# all the possible bzrdir disk formats.
498
# This method is tested via the workingtree is_control_filename tests-
499
# it was extracted from WorkingTree.is_control_filename. If the methods
500
# contract is extended beyond the current trivial implementation please
501
# add new tests for it to the appropriate place.
502
return filename == '.bzr' or filename.startswith('.bzr/')
504
def needs_format_conversion(self, format=None):
505
"""Return true if this bzrdir needs convert_format run on it.
507
For instance, if the repository format is out of date but the
508
branch and working tree are not, this should return True.
510
:param format: Optional parameter indicating a specific desired
511
format we plan to arrive at.
513
raise NotImplementedError(self.needs_format_conversion)
516
def open_unsupported(base):
517
"""Open a branch which is not supported."""
518
return BzrDir.open(base, _unsupported=True)
521
def open(base, _unsupported=False):
522
"""Open an existing bzrdir, rooted at 'base' (url)
524
_unsupported is a private parameter to the BzrDir class.
526
t = get_transport(base)
527
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
530
def open_from_transport(transport, _unsupported=False,
531
_server_formats=True):
532
"""Open a bzrdir within a particular directory.
534
:param transport: Transport containing the bzrdir.
535
:param _unsupported: private.
537
base = transport.base
539
def find_format(transport):
540
return transport, BzrDirFormat.find_format(
541
transport, _server_formats=_server_formats)
543
def redirected(transport, e, redirection_notice):
544
qualified_source = e.get_source_url()
545
relpath = transport.relpath(qualified_source)
546
if not e.target.endswith(relpath):
547
# Not redirected to a branch-format, not a branch
548
raise errors.NotBranchError(path=e.target)
549
target = e.target[:-len(relpath)]
550
note('%s is%s redirected to %s',
551
transport.base, e.permanently, target)
552
# Let's try with a new transport
553
qualified_target = e.get_target_url()[:-len(relpath)]
554
# FIXME: If 'transport' has a qualifier, this should
555
# be applied again to the new transport *iff* the
556
# schemes used are the same. It's a bit tricky to
557
# verify, so I'll punt for now
559
return get_transport(target)
562
transport, format = do_catching_redirections(find_format,
565
except errors.TooManyRedirections:
566
raise errors.NotBranchError(base)
568
BzrDir._check_supported(format, _unsupported)
569
return format.open(transport, _found=True)
571
def open_branch(self, unsupported=False):
572
"""Open the branch object at this BzrDir if one is present.
574
If unsupported is True, then no longer supported branch formats can
577
TODO: static convenience version of this?
579
raise NotImplementedError(self.open_branch)
582
def open_containing(url):
583
"""Open an existing branch which contains url.
585
:param url: url to search from.
586
See open_containing_from_transport for more detail.
588
return BzrDir.open_containing_from_transport(get_transport(url))
591
def open_containing_from_transport(a_transport):
592
"""Open an existing branch which contains a_transport.base
594
This probes for a branch at a_transport, and searches upwards from there.
596
Basically we keep looking up until we find the control directory or
597
run into the root. If there isn't one, raises NotBranchError.
598
If there is one and it is either an unrecognised format or an unsupported
599
format, UnknownFormatError or UnsupportedFormatError are raised.
600
If there is one, it is returned, along with the unused portion of url.
602
:return: The BzrDir that contains the path, and a Unicode path
603
for the rest of the URL.
605
# this gets the normalised url back. I.e. '.' -> the full path.
606
url = a_transport.base
609
result = BzrDir.open_from_transport(a_transport)
610
return result, urlutils.unescape(a_transport.relpath(url))
611
except errors.NotBranchError, e:
614
new_t = a_transport.clone('..')
615
except errors.InvalidURLJoin:
616
# reached the root, whatever that may be
617
raise errors.NotBranchError(path=url)
618
if new_t.base == a_transport.base:
619
# reached the root, whatever that may be
620
raise errors.NotBranchError(path=url)
624
def open_containing_tree_or_branch(klass, location):
625
"""Return the branch and working tree contained by a location.
627
Returns (tree, branch, relpath).
628
If there is no tree at containing the location, tree will be None.
629
If there is no branch containing the location, an exception will be
631
relpath is the portion of the path that is contained by the branch.
633
bzrdir, relpath = klass.open_containing(location)
635
tree = bzrdir.open_workingtree()
636
except (errors.NoWorkingTree, errors.NotLocalUrl):
638
branch = bzrdir.open_branch()
641
return tree, branch, relpath
643
def open_repository(self, _unsupported=False):
644
"""Open the repository object at this BzrDir if one is present.
646
This will not follow the Branch object pointer - its strictly a direct
647
open facility. Most client code should use open_branch().repository to
650
_unsupported is a private parameter, not part of the api.
651
TODO: static convenience version of this?
653
raise NotImplementedError(self.open_repository)
655
def open_workingtree(self, _unsupported=False,
656
recommend_upgrade=True):
657
"""Open the workingtree object at this BzrDir if one is present.
659
:param recommend_upgrade: Optional keyword parameter, when True (the
660
default), emit through the ui module a recommendation that the user
661
upgrade the working tree when the workingtree being opened is old
662
(but still fully supported).
664
raise NotImplementedError(self.open_workingtree)
666
def has_branch(self):
667
"""Tell if this bzrdir contains a branch.
669
Note: if you're going to open the branch, you should just go ahead
670
and try, and not ask permission first. (This method just opens the
671
branch and discards it, and that's somewhat expensive.)
676
except errors.NotBranchError:
679
def has_workingtree(self):
680
"""Tell if this bzrdir contains a working tree.
682
This will still raise an exception if the bzrdir has a workingtree that
683
is remote & inaccessible.
685
Note: if you're going to open the working tree, you should just go ahead
686
and try, and not ask permission first. (This method just opens the
687
workingtree and discards it, and that's somewhat expensive.)
690
self.open_workingtree(recommend_upgrade=False)
692
except errors.NoWorkingTree:
695
def _cloning_metadir(self):
696
"""Produce a metadir suitable for cloning with"""
697
result_format = self._format.__class__()
700
branch = self.open_branch()
701
source_repository = branch.repository
702
except errors.NotBranchError:
704
source_repository = self.open_repository()
705
except errors.NoRepositoryPresent:
706
source_repository = None
708
# XXX TODO: This isinstance is here because we have not implemented
709
# the fix recommended in bug # 103195 - to delegate this choice the
711
repo_format = source_repository._format
712
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
713
result_format.repository_format = repo_format
715
# TODO: Couldn't we just probe for the format in these cases,
716
# rather than opening the whole tree? It would be a little
717
# faster. mbp 20070401
718
tree = self.open_workingtree(recommend_upgrade=False)
719
except (errors.NoWorkingTree, errors.NotLocalUrl):
720
result_format.workingtree_format = None
722
result_format.workingtree_format = tree._format.__class__()
723
return result_format, source_repository
725
def cloning_metadir(self):
726
"""Produce a metadir suitable for cloning or sprouting with.
728
These operations may produce workingtrees (yes, even though they're
729
"cloning" something that doesn't have a tree, so a viable workingtree
730
format must be selected.
732
format, repository = self._cloning_metadir()
733
if format._workingtree_format is None:
734
if repository is None:
736
tree_format = repository._format._matchingbzrdir.workingtree_format
737
format.workingtree_format = tree_format.__class__()
740
def checkout_metadir(self):
741
return self.cloning_metadir()
743
def sprout(self, url, revision_id=None, force_new_repo=False,
745
"""Create a copy of this bzrdir prepared for use as a new line of
748
If urls last component does not exist, it will be created.
750
Attributes related to the identity of the source branch like
751
branch nickname will be cleaned, a working tree is created
752
whether one existed before or not; and a local branch is always
755
if revision_id is not None, then the clone operation may tune
756
itself to download less data.
759
cloning_format = self.cloning_metadir()
760
result = cloning_format.initialize(url)
762
source_branch = self.open_branch()
763
source_repository = source_branch.repository
764
except errors.NotBranchError:
767
source_repository = self.open_repository()
768
except errors.NoRepositoryPresent:
769
source_repository = None
774
result_repo = result.find_repository()
775
except errors.NoRepositoryPresent:
777
if source_repository is None and result_repo is not None:
779
elif source_repository is None and result_repo is None:
780
# no repo available, make a new one
781
result.create_repository()
782
elif source_repository is not None and result_repo is None:
783
# have source, and want to make a new target repo
784
result_repo = source_repository.sprout(result, revision_id=revision_id)
786
# fetch needed content into target.
787
if source_repository is not None:
789
# source_repository.copy_content_into(result_repo, revision_id=revision_id)
790
# so we can override the copy method
791
result_repo.fetch(source_repository, revision_id=revision_id)
792
if source_branch is not None:
793
source_branch.sprout(result, revision_id=revision_id)
795
result.create_branch()
796
# TODO: jam 20060426 we probably need a test in here in the
797
# case that the newly sprouted branch is a remote one
798
if result_repo is None or result_repo.make_working_trees():
799
wt = result.create_workingtree()
802
if wt.path2id('') is None:
804
wt.set_root_id(self.open_workingtree.get_root_id())
805
except errors.NoWorkingTree:
811
if recurse == 'down':
813
basis = wt.basis_tree()
815
subtrees = basis.iter_references()
816
recurse_branch = wt.branch
817
elif source_branch is not None:
818
basis = source_branch.basis_tree()
820
subtrees = basis.iter_references()
821
recurse_branch = source_branch
826
for path, file_id in subtrees:
827
target = urlutils.join(url, urlutils.escape(path))
828
sublocation = source_branch.reference_parent(file_id, path)
829
sublocation.bzrdir.sprout(target,
830
basis.get_reference_revision(file_id, path),
831
force_new_repo=force_new_repo, recurse=recurse)
833
if basis is not None:
838
class BzrDirPreSplitOut(BzrDir):
839
"""A common class for the all-in-one formats."""
841
def __init__(self, _transport, _format):
842
"""See BzrDir.__init__."""
843
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
844
assert self._format._lock_class == lockable_files.TransportLock
845
assert self._format._lock_file_name == 'branch-lock'
846
self._control_files = lockable_files.LockableFiles(
847
self.get_branch_transport(None),
848
self._format._lock_file_name,
849
self._format._lock_class)
851
def break_lock(self):
852
"""Pre-splitout bzrdirs do not suffer from stale locks."""
853
raise NotImplementedError(self.break_lock)
855
def clone(self, url, revision_id=None, force_new_repo=False):
856
"""See BzrDir.clone()."""
857
from bzrlib.workingtree import WorkingTreeFormat2
859
result = self._format._initialize_for_clone(url)
860
self.open_repository().clone(result, revision_id=revision_id)
861
from_branch = self.open_branch()
862
from_branch.clone(result, revision_id=revision_id)
864
self.open_workingtree().clone(result)
865
except errors.NotLocalUrl:
866
# make a new one, this format always has to have one.
868
WorkingTreeFormat2().initialize(result)
869
except errors.NotLocalUrl:
870
# but we cannot do it for remote trees.
871
to_branch = result.open_branch()
872
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
875
def create_branch(self):
876
"""See BzrDir.create_branch."""
877
return self.open_branch()
879
def create_repository(self, shared=False):
880
"""See BzrDir.create_repository."""
882
raise errors.IncompatibleFormat('shared repository', self._format)
883
return self.open_repository()
885
def create_workingtree(self, revision_id=None):
886
"""See BzrDir.create_workingtree."""
887
# this looks buggy but is not -really-
888
# because this format creates the workingtree when the bzrdir is
890
# clone and sprout will have set the revision_id
891
# and that will have set it for us, its only
892
# specific uses of create_workingtree in isolation
893
# that can do wonky stuff here, and that only
894
# happens for creating checkouts, which cannot be
895
# done on this format anyway. So - acceptable wart.
896
result = self.open_workingtree(recommend_upgrade=False)
897
if revision_id is not None:
898
if revision_id == _mod_revision.NULL_REVISION:
899
result.set_parent_ids([])
901
result.set_parent_ids([revision_id])
904
def destroy_workingtree(self):
905
"""See BzrDir.destroy_workingtree."""
906
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
908
def destroy_workingtree_metadata(self):
909
"""See BzrDir.destroy_workingtree_metadata."""
910
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
913
def get_branch_transport(self, branch_format):
914
"""See BzrDir.get_branch_transport()."""
915
if branch_format is None:
916
return self.transport
918
branch_format.get_format_string()
919
except NotImplementedError:
920
return self.transport
921
raise errors.IncompatibleFormat(branch_format, self._format)
923
def get_repository_transport(self, repository_format):
924
"""See BzrDir.get_repository_transport()."""
925
if repository_format is None:
926
return self.transport
928
repository_format.get_format_string()
929
except NotImplementedError:
930
return self.transport
931
raise errors.IncompatibleFormat(repository_format, self._format)
933
def get_workingtree_transport(self, workingtree_format):
934
"""See BzrDir.get_workingtree_transport()."""
935
if workingtree_format is None:
936
return self.transport
938
workingtree_format.get_format_string()
939
except NotImplementedError:
940
return self.transport
941
raise errors.IncompatibleFormat(workingtree_format, self._format)
943
def needs_format_conversion(self, format=None):
944
"""See BzrDir.needs_format_conversion()."""
945
# if the format is not the same as the system default,
946
# an upgrade is needed.
948
format = BzrDirFormat.get_default_format()
949
return not isinstance(self._format, format.__class__)
951
def open_branch(self, unsupported=False):
952
"""See BzrDir.open_branch."""
953
from bzrlib.branch import BzrBranchFormat4
954
format = BzrBranchFormat4()
955
self._check_supported(format, unsupported)
956
return format.open(self, _found=True)
958
def sprout(self, url, revision_id=None, force_new_repo=False):
959
"""See BzrDir.sprout()."""
960
from bzrlib.workingtree import WorkingTreeFormat2
962
result = self._format._initialize_for_clone(url)
964
self.open_repository().clone(result, revision_id=revision_id)
965
except errors.NoRepositoryPresent:
968
self.open_branch().sprout(result, revision_id=revision_id)
969
except errors.NotBranchError:
971
# we always want a working tree
972
WorkingTreeFormat2().initialize(result)
976
class BzrDir4(BzrDirPreSplitOut):
977
"""A .bzr version 4 control object.
979
This is a deprecated format and may be removed after sept 2006.
982
def create_repository(self, shared=False):
983
"""See BzrDir.create_repository."""
984
return self._format.repository_format.initialize(self, shared)
986
def needs_format_conversion(self, format=None):
987
"""Format 4 dirs are always in need of conversion."""
990
def open_repository(self):
991
"""See BzrDir.open_repository."""
992
from bzrlib.repofmt.weaverepo import RepositoryFormat4
993
return RepositoryFormat4().open(self, _found=True)
996
class BzrDir5(BzrDirPreSplitOut):
997
"""A .bzr version 5 control object.
999
This is a deprecated format and may be removed after sept 2006.
1002
def open_repository(self):
1003
"""See BzrDir.open_repository."""
1004
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1005
return RepositoryFormat5().open(self, _found=True)
1007
def open_workingtree(self, _unsupported=False,
1008
recommend_upgrade=True):
1009
"""See BzrDir.create_workingtree."""
1010
from bzrlib.workingtree import WorkingTreeFormat2
1011
wt_format = WorkingTreeFormat2()
1012
# we don't warn here about upgrades; that ought to be handled for the
1014
return wt_format.open(self, _found=True)
1017
class BzrDir6(BzrDirPreSplitOut):
1018
"""A .bzr version 6 control object.
1020
This is a deprecated format and may be removed after sept 2006.
1023
def open_repository(self):
1024
"""See BzrDir.open_repository."""
1025
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1026
return RepositoryFormat6().open(self, _found=True)
1028
def open_workingtree(self, _unsupported=False,
1029
recommend_upgrade=True):
1030
"""See BzrDir.create_workingtree."""
1031
# we don't warn here about upgrades; that ought to be handled for the
1033
from bzrlib.workingtree import WorkingTreeFormat2
1034
return WorkingTreeFormat2().open(self, _found=True)
1037
class BzrDirMeta1(BzrDir):
1038
"""A .bzr meta version 1 control object.
1040
This is the first control object where the
1041
individual aspects are really split out: there are separate repository,
1042
workingtree and branch subdirectories and any subset of the three can be
1043
present within a BzrDir.
1046
def can_convert_format(self):
1047
"""See BzrDir.can_convert_format()."""
1050
def create_branch(self):
1051
"""See BzrDir.create_branch."""
1052
return self._format.get_branch_format().initialize(self)
1054
def create_repository(self, shared=False):
1055
"""See BzrDir.create_repository."""
1056
return self._format.repository_format.initialize(self, shared)
1058
def create_workingtree(self, revision_id=None):
1059
"""See BzrDir.create_workingtree."""
1060
from bzrlib.workingtree import WorkingTreeFormat
1061
return self._format.workingtree_format.initialize(self, revision_id)
1063
def destroy_workingtree(self):
1064
"""See BzrDir.destroy_workingtree."""
1065
wt = self.open_workingtree(recommend_upgrade=False)
1066
repository = wt.branch.repository
1067
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1068
wt.revert([], old_tree=empty)
1069
self.destroy_workingtree_metadata()
1071
def destroy_workingtree_metadata(self):
1072
self.transport.delete_tree('checkout')
1074
def find_branch_format(self):
1075
"""Find the branch 'format' for this bzrdir.
1077
This might be a synthetic object for e.g. RemoteBranch and SVN.
1079
from bzrlib.branch import BranchFormat
1080
return BranchFormat.find_format(self)
1082
def _get_mkdir_mode(self):
1083
"""Figure out the mode to use when creating a bzrdir subdir."""
1084
temp_control = lockable_files.LockableFiles(self.transport, '',
1085
lockable_files.TransportLock)
1086
return temp_control._dir_mode
1088
def get_branch_reference(self):
1089
"""See BzrDir.get_branch_reference()."""
1090
from bzrlib.branch import BranchFormat
1091
format = BranchFormat.find_format(self)
1092
return format.get_reference(self)
1094
def get_branch_transport(self, branch_format):
1095
"""See BzrDir.get_branch_transport()."""
1096
if branch_format is None:
1097
return self.transport.clone('branch')
1099
branch_format.get_format_string()
1100
except NotImplementedError:
1101
raise errors.IncompatibleFormat(branch_format, self._format)
1103
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1104
except errors.FileExists:
1106
return self.transport.clone('branch')
1108
def get_repository_transport(self, repository_format):
1109
"""See BzrDir.get_repository_transport()."""
1110
if repository_format is None:
1111
return self.transport.clone('repository')
1113
repository_format.get_format_string()
1114
except NotImplementedError:
1115
raise errors.IncompatibleFormat(repository_format, self._format)
1117
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1118
except errors.FileExists:
1120
return self.transport.clone('repository')
1122
def get_workingtree_transport(self, workingtree_format):
1123
"""See BzrDir.get_workingtree_transport()."""
1124
if workingtree_format is None:
1125
return self.transport.clone('checkout')
1127
workingtree_format.get_format_string()
1128
except NotImplementedError:
1129
raise errors.IncompatibleFormat(workingtree_format, self._format)
1131
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1132
except errors.FileExists:
1134
return self.transport.clone('checkout')
1136
def needs_format_conversion(self, format=None):
1137
"""See BzrDir.needs_format_conversion()."""
1139
format = BzrDirFormat.get_default_format()
1140
if not isinstance(self._format, format.__class__):
1141
# it is not a meta dir format, conversion is needed.
1143
# we might want to push this down to the repository?
1145
if not isinstance(self.open_repository()._format,
1146
format.repository_format.__class__):
1147
# the repository needs an upgrade.
1149
except errors.NoRepositoryPresent:
1152
if not isinstance(self.open_branch()._format,
1153
format.get_branch_format().__class__):
1154
# the branch needs an upgrade.
1156
except errors.NotBranchError:
1159
my_wt = self.open_workingtree(recommend_upgrade=False)
1160
if not isinstance(my_wt._format,
1161
format.workingtree_format.__class__):
1162
# the workingtree needs an upgrade.
1164
except (errors.NoWorkingTree, errors.NotLocalUrl):
1168
def open_branch(self, unsupported=False):
1169
"""See BzrDir.open_branch."""
1170
format = self.find_branch_format()
1171
self._check_supported(format, unsupported)
1172
return format.open(self, _found=True)
1174
def open_repository(self, unsupported=False):
1175
"""See BzrDir.open_repository."""
1176
from bzrlib.repository import RepositoryFormat
1177
format = RepositoryFormat.find_format(self)
1178
self._check_supported(format, unsupported)
1179
return format.open(self, _found=True)
1181
def open_workingtree(self, unsupported=False,
1182
recommend_upgrade=True):
1183
"""See BzrDir.open_workingtree."""
1184
from bzrlib.workingtree import WorkingTreeFormat
1185
format = WorkingTreeFormat.find_format(self)
1186
self._check_supported(format, unsupported,
1188
basedir=self.root_transport.base)
1189
return format.open(self, _found=True)
1192
class BzrDirFormat(object):
1193
"""An encapsulation of the initialization and open routines for a format.
1195
Formats provide three things:
1196
* An initialization routine,
1200
Formats are placed in an dict by their format string for reference
1201
during bzrdir opening. These should be subclasses of BzrDirFormat
1204
Once a format is deprecated, just deprecate the initialize and open
1205
methods on the format class. Do not deprecate the object, as the
1206
object will be created every system load.
1209
_default_format = None
1210
"""The default format used for new .bzr dirs."""
1213
"""The known formats."""
1215
_control_formats = []
1216
"""The registered control formats - .bzr, ....
1218
This is a list of BzrDirFormat objects.
1221
_control_server_formats = []
1222
"""The registered control server formats, e.g. RemoteBzrDirs.
1224
This is a list of BzrDirFormat objects.
1227
_lock_file_name = 'branch-lock'
1229
# _lock_class must be set in subclasses to the lock type, typ.
1230
# TransportLock or LockDir
1233
def find_format(klass, transport, _server_formats=True):
1234
"""Return the format present at transport."""
1236
formats = klass._control_server_formats + klass._control_formats
1238
formats = klass._control_formats
1239
for format in formats:
1241
return format.probe_transport(transport)
1242
except errors.NotBranchError:
1243
# this format does not find a control dir here.
1245
raise errors.NotBranchError(path=transport.base)
1248
def probe_transport(klass, transport):
1249
"""Return the .bzrdir style format present in a directory."""
1251
format_string = transport.get(".bzr/branch-format").read()
1252
except errors.NoSuchFile:
1253
raise errors.NotBranchError(path=transport.base)
1256
return klass._formats[format_string]
1258
raise errors.UnknownFormatError(format=format_string)
1261
def get_default_format(klass):
1262
"""Return the current default format."""
1263
return klass._default_format
1265
def get_format_string(self):
1266
"""Return the ASCII format string that identifies this format."""
1267
raise NotImplementedError(self.get_format_string)
1269
def get_format_description(self):
1270
"""Return the short description for this format."""
1271
raise NotImplementedError(self.get_format_description)
1273
def get_converter(self, format=None):
1274
"""Return the converter to use to convert bzrdirs needing converts.
1276
This returns a bzrlib.bzrdir.Converter object.
1278
This should return the best upgrader to step this format towards the
1279
current default format. In the case of plugins we can/should provide
1280
some means for them to extend the range of returnable converters.
1282
:param format: Optional format to override the default format of the
1285
raise NotImplementedError(self.get_converter)
1287
def initialize(self, url):
1288
"""Create a bzr control dir at this url and return an opened copy.
1290
Subclasses should typically override initialize_on_transport
1291
instead of this method.
1293
return self.initialize_on_transport(get_transport(url))
1295
def initialize_on_transport(self, transport):
1296
"""Initialize a new bzrdir in the base directory of a Transport."""
1297
# Since we don't have a .bzr directory, inherit the
1298
# mode from the root directory
1299
temp_control = lockable_files.LockableFiles(transport,
1300
'', lockable_files.TransportLock)
1301
temp_control._transport.mkdir('.bzr',
1302
# FIXME: RBC 20060121 don't peek under
1304
mode=temp_control._dir_mode)
1305
file_mode = temp_control._file_mode
1307
mutter('created control directory in ' + transport.base)
1308
control = transport.clone('.bzr')
1309
utf8_files = [('README',
1310
"This is a Bazaar-NG control directory.\n"
1311
"Do not change any files in this directory.\n"),
1312
('branch-format', self.get_format_string()),
1314
# NB: no need to escape relative paths that are url safe.
1315
control_files = lockable_files.LockableFiles(control,
1316
self._lock_file_name, self._lock_class)
1317
control_files.create_lock()
1318
control_files.lock_write()
1320
for file, content in utf8_files:
1321
control_files.put_utf8(file, content)
1323
control_files.unlock()
1324
return self.open(transport, _found=True)
1326
def is_supported(self):
1327
"""Is this format supported?
1329
Supported formats must be initializable and openable.
1330
Unsupported formats may not support initialization or committing or
1331
some other features depending on the reason for not being supported.
1335
def same_model(self, target_format):
1336
return (self.repository_format.rich_root_data ==
1337
target_format.rich_root_data)
1340
def known_formats(klass):
1341
"""Return all the known formats.
1343
Concrete formats should override _known_formats.
1345
# There is double indirection here to make sure that control
1346
# formats used by more than one dir format will only be probed
1347
# once. This can otherwise be quite expensive for remote connections.
1349
for format in klass._control_formats:
1350
result.update(format._known_formats())
1354
def _known_formats(klass):
1355
"""Return the known format instances for this control format."""
1356
return set(klass._formats.values())
1358
def open(self, transport, _found=False):
1359
"""Return an instance of this format for the dir transport points at.
1361
_found is a private parameter, do not use it.
1364
found_format = BzrDirFormat.find_format(transport)
1365
if not isinstance(found_format, self.__class__):
1366
raise AssertionError("%s was asked to open %s, but it seems to need "
1368
% (self, transport, found_format))
1369
return self._open(transport)
1371
def _open(self, transport):
1372
"""Template method helper for opening BzrDirectories.
1374
This performs the actual open and any additional logic or parameter
1377
raise NotImplementedError(self._open)
1380
def register_format(klass, format):
1381
klass._formats[format.get_format_string()] = format
1384
def register_control_format(klass, format):
1385
"""Register a format that does not use '.bzr' for its control dir.
1387
TODO: This should be pulled up into a 'ControlDirFormat' base class
1388
which BzrDirFormat can inherit from, and renamed to register_format
1389
there. It has been done without that for now for simplicity of
1392
klass._control_formats.append(format)
1395
def register_control_server_format(klass, format):
1396
"""Register a control format for client-server environments.
1398
These formats will be tried before ones registered with
1399
register_control_format. This gives implementations that decide to the
1400
chance to grab it before anything looks at the contents of the format
1403
klass._control_server_formats.append(format)
1406
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1407
def set_default_format(klass, format):
1408
klass._set_default_format(format)
1411
def _set_default_format(klass, format):
1412
"""Set default format (for testing behavior of defaults only)"""
1413
klass._default_format = format
1416
return self.get_format_string()[:-1]
1419
def unregister_format(klass, format):
1420
assert klass._formats[format.get_format_string()] is format
1421
del klass._formats[format.get_format_string()]
1424
def unregister_control_format(klass, format):
1425
klass._control_formats.remove(format)
1428
class BzrDirFormat4(BzrDirFormat):
1429
"""Bzr dir format 4.
1431
This format is a combined format for working tree, branch and repository.
1433
- Format 1 working trees [always]
1434
- Format 4 branches [always]
1435
- Format 4 repositories [always]
1437
This format is deprecated: it indexes texts using a text it which is
1438
removed in format 5; write support for this format has been removed.
1441
_lock_class = lockable_files.TransportLock
1443
def get_format_string(self):
1444
"""See BzrDirFormat.get_format_string()."""
1445
return "Bazaar-NG branch, format 0.0.4\n"
1447
def get_format_description(self):
1448
"""See BzrDirFormat.get_format_description()."""
1449
return "All-in-one format 4"
1451
def get_converter(self, format=None):
1452
"""See BzrDirFormat.get_converter()."""
1453
# there is one and only one upgrade path here.
1454
return ConvertBzrDir4To5()
1456
def initialize_on_transport(self, transport):
1457
"""Format 4 branches cannot be created."""
1458
raise errors.UninitializableFormat(self)
1460
def is_supported(self):
1461
"""Format 4 is not supported.
1463
It is not supported because the model changed from 4 to 5 and the
1464
conversion logic is expensive - so doing it on the fly was not
1469
def _open(self, transport):
1470
"""See BzrDirFormat._open."""
1471
return BzrDir4(transport, self)
1473
def __return_repository_format(self):
1474
"""Circular import protection."""
1475
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1476
return RepositoryFormat4()
1477
repository_format = property(__return_repository_format)
1480
class BzrDirFormat5(BzrDirFormat):
1481
"""Bzr control format 5.
1483
This format is a combined format for working tree, branch and repository.
1485
- Format 2 working trees [always]
1486
- Format 4 branches [always]
1487
- Format 5 repositories [always]
1488
Unhashed stores in the repository.
1491
_lock_class = lockable_files.TransportLock
1493
def get_format_string(self):
1494
"""See BzrDirFormat.get_format_string()."""
1495
return "Bazaar-NG branch, format 5\n"
1497
def get_format_description(self):
1498
"""See BzrDirFormat.get_format_description()."""
1499
return "All-in-one format 5"
1501
def get_converter(self, format=None):
1502
"""See BzrDirFormat.get_converter()."""
1503
# there is one and only one upgrade path here.
1504
return ConvertBzrDir5To6()
1506
def _initialize_for_clone(self, url):
1507
return self.initialize_on_transport(get_transport(url), _cloning=True)
1509
def initialize_on_transport(self, transport, _cloning=False):
1510
"""Format 5 dirs always have working tree, branch and repository.
1512
Except when they are being cloned.
1514
from bzrlib.branch import BzrBranchFormat4
1515
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1516
from bzrlib.workingtree import WorkingTreeFormat2
1517
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1518
RepositoryFormat5().initialize(result, _internal=True)
1520
branch = BzrBranchFormat4().initialize(result)
1522
WorkingTreeFormat2().initialize(result)
1523
except errors.NotLocalUrl:
1524
# Even though we can't access the working tree, we need to
1525
# create its control files.
1526
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1529
def _open(self, transport):
1530
"""See BzrDirFormat._open."""
1531
return BzrDir5(transport, self)
1533
def __return_repository_format(self):
1534
"""Circular import protection."""
1535
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1536
return RepositoryFormat5()
1537
repository_format = property(__return_repository_format)
1540
class BzrDirFormat6(BzrDirFormat):
1541
"""Bzr control format 6.
1543
This format is a combined format for working tree, branch and repository.
1545
- Format 2 working trees [always]
1546
- Format 4 branches [always]
1547
- Format 6 repositories [always]
1550
_lock_class = lockable_files.TransportLock
1552
def get_format_string(self):
1553
"""See BzrDirFormat.get_format_string()."""
1554
return "Bazaar-NG branch, format 6\n"
1556
def get_format_description(self):
1557
"""See BzrDirFormat.get_format_description()."""
1558
return "All-in-one format 6"
1560
def get_converter(self, format=None):
1561
"""See BzrDirFormat.get_converter()."""
1562
# there is one and only one upgrade path here.
1563
return ConvertBzrDir6ToMeta()
1565
def _initialize_for_clone(self, url):
1566
return self.initialize_on_transport(get_transport(url), _cloning=True)
1568
def initialize_on_transport(self, transport, _cloning=False):
1569
"""Format 6 dirs always have working tree, branch and repository.
1571
Except when they are being cloned.
1573
from bzrlib.branch import BzrBranchFormat4
1574
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1575
from bzrlib.workingtree import WorkingTreeFormat2
1576
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1577
RepositoryFormat6().initialize(result, _internal=True)
1579
branch = BzrBranchFormat4().initialize(result)
1581
WorkingTreeFormat2().initialize(result)
1582
except errors.NotLocalUrl:
1583
# Even though we can't access the working tree, we need to
1584
# create its control files.
1585
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1588
def _open(self, transport):
1589
"""See BzrDirFormat._open."""
1590
return BzrDir6(transport, self)
1592
def __return_repository_format(self):
1593
"""Circular import protection."""
1594
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1595
return RepositoryFormat6()
1596
repository_format = property(__return_repository_format)
1599
class BzrDirMetaFormat1(BzrDirFormat):
1600
"""Bzr meta control format 1
1602
This is the first format with split out working tree, branch and repository
1605
- Format 3 working trees [optional]
1606
- Format 5 branches [optional]
1607
- Format 7 repositories [optional]
1610
_lock_class = lockdir.LockDir
1613
self._workingtree_format = None
1614
self._branch_format = None
1616
def __eq__(self, other):
1617
if other.__class__ is not self.__class__:
1619
if other.repository_format != self.repository_format:
1621
if other.workingtree_format != self.workingtree_format:
1625
def __ne__(self, other):
1626
return not self == other
1628
def get_branch_format(self):
1629
if self._branch_format is None:
1630
from bzrlib.branch import BranchFormat
1631
self._branch_format = BranchFormat.get_default_format()
1632
return self._branch_format
1634
def set_branch_format(self, format):
1635
self._branch_format = format
1637
def get_converter(self, format=None):
1638
"""See BzrDirFormat.get_converter()."""
1640
format = BzrDirFormat.get_default_format()
1641
if not isinstance(self, format.__class__):
1642
# converting away from metadir is not implemented
1643
raise NotImplementedError(self.get_converter)
1644
return ConvertMetaToMeta(format)
1646
def get_format_string(self):
1647
"""See BzrDirFormat.get_format_string()."""
1648
return "Bazaar-NG meta directory, format 1\n"
1650
def get_format_description(self):
1651
"""See BzrDirFormat.get_format_description()."""
1652
return "Meta directory format 1"
1654
def _open(self, transport):
1655
"""See BzrDirFormat._open."""
1656
return BzrDirMeta1(transport, self)
1658
def __return_repository_format(self):
1659
"""Circular import protection."""
1660
if getattr(self, '_repository_format', None):
1661
return self._repository_format
1662
from bzrlib.repository import RepositoryFormat
1663
return RepositoryFormat.get_default_format()
1665
def __set_repository_format(self, value):
1666
"""Allow changint the repository format for metadir formats."""
1667
self._repository_format = value
1669
repository_format = property(__return_repository_format, __set_repository_format)
1671
def __get_workingtree_format(self):
1672
if self._workingtree_format is None:
1673
from bzrlib.workingtree import WorkingTreeFormat
1674
self._workingtree_format = WorkingTreeFormat.get_default_format()
1675
return self._workingtree_format
1677
def __set_workingtree_format(self, wt_format):
1678
self._workingtree_format = wt_format
1680
workingtree_format = property(__get_workingtree_format,
1681
__set_workingtree_format)
1684
# Register bzr control format
1685
BzrDirFormat.register_control_format(BzrDirFormat)
1687
# Register bzr formats
1688
BzrDirFormat.register_format(BzrDirFormat4())
1689
BzrDirFormat.register_format(BzrDirFormat5())
1690
BzrDirFormat.register_format(BzrDirFormat6())
1691
__default_format = BzrDirMetaFormat1()
1692
BzrDirFormat.register_format(__default_format)
1693
BzrDirFormat._default_format = __default_format
1696
class BzrDirTestProviderAdapter(object):
1697
"""A tool to generate a suite testing multiple bzrdir formats at once.
1699
This is done by copying the test once for each transport and injecting
1700
the transport_server, transport_readonly_server, and bzrdir_format
1701
classes into each copy. Each copy is also given a new id() to make it
1705
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1707
"""Create an object to adapt tests.
1709
:param vfs_server: A factory to create a Transport Server which has
1710
all the VFS methods working, and is writable.
1712
self._vfs_factory = vfs_factory
1713
self._transport_server = transport_server
1714
self._transport_readonly_server = transport_readonly_server
1715
self._formats = formats
1717
def adapt(self, test):
1718
result = unittest.TestSuite()
1719
for format in self._formats:
1720
new_test = deepcopy(test)
1721
new_test.vfs_transport_factory = self._vfs_factory
1722
new_test.transport_server = self._transport_server
1723
new_test.transport_readonly_server = self._transport_readonly_server
1724
new_test.bzrdir_format = format
1725
def make_new_test_id():
1726
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1727
return lambda: new_id
1728
new_test.id = make_new_test_id()
1729
result.addTest(new_test)
1733
class Converter(object):
1734
"""Converts a disk format object from one format to another."""
1736
def convert(self, to_convert, pb):
1737
"""Perform the conversion of to_convert, giving feedback via pb.
1739
:param to_convert: The disk object to convert.
1740
:param pb: a progress bar to use for progress information.
1743
def step(self, message):
1744
"""Update the pb by a step."""
1746
self.pb.update(message, self.count, self.total)
1749
class ConvertBzrDir4To5(Converter):
1750
"""Converts format 4 bzr dirs to format 5."""
1753
super(ConvertBzrDir4To5, self).__init__()
1754
self.converted_revs = set()
1755
self.absent_revisions = set()
1759
def convert(self, to_convert, pb):
1760
"""See Converter.convert()."""
1761
self.bzrdir = to_convert
1763
self.pb.note('starting upgrade from format 4 to 5')
1764
if isinstance(self.bzrdir.transport, LocalTransport):
1765
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1766
self._convert_to_weaves()
1767
return BzrDir.open(self.bzrdir.root_transport.base)
1769
def _convert_to_weaves(self):
1770
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1773
stat = self.bzrdir.transport.stat('weaves')
1774
if not S_ISDIR(stat.st_mode):
1775
self.bzrdir.transport.delete('weaves')
1776
self.bzrdir.transport.mkdir('weaves')
1777
except errors.NoSuchFile:
1778
self.bzrdir.transport.mkdir('weaves')
1779
# deliberately not a WeaveFile as we want to build it up slowly.
1780
self.inv_weave = Weave('inventory')
1781
# holds in-memory weaves for all files
1782
self.text_weaves = {}
1783
self.bzrdir.transport.delete('branch-format')
1784
self.branch = self.bzrdir.open_branch()
1785
self._convert_working_inv()
1786
rev_history = self.branch.revision_history()
1787
# to_read is a stack holding the revisions we still need to process;
1788
# appending to it adds new highest-priority revisions
1789
self.known_revisions = set(rev_history)
1790
self.to_read = rev_history[-1:]
1792
rev_id = self.to_read.pop()
1793
if (rev_id not in self.revisions
1794
and rev_id not in self.absent_revisions):
1795
self._load_one_rev(rev_id)
1797
to_import = self._make_order()
1798
for i, rev_id in enumerate(to_import):
1799
self.pb.update('converting revision', i, len(to_import))
1800
self._convert_one_rev(rev_id)
1802
self._write_all_weaves()
1803
self._write_all_revs()
1804
self.pb.note('upgraded to weaves:')
1805
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1806
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1807
self.pb.note(' %6d texts', self.text_count)
1808
self._cleanup_spare_files_after_format4()
1809
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1811
def _cleanup_spare_files_after_format4(self):
1812
# FIXME working tree upgrade foo.
1813
for n in 'merged-patches', 'pending-merged-patches':
1815
## assert os.path.getsize(p) == 0
1816
self.bzrdir.transport.delete(n)
1817
except errors.NoSuchFile:
1819
self.bzrdir.transport.delete_tree('inventory-store')
1820
self.bzrdir.transport.delete_tree('text-store')
1822
def _convert_working_inv(self):
1823
inv = xml4.serializer_v4.read_inventory(
1824
self.branch.control_files.get('inventory'))
1825
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1826
# FIXME inventory is a working tree change.
1827
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1829
def _write_all_weaves(self):
1830
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1831
weave_transport = self.bzrdir.transport.clone('weaves')
1832
weaves = WeaveStore(weave_transport, prefixed=False)
1833
transaction = WriteTransaction()
1837
for file_id, file_weave in self.text_weaves.items():
1838
self.pb.update('writing weave', i, len(self.text_weaves))
1839
weaves._put_weave(file_id, file_weave, transaction)
1841
self.pb.update('inventory', 0, 1)
1842
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1843
self.pb.update('inventory', 1, 1)
1847
def _write_all_revs(self):
1848
"""Write all revisions out in new form."""
1849
self.bzrdir.transport.delete_tree('revision-store')
1850
self.bzrdir.transport.mkdir('revision-store')
1851
revision_transport = self.bzrdir.transport.clone('revision-store')
1853
_revision_store = TextRevisionStore(TextStore(revision_transport,
1857
transaction = WriteTransaction()
1858
for i, rev_id in enumerate(self.converted_revs):
1859
self.pb.update('write revision', i, len(self.converted_revs))
1860
_revision_store.add_revision(self.revisions[rev_id], transaction)
1864
def _load_one_rev(self, rev_id):
1865
"""Load a revision object into memory.
1867
Any parents not either loaded or abandoned get queued to be
1869
self.pb.update('loading revision',
1870
len(self.revisions),
1871
len(self.known_revisions))
1872
if not self.branch.repository.has_revision(rev_id):
1874
self.pb.note('revision {%s} not present in branch; '
1875
'will be converted as a ghost',
1877
self.absent_revisions.add(rev_id)
1879
rev = self.branch.repository._revision_store.get_revision(rev_id,
1880
self.branch.repository.get_transaction())
1881
for parent_id in rev.parent_ids:
1882
self.known_revisions.add(parent_id)
1883
self.to_read.append(parent_id)
1884
self.revisions[rev_id] = rev
1886
def _load_old_inventory(self, rev_id):
1887
assert rev_id not in self.converted_revs
1888
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1889
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1890
inv.revision_id = rev_id
1891
rev = self.revisions[rev_id]
1892
if rev.inventory_sha1:
1893
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1894
'inventory sha mismatch for {%s}' % rev_id
1897
def _load_updated_inventory(self, rev_id):
1898
assert rev_id in self.converted_revs
1899
inv_xml = self.inv_weave.get_text(rev_id)
1900
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1903
def _convert_one_rev(self, rev_id):
1904
"""Convert revision and all referenced objects to new format."""
1905
rev = self.revisions[rev_id]
1906
inv = self._load_old_inventory(rev_id)
1907
present_parents = [p for p in rev.parent_ids
1908
if p not in self.absent_revisions]
1909
self._convert_revision_contents(rev, inv, present_parents)
1910
self._store_new_weave(rev, inv, present_parents)
1911
self.converted_revs.add(rev_id)
1913
def _store_new_weave(self, rev, inv, present_parents):
1914
# the XML is now updated with text versions
1916
entries = inv.iter_entries()
1918
for path, ie in entries:
1919
assert getattr(ie, 'revision', None) is not None, \
1920
'no revision on {%s} in {%s}' % \
1921
(file_id, rev.revision_id)
1922
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1923
new_inv_sha1 = sha_string(new_inv_xml)
1924
self.inv_weave.add_lines(rev.revision_id,
1926
new_inv_xml.splitlines(True))
1927
rev.inventory_sha1 = new_inv_sha1
1929
def _convert_revision_contents(self, rev, inv, present_parents):
1930
"""Convert all the files within a revision.
1932
Also upgrade the inventory to refer to the text revision ids."""
1933
rev_id = rev.revision_id
1934
mutter('converting texts of revision {%s}',
1936
parent_invs = map(self._load_updated_inventory, present_parents)
1937
entries = inv.iter_entries()
1939
for path, ie in entries:
1940
self._convert_file_version(rev, ie, parent_invs)
1942
def _convert_file_version(self, rev, ie, parent_invs):
1943
"""Convert one version of one file.
1945
The file needs to be added into the weave if it is a merge
1946
of >=2 parents or if it's changed from its parent.
1948
file_id = ie.file_id
1949
rev_id = rev.revision_id
1950
w = self.text_weaves.get(file_id)
1953
self.text_weaves[file_id] = w
1954
text_changed = False
1955
previous_entries = ie.find_previous_heads(parent_invs,
1959
for old_revision in previous_entries:
1960
# if this fails, its a ghost ?
1961
assert old_revision in self.converted_revs, \
1962
"Revision {%s} not in converted_revs" % old_revision
1963
self.snapshot_ie(previous_entries, ie, w, rev_id)
1965
assert getattr(ie, 'revision', None) is not None
1967
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1968
# TODO: convert this logic, which is ~= snapshot to
1969
# a call to:. This needs the path figured out. rather than a work_tree
1970
# a v4 revision_tree can be given, or something that looks enough like
1971
# one to give the file content to the entry if it needs it.
1972
# and we need something that looks like a weave store for snapshot to
1974
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1975
if len(previous_revisions) == 1:
1976
previous_ie = previous_revisions.values()[0]
1977
if ie._unchanged(previous_ie):
1978
ie.revision = previous_ie.revision
1981
text = self.branch.repository.text_store.get(ie.text_id)
1982
file_lines = text.readlines()
1983
assert sha_strings(file_lines) == ie.text_sha1
1984
assert sum(map(len, file_lines)) == ie.text_size
1985
w.add_lines(rev_id, previous_revisions, file_lines)
1986
self.text_count += 1
1988
w.add_lines(rev_id, previous_revisions, [])
1989
ie.revision = rev_id
1991
def _make_order(self):
1992
"""Return a suitable order for importing revisions.
1994
The order must be such that an revision is imported after all
1995
its (present) parents.
1997
todo = set(self.revisions.keys())
1998
done = self.absent_revisions.copy()
2001
# scan through looking for a revision whose parents
2003
for rev_id in sorted(list(todo)):
2004
rev = self.revisions[rev_id]
2005
parent_ids = set(rev.parent_ids)
2006
if parent_ids.issubset(done):
2007
# can take this one now
2008
order.append(rev_id)
2014
class ConvertBzrDir5To6(Converter):
2015
"""Converts format 5 bzr dirs to format 6."""
2017
def convert(self, to_convert, pb):
2018
"""See Converter.convert()."""
2019
self.bzrdir = to_convert
2021
self.pb.note('starting upgrade from format 5 to 6')
2022
self._convert_to_prefixed()
2023
return BzrDir.open(self.bzrdir.root_transport.base)
2025
def _convert_to_prefixed(self):
2026
from bzrlib.store import TransportStore
2027
self.bzrdir.transport.delete('branch-format')
2028
for store_name in ["weaves", "revision-store"]:
2029
self.pb.note("adding prefixes to %s" % store_name)
2030
store_transport = self.bzrdir.transport.clone(store_name)
2031
store = TransportStore(store_transport, prefixed=True)
2032
for urlfilename in store_transport.list_dir('.'):
2033
filename = urlutils.unescape(urlfilename)
2034
if (filename.endswith(".weave") or
2035
filename.endswith(".gz") or
2036
filename.endswith(".sig")):
2037
file_id = os.path.splitext(filename)[0]
2040
prefix_dir = store.hash_prefix(file_id)
2041
# FIXME keep track of the dirs made RBC 20060121
2043
store_transport.move(filename, prefix_dir + '/' + filename)
2044
except errors.NoSuchFile: # catches missing dirs strangely enough
2045
store_transport.mkdir(prefix_dir)
2046
store_transport.move(filename, prefix_dir + '/' + filename)
2047
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2050
class ConvertBzrDir6ToMeta(Converter):
2051
"""Converts format 6 bzr dirs to metadirs."""
2053
def convert(self, to_convert, pb):
2054
"""See Converter.convert()."""
2055
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2056
from bzrlib.branch import BzrBranchFormat5
2057
self.bzrdir = to_convert
2060
self.total = 20 # the steps we know about
2061
self.garbage_inventories = []
2063
self.pb.note('starting upgrade from format 6 to metadir')
2064
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2065
# its faster to move specific files around than to open and use the apis...
2066
# first off, nuke ancestry.weave, it was never used.
2068
self.step('Removing ancestry.weave')
2069
self.bzrdir.transport.delete('ancestry.weave')
2070
except errors.NoSuchFile:
2072
# find out whats there
2073
self.step('Finding branch files')
2074
last_revision = self.bzrdir.open_branch().last_revision()
2075
bzrcontents = self.bzrdir.transport.list_dir('.')
2076
for name in bzrcontents:
2077
if name.startswith('basis-inventory.'):
2078
self.garbage_inventories.append(name)
2079
# create new directories for repository, working tree and branch
2080
self.dir_mode = self.bzrdir._control_files._dir_mode
2081
self.file_mode = self.bzrdir._control_files._file_mode
2082
repository_names = [('inventory.weave', True),
2083
('revision-store', True),
2085
self.step('Upgrading repository ')
2086
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2087
self.make_lock('repository')
2088
# we hard code the formats here because we are converting into
2089
# the meta format. The meta format upgrader can take this to a
2090
# future format within each component.
2091
self.put_format('repository', RepositoryFormat7())
2092
for entry in repository_names:
2093
self.move_entry('repository', entry)
2095
self.step('Upgrading branch ')
2096
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2097
self.make_lock('branch')
2098
self.put_format('branch', BzrBranchFormat5())
2099
branch_files = [('revision-history', True),
2100
('branch-name', True),
2102
for entry in branch_files:
2103
self.move_entry('branch', entry)
2105
checkout_files = [('pending-merges', True),
2106
('inventory', True),
2107
('stat-cache', False)]
2108
# If a mandatory checkout file is not present, the branch does not have
2109
# a functional checkout. Do not create a checkout in the converted
2111
for name, mandatory in checkout_files:
2112
if mandatory and name not in bzrcontents:
2113
has_checkout = False
2117
if not has_checkout:
2118
self.pb.note('No working tree.')
2119
# If some checkout files are there, we may as well get rid of them.
2120
for name, mandatory in checkout_files:
2121
if name in bzrcontents:
2122
self.bzrdir.transport.delete(name)
2124
from bzrlib.workingtree import WorkingTreeFormat3
2125
self.step('Upgrading working tree')
2126
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2127
self.make_lock('checkout')
2129
'checkout', WorkingTreeFormat3())
2130
self.bzrdir.transport.delete_multi(
2131
self.garbage_inventories, self.pb)
2132
for entry in checkout_files:
2133
self.move_entry('checkout', entry)
2134
if last_revision is not None:
2135
self.bzrdir._control_files.put_utf8(
2136
'checkout/last-revision', last_revision)
2137
self.bzrdir._control_files.put_utf8(
2138
'branch-format', BzrDirMetaFormat1().get_format_string())
2139
return BzrDir.open(self.bzrdir.root_transport.base)
2141
def make_lock(self, name):
2142
"""Make a lock for the new control dir name."""
2143
self.step('Make %s lock' % name)
2144
ld = lockdir.LockDir(self.bzrdir.transport,
2146
file_modebits=self.file_mode,
2147
dir_modebits=self.dir_mode)
2150
def move_entry(self, new_dir, entry):
2151
"""Move then entry name into new_dir."""
2153
mandatory = entry[1]
2154
self.step('Moving %s' % name)
2156
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2157
except errors.NoSuchFile:
2161
def put_format(self, dirname, format):
2162
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2165
class ConvertMetaToMeta(Converter):
2166
"""Converts the components of metadirs."""
2168
def __init__(self, target_format):
2169
"""Create a metadir to metadir converter.
2171
:param target_format: The final metadir format that is desired.
2173
self.target_format = target_format
2175
def convert(self, to_convert, pb):
2176
"""See Converter.convert()."""
2177
self.bzrdir = to_convert
2181
self.step('checking repository format')
2183
repo = self.bzrdir.open_repository()
2184
except errors.NoRepositoryPresent:
2187
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2188
from bzrlib.repository import CopyConverter
2189
self.pb.note('starting repository conversion')
2190
converter = CopyConverter(self.target_format.repository_format)
2191
converter.convert(repo, pb)
2193
branch = self.bzrdir.open_branch()
2194
except errors.NotBranchError:
2197
# TODO: conversions of Branch and Tree should be done by
2198
# InterXFormat lookups
2199
# Avoid circular imports
2200
from bzrlib import branch as _mod_branch
2201
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2202
self.target_format.get_branch_format().__class__ is
2203
_mod_branch.BzrBranchFormat6):
2204
branch_converter = _mod_branch.Converter5to6()
2205
branch_converter.convert(branch)
2207
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2208
except (errors.NoWorkingTree, errors.NotLocalUrl):
2211
# TODO: conversions of Branch and Tree should be done by
2212
# InterXFormat lookups
2213
if (isinstance(tree, workingtree.WorkingTree3) and
2214
not isinstance(tree, workingtree_4.WorkingTree4) and
2215
isinstance(self.target_format.workingtree_format,
2216
workingtree_4.WorkingTreeFormat4)):
2217
workingtree_4.Converter3to4().convert(tree)
2221
# This is not in remote.py because it's small, and needs to be registered.
2222
# Putting it in remote.py creates a circular import problem.
2223
# we can make it a lazy object if the control formats is turned into something
2225
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2226
"""Format representing bzrdirs accessed via a smart server"""
2228
def get_format_description(self):
2229
return 'bzr remote bzrdir'
2232
def probe_transport(klass, transport):
2233
"""Return a RemoteBzrDirFormat object if it looks possible."""
2235
transport.get_smart_client()
2236
except (NotImplementedError, AttributeError,
2237
errors.TransportNotPossible):
2238
# no smart server, so not a branch for this format type.
2239
raise errors.NotBranchError(path=transport.base)
2243
def initialize_on_transport(self, transport):
2245
# hand off the request to the smart server
2246
medium = transport.get_smart_medium()
2247
except errors.NoSmartMedium:
2248
# TODO: lookup the local format from a server hint.
2249
local_dir_format = BzrDirMetaFormat1()
2250
return local_dir_format.initialize_on_transport(transport)
2251
client = _SmartClient(medium)
2252
path = client.remote_path_from_transport(transport)
2253
response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
2254
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2255
return remote.RemoteBzrDir(transport)
2257
def _open(self, transport):
2258
return remote.RemoteBzrDir(transport)
2260
def __eq__(self, other):
2261
if not isinstance(other, RemoteBzrDirFormat):
2263
return self.get_format_description() == other.get_format_description()
2266
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2269
class BzrDirFormatInfo(object):
2271
def __init__(self, native, deprecated, hidden):
2272
self.deprecated = deprecated
2273
self.native = native
2274
self.hidden = hidden
2277
class BzrDirFormatRegistry(registry.Registry):
2278
"""Registry of user-selectable BzrDir subformats.
2280
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2281
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2284
def register_metadir(self, key,
2285
repository_format, help, native=True, deprecated=False,
2289
"""Register a metadir subformat.
2291
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2292
by the Repository format.
2294
:param repository_format: The fully-qualified repository format class
2296
:param branch_format: Fully-qualified branch format class name as
2298
:param tree_format: Fully-qualified tree format class name as
2301
# This should be expanded to support setting WorkingTree and Branch
2302
# formats, once BzrDirMetaFormat1 supports that.
2303
def _load(full_name):
2304
mod_name, factory_name = full_name.rsplit('.', 1)
2306
mod = __import__(mod_name, globals(), locals(),
2308
except ImportError, e:
2309
raise ImportError('failed to load %s: %s' % (full_name, e))
2311
factory = getattr(mod, factory_name)
2312
except AttributeError:
2313
raise AttributeError('no factory %s in module %r'
2318
bd = BzrDirMetaFormat1()
2319
if branch_format is not None:
2320
bd.set_branch_format(_load(branch_format))
2321
if tree_format is not None:
2322
bd.workingtree_format = _load(tree_format)
2323
if repository_format is not None:
2324
bd.repository_format = _load(repository_format)
2326
self.register(key, helper, help, native, deprecated, hidden)
2328
def register(self, key, factory, help, native=True, deprecated=False,
2330
"""Register a BzrDirFormat factory.
2332
The factory must be a callable that takes one parameter: the key.
2333
It must produce an instance of the BzrDirFormat when called.
2335
This function mainly exists to prevent the info object from being
2338
registry.Registry.register(self, key, factory, help,
2339
BzrDirFormatInfo(native, deprecated, hidden))
2341
def register_lazy(self, key, module_name, member_name, help, native=True,
2342
deprecated=False, hidden=False):
2343
registry.Registry.register_lazy(self, key, module_name, member_name,
2344
help, BzrDirFormatInfo(native, deprecated, hidden))
2346
def set_default(self, key):
2347
"""Set the 'default' key to be a clone of the supplied key.
2349
This method must be called once and only once.
2351
registry.Registry.register(self, 'default', self.get(key),
2352
self.get_help(key), info=self.get_info(key))
2354
def set_default_repository(self, key):
2355
"""Set the FormatRegistry default and Repository default.
2357
This is a transitional method while Repository.set_default_format
2360
if 'default' in self:
2361
self.remove('default')
2362
self.set_default(key)
2363
format = self.get('default')()
2364
assert isinstance(format, BzrDirMetaFormat1)
2366
def make_bzrdir(self, key):
2367
return self.get(key)()
2369
def help_topic(self, topic):
2370
output = textwrap.dedent("""\
2371
Bazaar directory formats
2372
------------------------
2374
These formats can be used for creating branches, working trees, and
2378
default_help = self.get_help('default')
2380
for key in self.keys():
2381
if key == 'default':
2383
help = self.get_help(key)
2384
if help == default_help:
2385
default_realkey = key
2387
help_pairs.append((key, help))
2389
def wrapped(key, help, info):
2391
help = '(native) ' + help
2392
return ' %s:\n%s\n\n' % (key,
2393
textwrap.fill(help, initial_indent=' ',
2394
subsequent_indent=' '))
2395
output += wrapped('%s/default' % default_realkey, default_help,
2396
self.get_info('default'))
2397
deprecated_pairs = []
2398
for key, help in help_pairs:
2399
info = self.get_info(key)
2402
elif info.deprecated:
2403
deprecated_pairs.append((key, help))
2405
output += wrapped(key, help, info)
2406
if len(deprecated_pairs) > 0:
2407
output += "Deprecated formats\n------------------\n\n"
2408
for key, help in deprecated_pairs:
2409
info = self.get_info(key)
2410
output += wrapped(key, help, info)
2415
format_registry = BzrDirFormatRegistry()
2416
format_registry.register('weave', BzrDirFormat6,
2417
'Pre-0.8 format. Slower than knit and does not'
2418
' support checkouts or shared repositories.',
2420
format_registry.register_metadir('knit',
2421
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2422
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2423
branch_format='bzrlib.branch.BzrBranchFormat5',
2424
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2425
format_registry.register_metadir('metaweave',
2426
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2427
'Transitional format in 0.8. Slower than knit.',
2428
branch_format='bzrlib.branch.BzrBranchFormat5',
2429
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2431
format_registry.register_metadir('dirstate',
2432
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2433
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2434
'above when accessed over the network.',
2435
branch_format='bzrlib.branch.BzrBranchFormat5',
2436
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2437
# directly from workingtree_4 triggers a circular import.
2438
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2440
format_registry.register_metadir('dirstate-tags',
2441
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2442
help='New in 0.15: Fast local operations and improved scaling for '
2443
'network operations. Additionally adds support for tags.'
2444
' Incompatible with bzr < 0.15.',
2445
branch_format='bzrlib.branch.BzrBranchFormat6',
2446
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2448
format_registry.register_metadir('dirstate-with-subtree',
2449
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2450
help='New in 0.15: Fast local operations and improved scaling for '
2451
'network operations. Additionally adds support for versioning nested '
2452
'bzr branches. Incompatible with bzr < 0.15.',
2453
branch_format='bzrlib.branch.BzrBranchFormat6',
2454
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2457
format_registry.set_default('dirstate')