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 (
58
from bzrlib.smart.client import _SmartClient
59
from bzrlib.smart import protocol
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.
157
return self.clone_on_transport(get_transport(url),
158
revision_id=revision_id,
159
force_new_repo=force_new_repo)
161
def clone_on_transport(self, transport, revision_id=None,
162
force_new_repo=False):
163
"""Clone this bzrdir and its contents to transport verbatim.
165
If the target directory does not exist, it will be created.
167
if revision_id is not None, then the clone operation may tune
168
itself to download less data.
169
:param force_new_repo: Do not use a shared repository for the target
170
even if one is available.
172
transport.ensure_base()
173
result = self._format.initialize_on_transport(transport)
175
local_repo = self.find_repository()
176
except errors.NoRepositoryPresent:
179
# may need to copy content in
181
result_repo = local_repo.clone(
183
revision_id=revision_id)
184
result_repo.set_make_working_trees(local_repo.make_working_trees())
187
result_repo = result.find_repository()
188
# fetch content this dir needs.
189
result_repo.fetch(local_repo, revision_id=revision_id)
190
except errors.NoRepositoryPresent:
191
# needed to make one anyway.
192
result_repo = local_repo.clone(
194
revision_id=revision_id)
195
result_repo.set_make_working_trees(local_repo.make_working_trees())
196
# 1 if there is a branch present
197
# make sure its content is available in the target repository
200
self.open_branch().clone(result, revision_id=revision_id)
201
except errors.NotBranchError:
204
self.open_workingtree().clone(result)
205
except (errors.NoWorkingTree, errors.NotLocalUrl):
209
# TODO: This should be given a Transport, and should chdir up; otherwise
210
# this will open a new connection.
211
def _make_tail(self, url):
212
t = get_transport(url)
216
def create(cls, base, format=None, possible_transports=None):
217
"""Create a new BzrDir at the url 'base'.
219
This will call the current default formats initialize with base
220
as the only parameter.
222
:param format: If supplied, the format of branch to create. If not
223
supplied, the default is used.
224
:param possible_transports: If supplied, a list of transports that
225
can be reused to share a remote connection.
227
if cls is not BzrDir:
228
raise AssertionError("BzrDir.create always creates the default"
229
" format, not one of %r" % cls)
230
t = get_transport(base, possible_transports)
233
format = BzrDirFormat.get_default_format()
234
return format.initialize(base, possible_transports)
236
def create_branch(self):
237
"""Create a branch in this BzrDir.
239
The bzrdirs format will control what branch format is created.
240
For more control see BranchFormatXX.create(a_bzrdir).
242
raise NotImplementedError(self.create_branch)
245
def create_branch_and_repo(base, force_new_repo=False, format=None):
246
"""Create a new BzrDir, Branch and Repository at the url 'base'.
248
This will use the current default BzrDirFormat, and use whatever
249
repository format that that uses via bzrdir.create_branch and
250
create_repository. If a shared repository is available that is used
253
The created Branch object is returned.
255
:param base: The URL to create the branch at.
256
:param force_new_repo: If True a new repository is always created.
258
bzrdir = BzrDir.create(base, format)
259
bzrdir._find_or_create_repository(force_new_repo)
260
return bzrdir.create_branch()
262
def _find_or_create_repository(self, force_new_repo):
263
"""Create a new repository if needed, returning the repository."""
265
return self.create_repository()
267
return self.find_repository()
268
except errors.NoRepositoryPresent:
269
return self.create_repository()
272
def create_branch_convenience(base, force_new_repo=False,
273
force_new_tree=None, format=None,
274
possible_transports=None):
275
"""Create a new BzrDir, Branch and Repository at the url 'base'.
277
This is a convenience function - it will use an existing repository
278
if possible, can be told explicitly whether to create a working tree or
281
This will use the current default BzrDirFormat, and use whatever
282
repository format that that uses via bzrdir.create_branch and
283
create_repository. If a shared repository is available that is used
284
preferentially. Whatever repository is used, its tree creation policy
287
The created Branch object is returned.
288
If a working tree cannot be made due to base not being a file:// url,
289
no error is raised unless force_new_tree is True, in which case no
290
data is created on disk and NotLocalUrl is raised.
292
:param base: The URL to create the branch at.
293
:param force_new_repo: If True a new repository is always created.
294
:param force_new_tree: If True or False force creation of a tree or
295
prevent such creation respectively.
296
:param format: Override for the for the bzrdir format to create.
297
:param possible_transports: An optional reusable transports list.
300
# check for non local urls
301
t = get_transport(base, possible_transports)
302
if not isinstance(t, LocalTransport):
303
raise errors.NotLocalUrl(base)
304
bzrdir = BzrDir.create(base, format, possible_transports)
305
repo = bzrdir._find_or_create_repository(force_new_repo)
306
result = bzrdir.create_branch()
307
if force_new_tree or (repo.make_working_trees() and
308
force_new_tree is None):
310
bzrdir.create_workingtree()
311
except errors.NotLocalUrl:
316
def create_repository(base, shared=False, format=None):
317
"""Create a new BzrDir and Repository at the url 'base'.
319
If no format is supplied, this will default to the current default
320
BzrDirFormat by default, and use whatever repository format that that
321
uses for bzrdirformat.create_repository.
323
:param shared: Create a shared repository rather than a standalone
325
The Repository object is returned.
327
This must be overridden as an instance method in child classes, where
328
it should take no parameters and construct whatever repository format
329
that child class desires.
331
bzrdir = BzrDir.create(base, format)
332
return bzrdir.create_repository(shared)
335
def create_standalone_workingtree(base, format=None):
336
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
338
'base' must be a local path or a file:// url.
340
This will use the current default BzrDirFormat, and use whatever
341
repository format that that uses for bzrdirformat.create_workingtree,
342
create_branch and create_repository.
344
:return: The WorkingTree object.
346
t = get_transport(base)
347
if not isinstance(t, LocalTransport):
348
raise errors.NotLocalUrl(base)
349
bzrdir = BzrDir.create_branch_and_repo(base,
351
format=format).bzrdir
352
return bzrdir.create_workingtree()
354
def create_workingtree(self, revision_id=None):
355
"""Create a working tree at this BzrDir.
357
revision_id: create it as of this revision id.
359
raise NotImplementedError(self.create_workingtree)
361
def retire_bzrdir(self):
362
"""Permanently disable the bzrdir.
364
This is done by renaming it to give the user some ability to recover
365
if there was a problem.
367
This will have horrible consequences if anyone has anything locked or
370
for i in xrange(10000):
372
to_path = '.bzr.retired.%d' % i
373
self.root_transport.rename('.bzr', to_path)
374
note("renamed %s to %s"
375
% (self.root_transport.abspath('.bzr'), to_path))
377
except (errors.TransportError, IOError, errors.PathError):
380
def destroy_workingtree(self):
381
"""Destroy the working tree at this BzrDir.
383
Formats that do not support this may raise UnsupportedOperation.
385
raise NotImplementedError(self.destroy_workingtree)
387
def destroy_workingtree_metadata(self):
388
"""Destroy the control files for the working tree at this BzrDir.
390
The contents of working tree files are not affected.
391
Formats that do not support this may raise UnsupportedOperation.
393
raise NotImplementedError(self.destroy_workingtree_metadata)
395
def find_repository(self):
396
"""Find the repository that should be used for a_bzrdir.
398
This does not require a branch as we use it to find the repo for
399
new branches as well as to hook existing branches up to their
403
return self.open_repository()
404
except errors.NoRepositoryPresent:
406
next_transport = self.root_transport.clone('..')
408
# find the next containing bzrdir
410
found_bzrdir = BzrDir.open_containing_from_transport(
412
except errors.NotBranchError:
414
raise errors.NoRepositoryPresent(self)
415
# does it have a repository ?
417
repository = found_bzrdir.open_repository()
418
except errors.NoRepositoryPresent:
419
next_transport = found_bzrdir.root_transport.clone('..')
420
if (found_bzrdir.root_transport.base == next_transport.base):
421
# top of the file system
425
if ((found_bzrdir.root_transport.base ==
426
self.root_transport.base) or repository.is_shared()):
429
raise errors.NoRepositoryPresent(self)
430
raise errors.NoRepositoryPresent(self)
432
def get_branch_reference(self):
433
"""Return the referenced URL for the branch in this bzrdir.
435
:raises NotBranchError: If there is no Branch.
436
:return: The URL the branch in this bzrdir references if it is a
437
reference branch, or None for regular branches.
441
def get_branch_transport(self, branch_format):
442
"""Get the transport for use by branch format in this BzrDir.
444
Note that bzr dirs that do not support format strings will raise
445
IncompatibleFormat if the branch format they are given has
446
a format string, and vice versa.
448
If branch_format is None, the transport is returned with no
449
checking. if it is not None, then the returned transport is
450
guaranteed to point to an existing directory ready for use.
452
raise NotImplementedError(self.get_branch_transport)
454
def get_repository_transport(self, repository_format):
455
"""Get the transport for use by repository format in this BzrDir.
457
Note that bzr dirs that do not support format strings will raise
458
IncompatibleFormat if the repository format they are given has
459
a format string, and vice versa.
461
If repository_format is None, the transport is returned with no
462
checking. if it is not None, then the returned transport is
463
guaranteed to point to an existing directory ready for use.
465
raise NotImplementedError(self.get_repository_transport)
467
def get_workingtree_transport(self, tree_format):
468
"""Get the transport for use by workingtree format in this BzrDir.
470
Note that bzr dirs that do not support format strings will raise
471
IncompatibleFormat if the workingtree format they are given has a
472
format string, and vice versa.
474
If workingtree_format is None, the transport is returned with no
475
checking. if it is not None, then the returned transport is
476
guaranteed to point to an existing directory ready for use.
478
raise NotImplementedError(self.get_workingtree_transport)
480
def __init__(self, _transport, _format):
481
"""Initialize a Bzr control dir object.
483
Only really common logic should reside here, concrete classes should be
484
made with varying behaviours.
486
:param _format: the format that is creating this BzrDir instance.
487
:param _transport: the transport this dir is based at.
489
self._format = _format
490
self.transport = _transport.clone('.bzr')
491
self.root_transport = _transport
493
def is_control_filename(self, filename):
494
"""True if filename is the name of a path which is reserved for bzrdir's.
496
:param filename: A filename within the root transport of this bzrdir.
498
This is true IF and ONLY IF the filename is part of the namespace reserved
499
for bzr control dirs. Currently this is the '.bzr' directory in the root
500
of the root_transport. it is expected that plugins will need to extend
501
this in the future - for instance to make bzr talk with svn working
504
# this might be better on the BzrDirFormat class because it refers to
505
# all the possible bzrdir disk formats.
506
# This method is tested via the workingtree is_control_filename tests-
507
# it was extracted from WorkingTree.is_control_filename. If the methods
508
# contract is extended beyond the current trivial implementation please
509
# add new tests for it to the appropriate place.
510
return filename == '.bzr' or filename.startswith('.bzr/')
512
def needs_format_conversion(self, format=None):
513
"""Return true if this bzrdir needs convert_format run on it.
515
For instance, if the repository format is out of date but the
516
branch and working tree are not, this should return True.
518
:param format: Optional parameter indicating a specific desired
519
format we plan to arrive at.
521
raise NotImplementedError(self.needs_format_conversion)
524
def open_unsupported(base):
525
"""Open a branch which is not supported."""
526
return BzrDir.open(base, _unsupported=True)
529
def open(base, _unsupported=False):
530
"""Open an existing bzrdir, rooted at 'base' (url)
532
_unsupported is a private parameter to the BzrDir class.
534
t = get_transport(base)
535
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
538
def open_from_transport(transport, _unsupported=False,
539
_server_formats=True):
540
"""Open a bzrdir within a particular directory.
542
:param transport: Transport containing the bzrdir.
543
:param _unsupported: private.
545
base = transport.base
547
def find_format(transport):
548
return transport, BzrDirFormat.find_format(
549
transport, _server_formats=_server_formats)
551
def redirected(transport, e, redirection_notice):
552
qualified_source = e.get_source_url()
553
relpath = transport.relpath(qualified_source)
554
if not e.target.endswith(relpath):
555
# Not redirected to a branch-format, not a branch
556
raise errors.NotBranchError(path=e.target)
557
target = e.target[:-len(relpath)]
558
note('%s is%s redirected to %s',
559
transport.base, e.permanently, target)
560
# Let's try with a new transport
561
qualified_target = e.get_target_url()[:-len(relpath)]
562
# FIXME: If 'transport' has a qualifier, this should
563
# be applied again to the new transport *iff* the
564
# schemes used are the same. It's a bit tricky to
565
# verify, so I'll punt for now
567
return get_transport(target)
570
transport, format = do_catching_redirections(find_format,
573
except errors.TooManyRedirections:
574
raise errors.NotBranchError(base)
576
BzrDir._check_supported(format, _unsupported)
577
return format.open(transport, _found=True)
579
def open_branch(self, unsupported=False):
580
"""Open the branch object at this BzrDir if one is present.
582
If unsupported is True, then no longer supported branch formats can
585
TODO: static convenience version of this?
587
raise NotImplementedError(self.open_branch)
590
def open_containing(url, possible_transports=None):
591
"""Open an existing branch which contains url.
593
:param url: url to search from.
594
See open_containing_from_transport for more detail.
596
transport = get_transport(url, possible_transports)
597
return BzrDir.open_containing_from_transport(transport)
600
def open_containing_from_transport(a_transport):
601
"""Open an existing branch which contains a_transport.base
603
This probes for a branch at a_transport, and searches upwards from there.
605
Basically we keep looking up until we find the control directory or
606
run into the root. If there isn't one, raises NotBranchError.
607
If there is one and it is either an unrecognised format or an unsupported
608
format, UnknownFormatError or UnsupportedFormatError are raised.
609
If there is one, it is returned, along with the unused portion of url.
611
:return: The BzrDir that contains the path, and a Unicode path
612
for the rest of the URL.
614
# this gets the normalised url back. I.e. '.' -> the full path.
615
url = a_transport.base
618
result = BzrDir.open_from_transport(a_transport)
619
return result, urlutils.unescape(a_transport.relpath(url))
620
except errors.NotBranchError, e:
623
new_t = a_transport.clone('..')
624
except errors.InvalidURLJoin:
625
# reached the root, whatever that may be
626
raise errors.NotBranchError(path=url)
627
if new_t.base == a_transport.base:
628
# reached the root, whatever that may be
629
raise errors.NotBranchError(path=url)
633
def open_containing_tree_or_branch(klass, location):
634
"""Return the branch and working tree contained by a location.
636
Returns (tree, branch, relpath).
637
If there is no tree at containing the location, tree will be None.
638
If there is no branch containing the location, an exception will be
640
relpath is the portion of the path that is contained by the branch.
642
bzrdir, relpath = klass.open_containing(location)
644
tree = bzrdir.open_workingtree()
645
except (errors.NoWorkingTree, errors.NotLocalUrl):
647
branch = bzrdir.open_branch()
650
return tree, branch, relpath
652
def open_repository(self, _unsupported=False):
653
"""Open the repository object at this BzrDir if one is present.
655
This will not follow the Branch object pointer - its strictly a direct
656
open facility. Most client code should use open_branch().repository to
659
_unsupported is a private parameter, not part of the api.
660
TODO: static convenience version of this?
662
raise NotImplementedError(self.open_repository)
664
def open_workingtree(self, _unsupported=False,
665
recommend_upgrade=True):
666
"""Open the workingtree object at this BzrDir if one is present.
668
:param recommend_upgrade: Optional keyword parameter, when True (the
669
default), emit through the ui module a recommendation that the user
670
upgrade the working tree when the workingtree being opened is old
671
(but still fully supported).
673
raise NotImplementedError(self.open_workingtree)
675
def has_branch(self):
676
"""Tell if this bzrdir contains a branch.
678
Note: if you're going to open the branch, you should just go ahead
679
and try, and not ask permission first. (This method just opens the
680
branch and discards it, and that's somewhat expensive.)
685
except errors.NotBranchError:
688
def has_workingtree(self):
689
"""Tell if this bzrdir contains a working tree.
691
This will still raise an exception if the bzrdir has a workingtree that
692
is remote & inaccessible.
694
Note: if you're going to open the working tree, you should just go ahead
695
and try, and not ask permission first. (This method just opens the
696
workingtree and discards it, and that's somewhat expensive.)
699
self.open_workingtree(recommend_upgrade=False)
701
except errors.NoWorkingTree:
704
def _cloning_metadir(self):
705
"""Produce a metadir suitable for cloning with"""
706
result_format = self._format.__class__()
709
branch = self.open_branch()
710
source_repository = branch.repository
711
except errors.NotBranchError:
713
source_repository = self.open_repository()
714
except errors.NoRepositoryPresent:
715
source_repository = None
717
# XXX TODO: This isinstance is here because we have not implemented
718
# the fix recommended in bug # 103195 - to delegate this choice the
720
repo_format = source_repository._format
721
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
722
result_format.repository_format = repo_format
724
# TODO: Couldn't we just probe for the format in these cases,
725
# rather than opening the whole tree? It would be a little
726
# faster. mbp 20070401
727
tree = self.open_workingtree(recommend_upgrade=False)
728
except (errors.NoWorkingTree, errors.NotLocalUrl):
729
result_format.workingtree_format = None
731
result_format.workingtree_format = tree._format.__class__()
732
return result_format, source_repository
734
def cloning_metadir(self):
735
"""Produce a metadir suitable for cloning or sprouting with.
737
These operations may produce workingtrees (yes, even though they're
738
"cloning" something that doesn't have a tree, so a viable workingtree
739
format must be selected.
741
format, repository = self._cloning_metadir()
742
if format._workingtree_format is None:
743
if repository is None:
745
tree_format = repository._format._matchingbzrdir.workingtree_format
746
format.workingtree_format = tree_format.__class__()
749
def checkout_metadir(self):
750
return self.cloning_metadir()
752
def sprout(self, url, revision_id=None, force_new_repo=False,
754
"""Create a copy of this bzrdir prepared for use as a new line of
757
If urls last component does not exist, it will be created.
759
Attributes related to the identity of the source branch like
760
branch nickname will be cleaned, a working tree is created
761
whether one existed before or not; and a local branch is always
764
if revision_id is not None, then the clone operation may tune
765
itself to download less data.
767
target_transport = get_transport(url)
768
target_transport.ensure_base()
769
cloning_format = self.cloning_metadir()
770
result = cloning_format.initialize_on_transport(target_transport)
772
source_branch = self.open_branch()
773
source_repository = source_branch.repository
774
except errors.NotBranchError:
777
source_repository = self.open_repository()
778
except errors.NoRepositoryPresent:
779
source_repository = None
784
result_repo = result.find_repository()
785
except errors.NoRepositoryPresent:
787
if source_repository is None and result_repo is not None:
789
elif source_repository is None and result_repo is None:
790
# no repo available, make a new one
791
result.create_repository()
792
elif source_repository is not None and result_repo is None:
793
# have source, and want to make a new target repo
794
result_repo = source_repository.sprout(result, revision_id=revision_id)
796
# fetch needed content into target.
797
if source_repository is not None:
799
# source_repository.copy_content_into(result_repo, revision_id=revision_id)
800
# so we can override the copy method
801
result_repo.fetch(source_repository, revision_id=revision_id)
802
if source_branch is not None:
803
source_branch.sprout(result, revision_id=revision_id)
805
result.create_branch()
806
# TODO: jam 20060426 we probably need a test in here in the
807
# case that the newly sprouted branch is a remote one
808
if result_repo is None or result_repo.make_working_trees():
809
wt = result.create_workingtree()
812
if wt.path2id('') is None:
814
wt.set_root_id(self.open_workingtree.get_root_id())
815
except errors.NoWorkingTree:
821
if recurse == 'down':
823
basis = wt.basis_tree()
825
subtrees = basis.iter_references()
826
recurse_branch = wt.branch
827
elif source_branch is not None:
828
basis = source_branch.basis_tree()
830
subtrees = basis.iter_references()
831
recurse_branch = source_branch
836
for path, file_id in subtrees:
837
target = urlutils.join(url, urlutils.escape(path))
838
sublocation = source_branch.reference_parent(file_id, path)
839
sublocation.bzrdir.sprout(target,
840
basis.get_reference_revision(file_id, path),
841
force_new_repo=force_new_repo, recurse=recurse)
843
if basis is not None:
848
class BzrDirPreSplitOut(BzrDir):
849
"""A common class for the all-in-one formats."""
851
def __init__(self, _transport, _format):
852
"""See BzrDir.__init__."""
853
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
854
assert self._format._lock_class == lockable_files.TransportLock
855
assert self._format._lock_file_name == 'branch-lock'
856
self._control_files = lockable_files.LockableFiles(
857
self.get_branch_transport(None),
858
self._format._lock_file_name,
859
self._format._lock_class)
861
def break_lock(self):
862
"""Pre-splitout bzrdirs do not suffer from stale locks."""
863
raise NotImplementedError(self.break_lock)
865
def clone(self, url, revision_id=None, force_new_repo=False):
866
"""See BzrDir.clone()."""
867
from bzrlib.workingtree import WorkingTreeFormat2
869
result = self._format._initialize_for_clone(url)
870
self.open_repository().clone(result, revision_id=revision_id)
871
from_branch = self.open_branch()
872
from_branch.clone(result, revision_id=revision_id)
874
self.open_workingtree().clone(result)
875
except errors.NotLocalUrl:
876
# make a new one, this format always has to have one.
878
WorkingTreeFormat2().initialize(result)
879
except errors.NotLocalUrl:
880
# but we cannot do it for remote trees.
881
to_branch = result.open_branch()
882
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
885
def create_branch(self):
886
"""See BzrDir.create_branch."""
887
return self.open_branch()
889
def create_repository(self, shared=False):
890
"""See BzrDir.create_repository."""
892
raise errors.IncompatibleFormat('shared repository', self._format)
893
return self.open_repository()
895
def create_workingtree(self, revision_id=None):
896
"""See BzrDir.create_workingtree."""
897
# this looks buggy but is not -really-
898
# because this format creates the workingtree when the bzrdir is
900
# clone and sprout will have set the revision_id
901
# and that will have set it for us, its only
902
# specific uses of create_workingtree in isolation
903
# that can do wonky stuff here, and that only
904
# happens for creating checkouts, which cannot be
905
# done on this format anyway. So - acceptable wart.
906
result = self.open_workingtree(recommend_upgrade=False)
907
if revision_id is not None:
908
if revision_id == _mod_revision.NULL_REVISION:
909
result.set_parent_ids([])
911
result.set_parent_ids([revision_id])
914
def destroy_workingtree(self):
915
"""See BzrDir.destroy_workingtree."""
916
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
918
def destroy_workingtree_metadata(self):
919
"""See BzrDir.destroy_workingtree_metadata."""
920
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
923
def get_branch_transport(self, branch_format):
924
"""See BzrDir.get_branch_transport()."""
925
if branch_format is None:
926
return self.transport
928
branch_format.get_format_string()
929
except NotImplementedError:
930
return self.transport
931
raise errors.IncompatibleFormat(branch_format, self._format)
933
def get_repository_transport(self, repository_format):
934
"""See BzrDir.get_repository_transport()."""
935
if repository_format is None:
936
return self.transport
938
repository_format.get_format_string()
939
except NotImplementedError:
940
return self.transport
941
raise errors.IncompatibleFormat(repository_format, self._format)
943
def get_workingtree_transport(self, workingtree_format):
944
"""See BzrDir.get_workingtree_transport()."""
945
if workingtree_format is None:
946
return self.transport
948
workingtree_format.get_format_string()
949
except NotImplementedError:
950
return self.transport
951
raise errors.IncompatibleFormat(workingtree_format, self._format)
953
def needs_format_conversion(self, format=None):
954
"""See BzrDir.needs_format_conversion()."""
955
# if the format is not the same as the system default,
956
# an upgrade is needed.
958
format = BzrDirFormat.get_default_format()
959
return not isinstance(self._format, format.__class__)
961
def open_branch(self, unsupported=False):
962
"""See BzrDir.open_branch."""
963
from bzrlib.branch import BzrBranchFormat4
964
format = BzrBranchFormat4()
965
self._check_supported(format, unsupported)
966
return format.open(self, _found=True)
968
def sprout(self, url, revision_id=None, force_new_repo=False):
969
"""See BzrDir.sprout()."""
970
from bzrlib.workingtree import WorkingTreeFormat2
972
result = self._format._initialize_for_clone(url)
974
self.open_repository().clone(result, revision_id=revision_id)
975
except errors.NoRepositoryPresent:
978
self.open_branch().sprout(result, revision_id=revision_id)
979
except errors.NotBranchError:
981
# we always want a working tree
982
WorkingTreeFormat2().initialize(result)
986
class BzrDir4(BzrDirPreSplitOut):
987
"""A .bzr version 4 control object.
989
This is a deprecated format and may be removed after sept 2006.
992
def create_repository(self, shared=False):
993
"""See BzrDir.create_repository."""
994
return self._format.repository_format.initialize(self, shared)
996
def needs_format_conversion(self, format=None):
997
"""Format 4 dirs are always in need of conversion."""
1000
def open_repository(self):
1001
"""See BzrDir.open_repository."""
1002
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1003
return RepositoryFormat4().open(self, _found=True)
1006
class BzrDir5(BzrDirPreSplitOut):
1007
"""A .bzr version 5 control object.
1009
This is a deprecated format and may be removed after sept 2006.
1012
def open_repository(self):
1013
"""See BzrDir.open_repository."""
1014
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1015
return RepositoryFormat5().open(self, _found=True)
1017
def open_workingtree(self, _unsupported=False,
1018
recommend_upgrade=True):
1019
"""See BzrDir.create_workingtree."""
1020
from bzrlib.workingtree import WorkingTreeFormat2
1021
wt_format = WorkingTreeFormat2()
1022
# we don't warn here about upgrades; that ought to be handled for the
1024
return wt_format.open(self, _found=True)
1027
class BzrDir6(BzrDirPreSplitOut):
1028
"""A .bzr version 6 control object.
1030
This is a deprecated format and may be removed after sept 2006.
1033
def open_repository(self):
1034
"""See BzrDir.open_repository."""
1035
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1036
return RepositoryFormat6().open(self, _found=True)
1038
def open_workingtree(self, _unsupported=False,
1039
recommend_upgrade=True):
1040
"""See BzrDir.create_workingtree."""
1041
# we don't warn here about upgrades; that ought to be handled for the
1043
from bzrlib.workingtree import WorkingTreeFormat2
1044
return WorkingTreeFormat2().open(self, _found=True)
1047
class BzrDirMeta1(BzrDir):
1048
"""A .bzr meta version 1 control object.
1050
This is the first control object where the
1051
individual aspects are really split out: there are separate repository,
1052
workingtree and branch subdirectories and any subset of the three can be
1053
present within a BzrDir.
1056
def can_convert_format(self):
1057
"""See BzrDir.can_convert_format()."""
1060
def create_branch(self):
1061
"""See BzrDir.create_branch."""
1062
return self._format.get_branch_format().initialize(self)
1064
def create_repository(self, shared=False):
1065
"""See BzrDir.create_repository."""
1066
return self._format.repository_format.initialize(self, shared)
1068
def create_workingtree(self, revision_id=None):
1069
"""See BzrDir.create_workingtree."""
1070
from bzrlib.workingtree import WorkingTreeFormat
1071
return self._format.workingtree_format.initialize(self, revision_id)
1073
def destroy_workingtree(self):
1074
"""See BzrDir.destroy_workingtree."""
1075
wt = self.open_workingtree(recommend_upgrade=False)
1076
repository = wt.branch.repository
1077
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1078
wt.revert([], old_tree=empty)
1079
self.destroy_workingtree_metadata()
1081
def destroy_workingtree_metadata(self):
1082
self.transport.delete_tree('checkout')
1084
def find_branch_format(self):
1085
"""Find the branch 'format' for this bzrdir.
1087
This might be a synthetic object for e.g. RemoteBranch and SVN.
1089
from bzrlib.branch import BranchFormat
1090
return BranchFormat.find_format(self)
1092
def _get_mkdir_mode(self):
1093
"""Figure out the mode to use when creating a bzrdir subdir."""
1094
temp_control = lockable_files.LockableFiles(self.transport, '',
1095
lockable_files.TransportLock)
1096
return temp_control._dir_mode
1098
def get_branch_reference(self):
1099
"""See BzrDir.get_branch_reference()."""
1100
from bzrlib.branch import BranchFormat
1101
format = BranchFormat.find_format(self)
1102
return format.get_reference(self)
1104
def get_branch_transport(self, branch_format):
1105
"""See BzrDir.get_branch_transport()."""
1106
if branch_format is None:
1107
return self.transport.clone('branch')
1109
branch_format.get_format_string()
1110
except NotImplementedError:
1111
raise errors.IncompatibleFormat(branch_format, self._format)
1113
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1114
except errors.FileExists:
1116
return self.transport.clone('branch')
1118
def get_repository_transport(self, repository_format):
1119
"""See BzrDir.get_repository_transport()."""
1120
if repository_format is None:
1121
return self.transport.clone('repository')
1123
repository_format.get_format_string()
1124
except NotImplementedError:
1125
raise errors.IncompatibleFormat(repository_format, self._format)
1127
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1128
except errors.FileExists:
1130
return self.transport.clone('repository')
1132
def get_workingtree_transport(self, workingtree_format):
1133
"""See BzrDir.get_workingtree_transport()."""
1134
if workingtree_format is None:
1135
return self.transport.clone('checkout')
1137
workingtree_format.get_format_string()
1138
except NotImplementedError:
1139
raise errors.IncompatibleFormat(workingtree_format, self._format)
1141
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1142
except errors.FileExists:
1144
return self.transport.clone('checkout')
1146
def needs_format_conversion(self, format=None):
1147
"""See BzrDir.needs_format_conversion()."""
1149
format = BzrDirFormat.get_default_format()
1150
if not isinstance(self._format, format.__class__):
1151
# it is not a meta dir format, conversion is needed.
1153
# we might want to push this down to the repository?
1155
if not isinstance(self.open_repository()._format,
1156
format.repository_format.__class__):
1157
# the repository needs an upgrade.
1159
except errors.NoRepositoryPresent:
1162
if not isinstance(self.open_branch()._format,
1163
format.get_branch_format().__class__):
1164
# the branch needs an upgrade.
1166
except errors.NotBranchError:
1169
my_wt = self.open_workingtree(recommend_upgrade=False)
1170
if not isinstance(my_wt._format,
1171
format.workingtree_format.__class__):
1172
# the workingtree needs an upgrade.
1174
except (errors.NoWorkingTree, errors.NotLocalUrl):
1178
def open_branch(self, unsupported=False):
1179
"""See BzrDir.open_branch."""
1180
format = self.find_branch_format()
1181
self._check_supported(format, unsupported)
1182
return format.open(self, _found=True)
1184
def open_repository(self, unsupported=False):
1185
"""See BzrDir.open_repository."""
1186
from bzrlib.repository import RepositoryFormat
1187
format = RepositoryFormat.find_format(self)
1188
self._check_supported(format, unsupported)
1189
return format.open(self, _found=True)
1191
def open_workingtree(self, unsupported=False,
1192
recommend_upgrade=True):
1193
"""See BzrDir.open_workingtree."""
1194
from bzrlib.workingtree import WorkingTreeFormat
1195
format = WorkingTreeFormat.find_format(self)
1196
self._check_supported(format, unsupported,
1198
basedir=self.root_transport.base)
1199
return format.open(self, _found=True)
1202
class BzrDirFormat(object):
1203
"""An encapsulation of the initialization and open routines for a format.
1205
Formats provide three things:
1206
* An initialization routine,
1210
Formats are placed in an dict by their format string for reference
1211
during bzrdir opening. These should be subclasses of BzrDirFormat
1214
Once a format is deprecated, just deprecate the initialize and open
1215
methods on the format class. Do not deprecate the object, as the
1216
object will be created every system load.
1219
_default_format = None
1220
"""The default format used for new .bzr dirs."""
1223
"""The known formats."""
1225
_control_formats = []
1226
"""The registered control formats - .bzr, ....
1228
This is a list of BzrDirFormat objects.
1231
_control_server_formats = []
1232
"""The registered control server formats, e.g. RemoteBzrDirs.
1234
This is a list of BzrDirFormat objects.
1237
_lock_file_name = 'branch-lock'
1239
# _lock_class must be set in subclasses to the lock type, typ.
1240
# TransportLock or LockDir
1243
def find_format(klass, transport, _server_formats=True):
1244
"""Return the format present at transport."""
1246
formats = klass._control_server_formats + klass._control_formats
1248
formats = klass._control_formats
1249
for format in formats:
1251
return format.probe_transport(transport)
1252
except errors.NotBranchError:
1253
# this format does not find a control dir here.
1255
raise errors.NotBranchError(path=transport.base)
1258
def probe_transport(klass, transport):
1259
"""Return the .bzrdir style format present in a directory."""
1261
format_string = transport.get(".bzr/branch-format").read()
1262
except errors.NoSuchFile:
1263
raise errors.NotBranchError(path=transport.base)
1266
return klass._formats[format_string]
1268
raise errors.UnknownFormatError(format=format_string)
1271
def get_default_format(klass):
1272
"""Return the current default format."""
1273
return klass._default_format
1275
def get_format_string(self):
1276
"""Return the ASCII format string that identifies this format."""
1277
raise NotImplementedError(self.get_format_string)
1279
def get_format_description(self):
1280
"""Return the short description for this format."""
1281
raise NotImplementedError(self.get_format_description)
1283
def get_converter(self, format=None):
1284
"""Return the converter to use to convert bzrdirs needing converts.
1286
This returns a bzrlib.bzrdir.Converter object.
1288
This should return the best upgrader to step this format towards the
1289
current default format. In the case of plugins we can/should provide
1290
some means for them to extend the range of returnable converters.
1292
:param format: Optional format to override the default format of the
1295
raise NotImplementedError(self.get_converter)
1297
def initialize(self, url, possible_transports=None):
1298
"""Create a bzr control dir at this url and return an opened copy.
1300
Subclasses should typically override initialize_on_transport
1301
instead of this method.
1303
return self.initialize_on_transport(get_transport(url,
1304
possible_transports))
1306
def initialize_on_transport(self, transport):
1307
"""Initialize a new bzrdir in the base directory of a Transport."""
1308
# Since we don't have a .bzr directory, inherit the
1309
# mode from the root directory
1310
temp_control = lockable_files.LockableFiles(transport,
1311
'', lockable_files.TransportLock)
1312
temp_control._transport.mkdir('.bzr',
1313
# FIXME: RBC 20060121 don't peek under
1315
mode=temp_control._dir_mode)
1316
file_mode = temp_control._file_mode
1318
mutter('created control directory in ' + transport.base)
1319
control = transport.clone('.bzr')
1320
utf8_files = [('README',
1321
"This is a Bazaar-NG control directory.\n"
1322
"Do not change any files in this directory.\n"),
1323
('branch-format', self.get_format_string()),
1325
# NB: no need to escape relative paths that are url safe.
1326
control_files = lockable_files.LockableFiles(control,
1327
self._lock_file_name, self._lock_class)
1328
control_files.create_lock()
1329
control_files.lock_write()
1331
for file, content in utf8_files:
1332
control_files.put_utf8(file, content)
1334
control_files.unlock()
1335
return self.open(transport, _found=True)
1337
def is_supported(self):
1338
"""Is this format supported?
1340
Supported formats must be initializable and openable.
1341
Unsupported formats may not support initialization or committing or
1342
some other features depending on the reason for not being supported.
1346
def same_model(self, target_format):
1347
return (self.repository_format.rich_root_data ==
1348
target_format.rich_root_data)
1351
def known_formats(klass):
1352
"""Return all the known formats.
1354
Concrete formats should override _known_formats.
1356
# There is double indirection here to make sure that control
1357
# formats used by more than one dir format will only be probed
1358
# once. This can otherwise be quite expensive for remote connections.
1360
for format in klass._control_formats:
1361
result.update(format._known_formats())
1365
def _known_formats(klass):
1366
"""Return the known format instances for this control format."""
1367
return set(klass._formats.values())
1369
def open(self, transport, _found=False):
1370
"""Return an instance of this format for the dir transport points at.
1372
_found is a private parameter, do not use it.
1375
found_format = BzrDirFormat.find_format(transport)
1376
if not isinstance(found_format, self.__class__):
1377
raise AssertionError("%s was asked to open %s, but it seems to need "
1379
% (self, transport, found_format))
1380
return self._open(transport)
1382
def _open(self, transport):
1383
"""Template method helper for opening BzrDirectories.
1385
This performs the actual open and any additional logic or parameter
1388
raise NotImplementedError(self._open)
1391
def register_format(klass, format):
1392
klass._formats[format.get_format_string()] = format
1395
def register_control_format(klass, format):
1396
"""Register a format that does not use '.bzr' for its control dir.
1398
TODO: This should be pulled up into a 'ControlDirFormat' base class
1399
which BzrDirFormat can inherit from, and renamed to register_format
1400
there. It has been done without that for now for simplicity of
1403
klass._control_formats.append(format)
1406
def register_control_server_format(klass, format):
1407
"""Register a control format for client-server environments.
1409
These formats will be tried before ones registered with
1410
register_control_format. This gives implementations that decide to the
1411
chance to grab it before anything looks at the contents of the format
1414
klass._control_server_formats.append(format)
1417
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1418
def set_default_format(klass, format):
1419
klass._set_default_format(format)
1422
def _set_default_format(klass, format):
1423
"""Set default format (for testing behavior of defaults only)"""
1424
klass._default_format = format
1427
return self.get_format_string()[:-1]
1430
def unregister_format(klass, format):
1431
assert klass._formats[format.get_format_string()] is format
1432
del klass._formats[format.get_format_string()]
1435
def unregister_control_format(klass, format):
1436
klass._control_formats.remove(format)
1439
class BzrDirFormat4(BzrDirFormat):
1440
"""Bzr dir format 4.
1442
This format is a combined format for working tree, branch and repository.
1444
- Format 1 working trees [always]
1445
- Format 4 branches [always]
1446
- Format 4 repositories [always]
1448
This format is deprecated: it indexes texts using a text it which is
1449
removed in format 5; write support for this format has been removed.
1452
_lock_class = lockable_files.TransportLock
1454
def get_format_string(self):
1455
"""See BzrDirFormat.get_format_string()."""
1456
return "Bazaar-NG branch, format 0.0.4\n"
1458
def get_format_description(self):
1459
"""See BzrDirFormat.get_format_description()."""
1460
return "All-in-one format 4"
1462
def get_converter(self, format=None):
1463
"""See BzrDirFormat.get_converter()."""
1464
# there is one and only one upgrade path here.
1465
return ConvertBzrDir4To5()
1467
def initialize_on_transport(self, transport):
1468
"""Format 4 branches cannot be created."""
1469
raise errors.UninitializableFormat(self)
1471
def is_supported(self):
1472
"""Format 4 is not supported.
1474
It is not supported because the model changed from 4 to 5 and the
1475
conversion logic is expensive - so doing it on the fly was not
1480
def _open(self, transport):
1481
"""See BzrDirFormat._open."""
1482
return BzrDir4(transport, self)
1484
def __return_repository_format(self):
1485
"""Circular import protection."""
1486
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1487
return RepositoryFormat4()
1488
repository_format = property(__return_repository_format)
1491
class BzrDirFormat5(BzrDirFormat):
1492
"""Bzr control format 5.
1494
This format is a combined format for working tree, branch and repository.
1496
- Format 2 working trees [always]
1497
- Format 4 branches [always]
1498
- Format 5 repositories [always]
1499
Unhashed stores in the repository.
1502
_lock_class = lockable_files.TransportLock
1504
def get_format_string(self):
1505
"""See BzrDirFormat.get_format_string()."""
1506
return "Bazaar-NG branch, format 5\n"
1508
def get_format_description(self):
1509
"""See BzrDirFormat.get_format_description()."""
1510
return "All-in-one format 5"
1512
def get_converter(self, format=None):
1513
"""See BzrDirFormat.get_converter()."""
1514
# there is one and only one upgrade path here.
1515
return ConvertBzrDir5To6()
1517
def _initialize_for_clone(self, url):
1518
return self.initialize_on_transport(get_transport(url), _cloning=True)
1520
def initialize_on_transport(self, transport, _cloning=False):
1521
"""Format 5 dirs always have working tree, branch and repository.
1523
Except when they are being cloned.
1525
from bzrlib.branch import BzrBranchFormat4
1526
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1527
from bzrlib.workingtree import WorkingTreeFormat2
1528
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1529
RepositoryFormat5().initialize(result, _internal=True)
1531
branch = BzrBranchFormat4().initialize(result)
1533
WorkingTreeFormat2().initialize(result)
1534
except errors.NotLocalUrl:
1535
# Even though we can't access the working tree, we need to
1536
# create its control files.
1537
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1540
def _open(self, transport):
1541
"""See BzrDirFormat._open."""
1542
return BzrDir5(transport, self)
1544
def __return_repository_format(self):
1545
"""Circular import protection."""
1546
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1547
return RepositoryFormat5()
1548
repository_format = property(__return_repository_format)
1551
class BzrDirFormat6(BzrDirFormat):
1552
"""Bzr control format 6.
1554
This format is a combined format for working tree, branch and repository.
1556
- Format 2 working trees [always]
1557
- Format 4 branches [always]
1558
- Format 6 repositories [always]
1561
_lock_class = lockable_files.TransportLock
1563
def get_format_string(self):
1564
"""See BzrDirFormat.get_format_string()."""
1565
return "Bazaar-NG branch, format 6\n"
1567
def get_format_description(self):
1568
"""See BzrDirFormat.get_format_description()."""
1569
return "All-in-one format 6"
1571
def get_converter(self, format=None):
1572
"""See BzrDirFormat.get_converter()."""
1573
# there is one and only one upgrade path here.
1574
return ConvertBzrDir6ToMeta()
1576
def _initialize_for_clone(self, url):
1577
return self.initialize_on_transport(get_transport(url), _cloning=True)
1579
def initialize_on_transport(self, transport, _cloning=False):
1580
"""Format 6 dirs always have working tree, branch and repository.
1582
Except when they are being cloned.
1584
from bzrlib.branch import BzrBranchFormat4
1585
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1586
from bzrlib.workingtree import WorkingTreeFormat2
1587
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1588
RepositoryFormat6().initialize(result, _internal=True)
1590
branch = BzrBranchFormat4().initialize(result)
1592
WorkingTreeFormat2().initialize(result)
1593
except errors.NotLocalUrl:
1594
# Even though we can't access the working tree, we need to
1595
# create its control files.
1596
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1599
def _open(self, transport):
1600
"""See BzrDirFormat._open."""
1601
return BzrDir6(transport, self)
1603
def __return_repository_format(self):
1604
"""Circular import protection."""
1605
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1606
return RepositoryFormat6()
1607
repository_format = property(__return_repository_format)
1610
class BzrDirMetaFormat1(BzrDirFormat):
1611
"""Bzr meta control format 1
1613
This is the first format with split out working tree, branch and repository
1616
- Format 3 working trees [optional]
1617
- Format 5 branches [optional]
1618
- Format 7 repositories [optional]
1621
_lock_class = lockdir.LockDir
1624
self._workingtree_format = None
1625
self._branch_format = None
1627
def __eq__(self, other):
1628
if other.__class__ is not self.__class__:
1630
if other.repository_format != self.repository_format:
1632
if other.workingtree_format != self.workingtree_format:
1636
def __ne__(self, other):
1637
return not self == other
1639
def get_branch_format(self):
1640
if self._branch_format is None:
1641
from bzrlib.branch import BranchFormat
1642
self._branch_format = BranchFormat.get_default_format()
1643
return self._branch_format
1645
def set_branch_format(self, format):
1646
self._branch_format = format
1648
def get_converter(self, format=None):
1649
"""See BzrDirFormat.get_converter()."""
1651
format = BzrDirFormat.get_default_format()
1652
if not isinstance(self, format.__class__):
1653
# converting away from metadir is not implemented
1654
raise NotImplementedError(self.get_converter)
1655
return ConvertMetaToMeta(format)
1657
def get_format_string(self):
1658
"""See BzrDirFormat.get_format_string()."""
1659
return "Bazaar-NG meta directory, format 1\n"
1661
def get_format_description(self):
1662
"""See BzrDirFormat.get_format_description()."""
1663
return "Meta directory format 1"
1665
def _open(self, transport):
1666
"""See BzrDirFormat._open."""
1667
return BzrDirMeta1(transport, self)
1669
def __return_repository_format(self):
1670
"""Circular import protection."""
1671
if getattr(self, '_repository_format', None):
1672
return self._repository_format
1673
from bzrlib.repository import RepositoryFormat
1674
return RepositoryFormat.get_default_format()
1676
def __set_repository_format(self, value):
1677
"""Allow changint the repository format for metadir formats."""
1678
self._repository_format = value
1680
repository_format = property(__return_repository_format, __set_repository_format)
1682
def __get_workingtree_format(self):
1683
if self._workingtree_format is None:
1684
from bzrlib.workingtree import WorkingTreeFormat
1685
self._workingtree_format = WorkingTreeFormat.get_default_format()
1686
return self._workingtree_format
1688
def __set_workingtree_format(self, wt_format):
1689
self._workingtree_format = wt_format
1691
workingtree_format = property(__get_workingtree_format,
1692
__set_workingtree_format)
1695
# Register bzr control format
1696
BzrDirFormat.register_control_format(BzrDirFormat)
1698
# Register bzr formats
1699
BzrDirFormat.register_format(BzrDirFormat4())
1700
BzrDirFormat.register_format(BzrDirFormat5())
1701
BzrDirFormat.register_format(BzrDirFormat6())
1702
__default_format = BzrDirMetaFormat1()
1703
BzrDirFormat.register_format(__default_format)
1704
BzrDirFormat._default_format = __default_format
1707
class BzrDirTestProviderAdapter(object):
1708
"""A tool to generate a suite testing multiple bzrdir formats at once.
1710
This is done by copying the test once for each transport and injecting
1711
the transport_server, transport_readonly_server, and bzrdir_format
1712
classes into each copy. Each copy is also given a new id() to make it
1716
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1718
"""Create an object to adapt tests.
1720
:param vfs_server: A factory to create a Transport Server which has
1721
all the VFS methods working, and is writable.
1723
self._vfs_factory = vfs_factory
1724
self._transport_server = transport_server
1725
self._transport_readonly_server = transport_readonly_server
1726
self._formats = formats
1728
def adapt(self, test):
1729
result = unittest.TestSuite()
1730
for format in self._formats:
1731
new_test = deepcopy(test)
1732
new_test.vfs_transport_factory = self._vfs_factory
1733
new_test.transport_server = self._transport_server
1734
new_test.transport_readonly_server = self._transport_readonly_server
1735
new_test.bzrdir_format = format
1736
def make_new_test_id():
1737
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1738
return lambda: new_id
1739
new_test.id = make_new_test_id()
1740
result.addTest(new_test)
1744
class Converter(object):
1745
"""Converts a disk format object from one format to another."""
1747
def convert(self, to_convert, pb):
1748
"""Perform the conversion of to_convert, giving feedback via pb.
1750
:param to_convert: The disk object to convert.
1751
:param pb: a progress bar to use for progress information.
1754
def step(self, message):
1755
"""Update the pb by a step."""
1757
self.pb.update(message, self.count, self.total)
1760
class ConvertBzrDir4To5(Converter):
1761
"""Converts format 4 bzr dirs to format 5."""
1764
super(ConvertBzrDir4To5, self).__init__()
1765
self.converted_revs = set()
1766
self.absent_revisions = set()
1770
def convert(self, to_convert, pb):
1771
"""See Converter.convert()."""
1772
self.bzrdir = to_convert
1774
self.pb.note('starting upgrade from format 4 to 5')
1775
if isinstance(self.bzrdir.transport, LocalTransport):
1776
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1777
self._convert_to_weaves()
1778
return BzrDir.open(self.bzrdir.root_transport.base)
1780
def _convert_to_weaves(self):
1781
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1784
stat = self.bzrdir.transport.stat('weaves')
1785
if not S_ISDIR(stat.st_mode):
1786
self.bzrdir.transport.delete('weaves')
1787
self.bzrdir.transport.mkdir('weaves')
1788
except errors.NoSuchFile:
1789
self.bzrdir.transport.mkdir('weaves')
1790
# deliberately not a WeaveFile as we want to build it up slowly.
1791
self.inv_weave = Weave('inventory')
1792
# holds in-memory weaves for all files
1793
self.text_weaves = {}
1794
self.bzrdir.transport.delete('branch-format')
1795
self.branch = self.bzrdir.open_branch()
1796
self._convert_working_inv()
1797
rev_history = self.branch.revision_history()
1798
# to_read is a stack holding the revisions we still need to process;
1799
# appending to it adds new highest-priority revisions
1800
self.known_revisions = set(rev_history)
1801
self.to_read = rev_history[-1:]
1803
rev_id = self.to_read.pop()
1804
if (rev_id not in self.revisions
1805
and rev_id not in self.absent_revisions):
1806
self._load_one_rev(rev_id)
1808
to_import = self._make_order()
1809
for i, rev_id in enumerate(to_import):
1810
self.pb.update('converting revision', i, len(to_import))
1811
self._convert_one_rev(rev_id)
1813
self._write_all_weaves()
1814
self._write_all_revs()
1815
self.pb.note('upgraded to weaves:')
1816
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1817
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1818
self.pb.note(' %6d texts', self.text_count)
1819
self._cleanup_spare_files_after_format4()
1820
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1822
def _cleanup_spare_files_after_format4(self):
1823
# FIXME working tree upgrade foo.
1824
for n in 'merged-patches', 'pending-merged-patches':
1826
## assert os.path.getsize(p) == 0
1827
self.bzrdir.transport.delete(n)
1828
except errors.NoSuchFile:
1830
self.bzrdir.transport.delete_tree('inventory-store')
1831
self.bzrdir.transport.delete_tree('text-store')
1833
def _convert_working_inv(self):
1834
inv = xml4.serializer_v4.read_inventory(
1835
self.branch.control_files.get('inventory'))
1836
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1837
# FIXME inventory is a working tree change.
1838
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1840
def _write_all_weaves(self):
1841
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1842
weave_transport = self.bzrdir.transport.clone('weaves')
1843
weaves = WeaveStore(weave_transport, prefixed=False)
1844
transaction = WriteTransaction()
1848
for file_id, file_weave in self.text_weaves.items():
1849
self.pb.update('writing weave', i, len(self.text_weaves))
1850
weaves._put_weave(file_id, file_weave, transaction)
1852
self.pb.update('inventory', 0, 1)
1853
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1854
self.pb.update('inventory', 1, 1)
1858
def _write_all_revs(self):
1859
"""Write all revisions out in new form."""
1860
self.bzrdir.transport.delete_tree('revision-store')
1861
self.bzrdir.transport.mkdir('revision-store')
1862
revision_transport = self.bzrdir.transport.clone('revision-store')
1864
_revision_store = TextRevisionStore(TextStore(revision_transport,
1868
transaction = WriteTransaction()
1869
for i, rev_id in enumerate(self.converted_revs):
1870
self.pb.update('write revision', i, len(self.converted_revs))
1871
_revision_store.add_revision(self.revisions[rev_id], transaction)
1875
def _load_one_rev(self, rev_id):
1876
"""Load a revision object into memory.
1878
Any parents not either loaded or abandoned get queued to be
1880
self.pb.update('loading revision',
1881
len(self.revisions),
1882
len(self.known_revisions))
1883
if not self.branch.repository.has_revision(rev_id):
1885
self.pb.note('revision {%s} not present in branch; '
1886
'will be converted as a ghost',
1888
self.absent_revisions.add(rev_id)
1890
rev = self.branch.repository._revision_store.get_revision(rev_id,
1891
self.branch.repository.get_transaction())
1892
for parent_id in rev.parent_ids:
1893
self.known_revisions.add(parent_id)
1894
self.to_read.append(parent_id)
1895
self.revisions[rev_id] = rev
1897
def _load_old_inventory(self, rev_id):
1898
assert rev_id not in self.converted_revs
1899
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1900
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1901
inv.revision_id = rev_id
1902
rev = self.revisions[rev_id]
1903
if rev.inventory_sha1:
1904
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1905
'inventory sha mismatch for {%s}' % rev_id
1908
def _load_updated_inventory(self, rev_id):
1909
assert rev_id in self.converted_revs
1910
inv_xml = self.inv_weave.get_text(rev_id)
1911
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1914
def _convert_one_rev(self, rev_id):
1915
"""Convert revision and all referenced objects to new format."""
1916
rev = self.revisions[rev_id]
1917
inv = self._load_old_inventory(rev_id)
1918
present_parents = [p for p in rev.parent_ids
1919
if p not in self.absent_revisions]
1920
self._convert_revision_contents(rev, inv, present_parents)
1921
self._store_new_weave(rev, inv, present_parents)
1922
self.converted_revs.add(rev_id)
1924
def _store_new_weave(self, rev, inv, present_parents):
1925
# the XML is now updated with text versions
1927
entries = inv.iter_entries()
1929
for path, ie in entries:
1930
assert getattr(ie, 'revision', None) is not None, \
1931
'no revision on {%s} in {%s}' % \
1932
(file_id, rev.revision_id)
1933
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1934
new_inv_sha1 = sha_string(new_inv_xml)
1935
self.inv_weave.add_lines(rev.revision_id,
1937
new_inv_xml.splitlines(True))
1938
rev.inventory_sha1 = new_inv_sha1
1940
def _convert_revision_contents(self, rev, inv, present_parents):
1941
"""Convert all the files within a revision.
1943
Also upgrade the inventory to refer to the text revision ids."""
1944
rev_id = rev.revision_id
1945
mutter('converting texts of revision {%s}',
1947
parent_invs = map(self._load_updated_inventory, present_parents)
1948
entries = inv.iter_entries()
1950
for path, ie in entries:
1951
self._convert_file_version(rev, ie, parent_invs)
1953
def _convert_file_version(self, rev, ie, parent_invs):
1954
"""Convert one version of one file.
1956
The file needs to be added into the weave if it is a merge
1957
of >=2 parents or if it's changed from its parent.
1959
file_id = ie.file_id
1960
rev_id = rev.revision_id
1961
w = self.text_weaves.get(file_id)
1964
self.text_weaves[file_id] = w
1965
text_changed = False
1966
previous_entries = ie.find_previous_heads(parent_invs,
1970
for old_revision in previous_entries:
1971
# if this fails, its a ghost ?
1972
assert old_revision in self.converted_revs, \
1973
"Revision {%s} not in converted_revs" % old_revision
1974
self.snapshot_ie(previous_entries, ie, w, rev_id)
1976
assert getattr(ie, 'revision', None) is not None
1978
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1979
# TODO: convert this logic, which is ~= snapshot to
1980
# a call to:. This needs the path figured out. rather than a work_tree
1981
# a v4 revision_tree can be given, or something that looks enough like
1982
# one to give the file content to the entry if it needs it.
1983
# and we need something that looks like a weave store for snapshot to
1985
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1986
if len(previous_revisions) == 1:
1987
previous_ie = previous_revisions.values()[0]
1988
if ie._unchanged(previous_ie):
1989
ie.revision = previous_ie.revision
1992
text = self.branch.repository.text_store.get(ie.text_id)
1993
file_lines = text.readlines()
1994
assert sha_strings(file_lines) == ie.text_sha1
1995
assert sum(map(len, file_lines)) == ie.text_size
1996
w.add_lines(rev_id, previous_revisions, file_lines)
1997
self.text_count += 1
1999
w.add_lines(rev_id, previous_revisions, [])
2000
ie.revision = rev_id
2002
def _make_order(self):
2003
"""Return a suitable order for importing revisions.
2005
The order must be such that an revision is imported after all
2006
its (present) parents.
2008
todo = set(self.revisions.keys())
2009
done = self.absent_revisions.copy()
2012
# scan through looking for a revision whose parents
2014
for rev_id in sorted(list(todo)):
2015
rev = self.revisions[rev_id]
2016
parent_ids = set(rev.parent_ids)
2017
if parent_ids.issubset(done):
2018
# can take this one now
2019
order.append(rev_id)
2025
class ConvertBzrDir5To6(Converter):
2026
"""Converts format 5 bzr dirs to format 6."""
2028
def convert(self, to_convert, pb):
2029
"""See Converter.convert()."""
2030
self.bzrdir = to_convert
2032
self.pb.note('starting upgrade from format 5 to 6')
2033
self._convert_to_prefixed()
2034
return BzrDir.open(self.bzrdir.root_transport.base)
2036
def _convert_to_prefixed(self):
2037
from bzrlib.store import TransportStore
2038
self.bzrdir.transport.delete('branch-format')
2039
for store_name in ["weaves", "revision-store"]:
2040
self.pb.note("adding prefixes to %s" % store_name)
2041
store_transport = self.bzrdir.transport.clone(store_name)
2042
store = TransportStore(store_transport, prefixed=True)
2043
for urlfilename in store_transport.list_dir('.'):
2044
filename = urlutils.unescape(urlfilename)
2045
if (filename.endswith(".weave") or
2046
filename.endswith(".gz") or
2047
filename.endswith(".sig")):
2048
file_id = os.path.splitext(filename)[0]
2051
prefix_dir = store.hash_prefix(file_id)
2052
# FIXME keep track of the dirs made RBC 20060121
2054
store_transport.move(filename, prefix_dir + '/' + filename)
2055
except errors.NoSuchFile: # catches missing dirs strangely enough
2056
store_transport.mkdir(prefix_dir)
2057
store_transport.move(filename, prefix_dir + '/' + filename)
2058
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2061
class ConvertBzrDir6ToMeta(Converter):
2062
"""Converts format 6 bzr dirs to metadirs."""
2064
def convert(self, to_convert, pb):
2065
"""See Converter.convert()."""
2066
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2067
from bzrlib.branch import BzrBranchFormat5
2068
self.bzrdir = to_convert
2071
self.total = 20 # the steps we know about
2072
self.garbage_inventories = []
2074
self.pb.note('starting upgrade from format 6 to metadir')
2075
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2076
# its faster to move specific files around than to open and use the apis...
2077
# first off, nuke ancestry.weave, it was never used.
2079
self.step('Removing ancestry.weave')
2080
self.bzrdir.transport.delete('ancestry.weave')
2081
except errors.NoSuchFile:
2083
# find out whats there
2084
self.step('Finding branch files')
2085
last_revision = self.bzrdir.open_branch().last_revision()
2086
bzrcontents = self.bzrdir.transport.list_dir('.')
2087
for name in bzrcontents:
2088
if name.startswith('basis-inventory.'):
2089
self.garbage_inventories.append(name)
2090
# create new directories for repository, working tree and branch
2091
self.dir_mode = self.bzrdir._control_files._dir_mode
2092
self.file_mode = self.bzrdir._control_files._file_mode
2093
repository_names = [('inventory.weave', True),
2094
('revision-store', True),
2096
self.step('Upgrading repository ')
2097
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2098
self.make_lock('repository')
2099
# we hard code the formats here because we are converting into
2100
# the meta format. The meta format upgrader can take this to a
2101
# future format within each component.
2102
self.put_format('repository', RepositoryFormat7())
2103
for entry in repository_names:
2104
self.move_entry('repository', entry)
2106
self.step('Upgrading branch ')
2107
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2108
self.make_lock('branch')
2109
self.put_format('branch', BzrBranchFormat5())
2110
branch_files = [('revision-history', True),
2111
('branch-name', True),
2113
for entry in branch_files:
2114
self.move_entry('branch', entry)
2116
checkout_files = [('pending-merges', True),
2117
('inventory', True),
2118
('stat-cache', False)]
2119
# If a mandatory checkout file is not present, the branch does not have
2120
# a functional checkout. Do not create a checkout in the converted
2122
for name, mandatory in checkout_files:
2123
if mandatory and name not in bzrcontents:
2124
has_checkout = False
2128
if not has_checkout:
2129
self.pb.note('No working tree.')
2130
# If some checkout files are there, we may as well get rid of them.
2131
for name, mandatory in checkout_files:
2132
if name in bzrcontents:
2133
self.bzrdir.transport.delete(name)
2135
from bzrlib.workingtree import WorkingTreeFormat3
2136
self.step('Upgrading working tree')
2137
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2138
self.make_lock('checkout')
2140
'checkout', WorkingTreeFormat3())
2141
self.bzrdir.transport.delete_multi(
2142
self.garbage_inventories, self.pb)
2143
for entry in checkout_files:
2144
self.move_entry('checkout', entry)
2145
if last_revision is not None:
2146
self.bzrdir._control_files.put_utf8(
2147
'checkout/last-revision', last_revision)
2148
self.bzrdir._control_files.put_utf8(
2149
'branch-format', BzrDirMetaFormat1().get_format_string())
2150
return BzrDir.open(self.bzrdir.root_transport.base)
2152
def make_lock(self, name):
2153
"""Make a lock for the new control dir name."""
2154
self.step('Make %s lock' % name)
2155
ld = lockdir.LockDir(self.bzrdir.transport,
2157
file_modebits=self.file_mode,
2158
dir_modebits=self.dir_mode)
2161
def move_entry(self, new_dir, entry):
2162
"""Move then entry name into new_dir."""
2164
mandatory = entry[1]
2165
self.step('Moving %s' % name)
2167
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2168
except errors.NoSuchFile:
2172
def put_format(self, dirname, format):
2173
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2176
class ConvertMetaToMeta(Converter):
2177
"""Converts the components of metadirs."""
2179
def __init__(self, target_format):
2180
"""Create a metadir to metadir converter.
2182
:param target_format: The final metadir format that is desired.
2184
self.target_format = target_format
2186
def convert(self, to_convert, pb):
2187
"""See Converter.convert()."""
2188
self.bzrdir = to_convert
2192
self.step('checking repository format')
2194
repo = self.bzrdir.open_repository()
2195
except errors.NoRepositoryPresent:
2198
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2199
from bzrlib.repository import CopyConverter
2200
self.pb.note('starting repository conversion')
2201
converter = CopyConverter(self.target_format.repository_format)
2202
converter.convert(repo, pb)
2204
branch = self.bzrdir.open_branch()
2205
except errors.NotBranchError:
2208
# TODO: conversions of Branch and Tree should be done by
2209
# InterXFormat lookups
2210
# Avoid circular imports
2211
from bzrlib import branch as _mod_branch
2212
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2213
self.target_format.get_branch_format().__class__ is
2214
_mod_branch.BzrBranchFormat6):
2215
branch_converter = _mod_branch.Converter5to6()
2216
branch_converter.convert(branch)
2218
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2219
except (errors.NoWorkingTree, errors.NotLocalUrl):
2222
# TODO: conversions of Branch and Tree should be done by
2223
# InterXFormat lookups
2224
if (isinstance(tree, workingtree.WorkingTree3) and
2225
not isinstance(tree, workingtree_4.WorkingTree4) and
2226
isinstance(self.target_format.workingtree_format,
2227
workingtree_4.WorkingTreeFormat4)):
2228
workingtree_4.Converter3to4().convert(tree)
2232
# This is not in remote.py because it's small, and needs to be registered.
2233
# Putting it in remote.py creates a circular import problem.
2234
# we can make it a lazy object if the control formats is turned into something
2236
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2237
"""Format representing bzrdirs accessed via a smart server"""
2239
def get_format_description(self):
2240
return 'bzr remote bzrdir'
2243
def probe_transport(klass, transport):
2244
"""Return a RemoteBzrDirFormat object if it looks possible."""
2246
client = transport.get_smart_client()
2247
except (NotImplementedError, AttributeError,
2248
errors.TransportNotPossible):
2249
# no smart server, so not a branch for this format type.
2250
raise errors.NotBranchError(path=transport.base)
2252
# Send a 'hello' request in protocol version one, and decline to
2253
# open it if the server doesn't support our required version (2) so
2254
# that the VFS-based transport will do it.
2255
request = client.get_request()
2256
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2257
server_version = smart_protocol.query_version()
2258
if server_version != 2:
2259
raise errors.NotBranchError(path=transport.base)
2262
def initialize_on_transport(self, transport):
2264
# hand off the request to the smart server
2265
medium = transport.get_smart_medium()
2266
except errors.NoSmartMedium:
2267
# TODO: lookup the local format from a server hint.
2268
local_dir_format = BzrDirMetaFormat1()
2269
return local_dir_format.initialize_on_transport(transport)
2270
client = _SmartClient(medium)
2271
path = client.remote_path_from_transport(transport)
2272
response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
2273
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2274
return remote.RemoteBzrDir(transport)
2276
def _open(self, transport):
2277
return remote.RemoteBzrDir(transport)
2279
def __eq__(self, other):
2280
if not isinstance(other, RemoteBzrDirFormat):
2282
return self.get_format_description() == other.get_format_description()
2285
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2288
class BzrDirFormatInfo(object):
2290
def __init__(self, native, deprecated, hidden):
2291
self.deprecated = deprecated
2292
self.native = native
2293
self.hidden = hidden
2296
class BzrDirFormatRegistry(registry.Registry):
2297
"""Registry of user-selectable BzrDir subformats.
2299
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2300
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2303
def register_metadir(self, key,
2304
repository_format, help, native=True, deprecated=False,
2308
"""Register a metadir subformat.
2310
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2311
by the Repository format.
2313
:param repository_format: The fully-qualified repository format class
2315
:param branch_format: Fully-qualified branch format class name as
2317
:param tree_format: Fully-qualified tree format class name as
2320
# This should be expanded to support setting WorkingTree and Branch
2321
# formats, once BzrDirMetaFormat1 supports that.
2322
def _load(full_name):
2323
mod_name, factory_name = full_name.rsplit('.', 1)
2325
mod = __import__(mod_name, globals(), locals(),
2327
except ImportError, e:
2328
raise ImportError('failed to load %s: %s' % (full_name, e))
2330
factory = getattr(mod, factory_name)
2331
except AttributeError:
2332
raise AttributeError('no factory %s in module %r'
2337
bd = BzrDirMetaFormat1()
2338
if branch_format is not None:
2339
bd.set_branch_format(_load(branch_format))
2340
if tree_format is not None:
2341
bd.workingtree_format = _load(tree_format)
2342
if repository_format is not None:
2343
bd.repository_format = _load(repository_format)
2345
self.register(key, helper, help, native, deprecated, hidden)
2347
def register(self, key, factory, help, native=True, deprecated=False,
2349
"""Register a BzrDirFormat factory.
2351
The factory must be a callable that takes one parameter: the key.
2352
It must produce an instance of the BzrDirFormat when called.
2354
This function mainly exists to prevent the info object from being
2357
registry.Registry.register(self, key, factory, help,
2358
BzrDirFormatInfo(native, deprecated, hidden))
2360
def register_lazy(self, key, module_name, member_name, help, native=True,
2361
deprecated=False, hidden=False):
2362
registry.Registry.register_lazy(self, key, module_name, member_name,
2363
help, BzrDirFormatInfo(native, deprecated, hidden))
2365
def set_default(self, key):
2366
"""Set the 'default' key to be a clone of the supplied key.
2368
This method must be called once and only once.
2370
registry.Registry.register(self, 'default', self.get(key),
2371
self.get_help(key), info=self.get_info(key))
2373
def set_default_repository(self, key):
2374
"""Set the FormatRegistry default and Repository default.
2376
This is a transitional method while Repository.set_default_format
2379
if 'default' in self:
2380
self.remove('default')
2381
self.set_default(key)
2382
format = self.get('default')()
2383
assert isinstance(format, BzrDirMetaFormat1)
2385
def make_bzrdir(self, key):
2386
return self.get(key)()
2388
def help_topic(self, topic):
2389
output = textwrap.dedent("""\
2390
Bazaar directory formats
2391
------------------------
2393
These formats can be used for creating branches, working trees, and
2397
default_help = self.get_help('default')
2399
for key in self.keys():
2400
if key == 'default':
2402
help = self.get_help(key)
2403
if help == default_help:
2404
default_realkey = key
2406
help_pairs.append((key, help))
2408
def wrapped(key, help, info):
2410
help = '(native) ' + help
2411
return ' %s:\n%s\n\n' % (key,
2412
textwrap.fill(help, initial_indent=' ',
2413
subsequent_indent=' '))
2414
output += wrapped('%s/default' % default_realkey, default_help,
2415
self.get_info('default'))
2416
deprecated_pairs = []
2417
for key, help in help_pairs:
2418
info = self.get_info(key)
2421
elif info.deprecated:
2422
deprecated_pairs.append((key, help))
2424
output += wrapped(key, help, info)
2425
if len(deprecated_pairs) > 0:
2426
output += "Deprecated formats\n------------------\n\n"
2427
for key, help in deprecated_pairs:
2428
info = self.get_info(key)
2429
output += wrapped(key, help, info)
2434
format_registry = BzrDirFormatRegistry()
2435
format_registry.register('weave', BzrDirFormat6,
2436
'Pre-0.8 format. Slower than knit and does not'
2437
' support checkouts or shared repositories.',
2439
format_registry.register_metadir('knit',
2440
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2441
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2442
branch_format='bzrlib.branch.BzrBranchFormat5',
2443
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2444
format_registry.register_metadir('metaweave',
2445
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2446
'Transitional format in 0.8. Slower than knit.',
2447
branch_format='bzrlib.branch.BzrBranchFormat5',
2448
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2450
format_registry.register_metadir('dirstate',
2451
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2452
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2453
'above when accessed over the network.',
2454
branch_format='bzrlib.branch.BzrBranchFormat5',
2455
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2456
# directly from workingtree_4 triggers a circular import.
2457
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2459
format_registry.register_metadir('dirstate-tags',
2460
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2461
help='New in 0.15: Fast local operations and improved scaling for '
2462
'network operations. Additionally adds support for tags.'
2463
' Incompatible with bzr < 0.15.',
2464
branch_format='bzrlib.branch.BzrBranchFormat6',
2465
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2467
format_registry.register_metadir('dirstate-with-subtree',
2468
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2469
help='New in 0.15: Fast local operations and improved scaling for '
2470
'network operations. Additionally adds support for versioning nested '
2471
'bzr branches. Incompatible with bzr < 0.15.',
2472
branch_format='bzrlib.branch.BzrBranchFormat6',
2473
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2476
format_registry.set_default('dirstate')