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
"""Open a bzrdir within a particular directory.
533
:param transport: Transport containing the bzrdir.
534
:param _unsupported: private.
536
base = transport.base
538
def find_format(transport):
539
return transport, BzrDirFormat.find_format(transport)
541
def redirected(transport, e, redirection_notice):
542
qualified_source = e.get_source_url()
543
relpath = transport.relpath(qualified_source)
544
if not e.target.endswith(relpath):
545
# Not redirected to a branch-format, not a branch
546
raise errors.NotBranchError(path=e.target)
547
target = e.target[:-len(relpath)]
548
note('%s is%s redirected to %s',
549
transport.base, e.permanently, target)
550
# Let's try with a new transport
551
qualified_target = e.get_target_url()[:-len(relpath)]
552
# FIXME: If 'transport' has a qualifier, this should
553
# be applied again to the new transport *iff* the
554
# schemes used are the same. It's a bit tricky to
555
# verify, so I'll punt for now
557
return get_transport(target)
560
transport, format = do_catching_redirections(find_format,
563
except errors.TooManyRedirections:
564
raise errors.NotBranchError(base)
566
BzrDir._check_supported(format, _unsupported)
567
return format.open(transport, _found=True)
569
def open_branch(self, unsupported=False):
570
"""Open the branch object at this BzrDir if one is present.
572
If unsupported is True, then no longer supported branch formats can
575
TODO: static convenience version of this?
577
raise NotImplementedError(self.open_branch)
580
def open_containing(url):
581
"""Open an existing branch which contains url.
583
:param url: url to search from.
584
See open_containing_from_transport for more detail.
586
return BzrDir.open_containing_from_transport(get_transport(url))
589
def open_containing_from_transport(a_transport):
590
"""Open an existing branch which contains a_transport.base
592
This probes for a branch at a_transport, and searches upwards from there.
594
Basically we keep looking up until we find the control directory or
595
run into the root. If there isn't one, raises NotBranchError.
596
If there is one and it is either an unrecognised format or an unsupported
597
format, UnknownFormatError or UnsupportedFormatError are raised.
598
If there is one, it is returned, along with the unused portion of url.
600
:return: The BzrDir that contains the path, and a Unicode path
601
for the rest of the URL.
603
# this gets the normalised url back. I.e. '.' -> the full path.
604
url = a_transport.base
607
result = BzrDir.open_from_transport(a_transport)
608
return result, urlutils.unescape(a_transport.relpath(url))
609
except errors.NotBranchError, e:
612
new_t = a_transport.clone('..')
613
except errors.InvalidURLJoin:
614
# reached the root, whatever that may be
615
raise errors.NotBranchError(path=url)
616
if new_t.base == a_transport.base:
617
# reached the root, whatever that may be
618
raise errors.NotBranchError(path=url)
622
def open_containing_tree_or_branch(klass, location):
623
"""Return the branch and working tree contained by a location.
625
Returns (tree, branch, relpath).
626
If there is no tree at containing the location, tree will be None.
627
If there is no branch containing the location, an exception will be
629
relpath is the portion of the path that is contained by the branch.
631
bzrdir, relpath = klass.open_containing(location)
633
tree = bzrdir.open_workingtree()
634
except (errors.NoWorkingTree, errors.NotLocalUrl):
636
branch = bzrdir.open_branch()
639
return tree, branch, relpath
641
def open_repository(self, _unsupported=False):
642
"""Open the repository object at this BzrDir if one is present.
644
This will not follow the Branch object pointer - its strictly a direct
645
open facility. Most client code should use open_branch().repository to
648
_unsupported is a private parameter, not part of the api.
649
TODO: static convenience version of this?
651
raise NotImplementedError(self.open_repository)
653
def open_workingtree(self, _unsupported=False,
654
recommend_upgrade=True):
655
"""Open the workingtree object at this BzrDir if one is present.
657
TODO: static convenience version of this?
659
raise NotImplementedError(self.open_workingtree)
661
def has_branch(self):
662
"""Tell if this bzrdir contains a branch.
664
Note: if you're going to open the branch, you should just go ahead
665
and try, and not ask permission first. (This method just opens the
666
branch and discards it, and that's somewhat expensive.)
671
except errors.NotBranchError:
674
def has_workingtree(self):
675
"""Tell if this bzrdir contains a working tree.
677
This will still raise an exception if the bzrdir has a workingtree that
678
is remote & inaccessible.
680
Note: if you're going to open the working tree, you should just go ahead
681
and try, and not ask permission first. (This method just opens the
682
workingtree and discards it, and that's somewhat expensive.)
685
self.open_workingtree(recommend_upgrade=False)
687
except errors.NoWorkingTree:
690
def _cloning_metadir(self):
691
"""Produce a metadir suitable for cloning with"""
692
result_format = self._format.__class__()
695
branch = self.open_branch()
696
source_repository = branch.repository
697
except errors.NotBranchError:
699
source_repository = self.open_repository()
700
except errors.NoRepositoryPresent:
701
source_repository = None
703
# XXX TODO: This isinstance is here because we have not implemented
704
# the fix recommended in bug # 103195 - to delegate this choice the
706
repo_format = source_repository._format
707
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
708
result_format.repository_format = repo_format
710
# TODO: Couldn't we just probe for the format in these cases,
711
# rather than opening the whole tree? It would be a little
712
# faster. mbp 20070401
713
tree = self.open_workingtree(recommend_upgrade=False)
714
except (errors.NoWorkingTree, errors.NotLocalUrl):
715
result_format.workingtree_format = None
717
result_format.workingtree_format = tree._format.__class__()
718
return result_format, source_repository
720
def cloning_metadir(self):
721
"""Produce a metadir suitable for cloning or sprouting with.
723
These operations may produce workingtrees (yes, even though they're
724
"cloning" something that doesn't have a tree, so a viable workingtree
725
format must be selected.
727
format, repository = self._cloning_metadir()
728
if format._workingtree_format is None:
729
if repository is None:
731
tree_format = repository._format._matchingbzrdir.workingtree_format
732
format.workingtree_format = tree_format.__class__()
735
def checkout_metadir(self):
736
return self.cloning_metadir()
738
def sprout(self, url, revision_id=None, force_new_repo=False,
740
"""Create a copy of this bzrdir prepared for use as a new line of
743
If urls last component does not exist, it will be created.
745
Attributes related to the identity of the source branch like
746
branch nickname will be cleaned, a working tree is created
747
whether one existed before or not; and a local branch is always
750
if revision_id is not None, then the clone operation may tune
751
itself to download less data.
754
cloning_format = self.cloning_metadir()
755
result = cloning_format.initialize(url)
757
source_branch = self.open_branch()
758
source_repository = source_branch.repository
759
except errors.NotBranchError:
762
source_repository = self.open_repository()
763
except errors.NoRepositoryPresent:
764
source_repository = None
769
result_repo = result.find_repository()
770
except errors.NoRepositoryPresent:
772
if source_repository is None and result_repo is not None:
774
elif source_repository is None and result_repo is None:
775
# no repo available, make a new one
776
result.create_repository()
777
elif source_repository is not None and result_repo is None:
778
# have source, and want to make a new target repo
779
# we don't clone the repo because that preserves attributes
780
# like is_shared(), and we have not yet implemented a
781
# repository sprout().
782
result_repo = result.create_repository()
783
if result_repo is not None:
784
# fetch needed content into target.
785
if source_repository is not None:
786
result_repo.fetch(source_repository, revision_id=revision_id)
787
if source_branch is not None:
788
source_branch.sprout(result, revision_id=revision_id)
790
result.create_branch()
791
# TODO: jam 20060426 we probably need a test in here in the
792
# case that the newly sprouted branch is a remote one
793
if result_repo is None or result_repo.make_working_trees():
794
wt = result.create_workingtree()
797
if wt.path2id('') is None:
799
wt.set_root_id(self.open_workingtree.get_root_id())
800
except errors.NoWorkingTree:
806
if recurse == 'down':
808
basis = wt.basis_tree()
810
subtrees = basis.iter_references()
811
recurse_branch = wt.branch
812
elif source_branch is not None:
813
basis = source_branch.basis_tree()
815
subtrees = basis.iter_references()
816
recurse_branch = source_branch
821
for path, file_id in subtrees:
822
target = urlutils.join(url, urlutils.escape(path))
823
sublocation = source_branch.reference_parent(file_id, path)
824
sublocation.bzrdir.sprout(target,
825
basis.get_reference_revision(file_id, path),
826
force_new_repo=force_new_repo, recurse=recurse)
828
if basis is not None:
833
class BzrDirPreSplitOut(BzrDir):
834
"""A common class for the all-in-one formats."""
836
def __init__(self, _transport, _format):
837
"""See BzrDir.__init__."""
838
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
839
assert self._format._lock_class == lockable_files.TransportLock
840
assert self._format._lock_file_name == 'branch-lock'
841
self._control_files = lockable_files.LockableFiles(
842
self.get_branch_transport(None),
843
self._format._lock_file_name,
844
self._format._lock_class)
846
def break_lock(self):
847
"""Pre-splitout bzrdirs do not suffer from stale locks."""
848
raise NotImplementedError(self.break_lock)
850
def clone(self, url, revision_id=None, force_new_repo=False):
851
"""See BzrDir.clone()."""
852
from bzrlib.workingtree import WorkingTreeFormat2
854
result = self._format._initialize_for_clone(url)
855
self.open_repository().clone(result, revision_id=revision_id)
856
from_branch = self.open_branch()
857
from_branch.clone(result, revision_id=revision_id)
859
self.open_workingtree().clone(result)
860
except errors.NotLocalUrl:
861
# make a new one, this format always has to have one.
863
WorkingTreeFormat2().initialize(result)
864
except errors.NotLocalUrl:
865
# but we cannot do it for remote trees.
866
to_branch = result.open_branch()
867
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
870
def create_branch(self):
871
"""See BzrDir.create_branch."""
872
return self.open_branch()
874
def create_repository(self, shared=False):
875
"""See BzrDir.create_repository."""
877
raise errors.IncompatibleFormat('shared repository', self._format)
878
return self.open_repository()
880
def create_workingtree(self, revision_id=None):
881
"""See BzrDir.create_workingtree."""
882
# this looks buggy but is not -really-
883
# because this format creates the workingtree when the bzrdir is
885
# clone and sprout will have set the revision_id
886
# and that will have set it for us, its only
887
# specific uses of create_workingtree in isolation
888
# that can do wonky stuff here, and that only
889
# happens for creating checkouts, which cannot be
890
# done on this format anyway. So - acceptable wart.
891
result = self.open_workingtree(recommend_upgrade=False)
892
if revision_id is not None:
893
if revision_id == _mod_revision.NULL_REVISION:
894
result.set_parent_ids([])
896
result.set_parent_ids([revision_id])
899
def destroy_workingtree(self):
900
"""See BzrDir.destroy_workingtree."""
901
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
903
def destroy_workingtree_metadata(self):
904
"""See BzrDir.destroy_workingtree_metadata."""
905
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
908
def get_branch_transport(self, branch_format):
909
"""See BzrDir.get_branch_transport()."""
910
if branch_format is None:
911
return self.transport
913
branch_format.get_format_string()
914
except NotImplementedError:
915
return self.transport
916
raise errors.IncompatibleFormat(branch_format, self._format)
918
def get_repository_transport(self, repository_format):
919
"""See BzrDir.get_repository_transport()."""
920
if repository_format is None:
921
return self.transport
923
repository_format.get_format_string()
924
except NotImplementedError:
925
return self.transport
926
raise errors.IncompatibleFormat(repository_format, self._format)
928
def get_workingtree_transport(self, workingtree_format):
929
"""See BzrDir.get_workingtree_transport()."""
930
if workingtree_format is None:
931
return self.transport
933
workingtree_format.get_format_string()
934
except NotImplementedError:
935
return self.transport
936
raise errors.IncompatibleFormat(workingtree_format, self._format)
938
def needs_format_conversion(self, format=None):
939
"""See BzrDir.needs_format_conversion()."""
940
# if the format is not the same as the system default,
941
# an upgrade is needed.
943
format = BzrDirFormat.get_default_format()
944
return not isinstance(self._format, format.__class__)
946
def open_branch(self, unsupported=False):
947
"""See BzrDir.open_branch."""
948
from bzrlib.branch import BzrBranchFormat4
949
format = BzrBranchFormat4()
950
self._check_supported(format, unsupported)
951
return format.open(self, _found=True)
953
def sprout(self, url, revision_id=None, force_new_repo=False):
954
"""See BzrDir.sprout()."""
955
from bzrlib.workingtree import WorkingTreeFormat2
957
result = self._format._initialize_for_clone(url)
959
self.open_repository().clone(result, revision_id=revision_id)
960
except errors.NoRepositoryPresent:
963
self.open_branch().sprout(result, revision_id=revision_id)
964
except errors.NotBranchError:
966
# we always want a working tree
967
WorkingTreeFormat2().initialize(result)
971
class BzrDir4(BzrDirPreSplitOut):
972
"""A .bzr version 4 control object.
974
This is a deprecated format and may be removed after sept 2006.
977
def create_repository(self, shared=False):
978
"""See BzrDir.create_repository."""
979
return self._format.repository_format.initialize(self, shared)
981
def needs_format_conversion(self, format=None):
982
"""Format 4 dirs are always in need of conversion."""
985
def open_repository(self):
986
"""See BzrDir.open_repository."""
987
from bzrlib.repofmt.weaverepo import RepositoryFormat4
988
return RepositoryFormat4().open(self, _found=True)
991
class BzrDir5(BzrDirPreSplitOut):
992
"""A .bzr version 5 control object.
994
This is a deprecated format and may be removed after sept 2006.
997
def open_repository(self):
998
"""See BzrDir.open_repository."""
999
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1000
return RepositoryFormat5().open(self, _found=True)
1002
def open_workingtree(self, _unsupported=False,
1003
recommend_upgrade=True):
1004
"""See BzrDir.create_workingtree."""
1005
from bzrlib.workingtree import WorkingTreeFormat2
1006
wt_format = WorkingTreeFormat2()
1007
# we don't warn here about upgrades; that ought to be handled for the
1009
return wt_format.open(self, _found=True)
1012
class BzrDir6(BzrDirPreSplitOut):
1013
"""A .bzr version 6 control object.
1015
This is a deprecated format and may be removed after sept 2006.
1018
def open_repository(self):
1019
"""See BzrDir.open_repository."""
1020
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1021
return RepositoryFormat6().open(self, _found=True)
1023
def open_workingtree(self, _unsupported=False,
1024
recommend_upgrade=True):
1025
"""See BzrDir.create_workingtree."""
1026
# we don't warn here about upgrades; that ought to be handled for the
1028
from bzrlib.workingtree import WorkingTreeFormat2
1029
return WorkingTreeFormat2().open(self, _found=True)
1032
class BzrDirMeta1(BzrDir):
1033
"""A .bzr meta version 1 control object.
1035
This is the first control object where the
1036
individual aspects are really split out: there are separate repository,
1037
workingtree and branch subdirectories and any subset of the three can be
1038
present within a BzrDir.
1041
def can_convert_format(self):
1042
"""See BzrDir.can_convert_format()."""
1045
def create_branch(self):
1046
"""See BzrDir.create_branch."""
1047
return self._format.get_branch_format().initialize(self)
1049
def create_repository(self, shared=False):
1050
"""See BzrDir.create_repository."""
1051
return self._format.repository_format.initialize(self, shared)
1053
def create_workingtree(self, revision_id=None):
1054
"""See BzrDir.create_workingtree."""
1055
from bzrlib.workingtree import WorkingTreeFormat
1056
return self._format.workingtree_format.initialize(self, revision_id)
1058
def destroy_workingtree(self):
1059
"""See BzrDir.destroy_workingtree."""
1060
wt = self.open_workingtree(recommend_upgrade=False)
1061
repository = wt.branch.repository
1062
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1063
wt.revert([], old_tree=empty)
1064
self.destroy_workingtree_metadata()
1066
def destroy_workingtree_metadata(self):
1067
self.transport.delete_tree('checkout')
1069
def find_branch_format(self):
1070
"""Find the branch 'format' for this bzrdir.
1072
This might be a synthetic object for e.g. RemoteBranch and SVN.
1074
from bzrlib.branch import BranchFormat
1075
return BranchFormat.find_format(self)
1077
def _get_mkdir_mode(self):
1078
"""Figure out the mode to use when creating a bzrdir subdir."""
1079
temp_control = lockable_files.LockableFiles(self.transport, '',
1080
lockable_files.TransportLock)
1081
return temp_control._dir_mode
1083
def get_branch_reference(self):
1084
"""See BzrDir.get_branch_reference()."""
1085
from bzrlib.branch import BranchFormat
1086
format = BranchFormat.find_format(self)
1087
return format.get_reference(self)
1089
def get_branch_transport(self, branch_format):
1090
"""See BzrDir.get_branch_transport()."""
1091
if branch_format is None:
1092
return self.transport.clone('branch')
1094
branch_format.get_format_string()
1095
except NotImplementedError:
1096
raise errors.IncompatibleFormat(branch_format, self._format)
1098
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1099
except errors.FileExists:
1101
return self.transport.clone('branch')
1103
def get_repository_transport(self, repository_format):
1104
"""See BzrDir.get_repository_transport()."""
1105
if repository_format is None:
1106
return self.transport.clone('repository')
1108
repository_format.get_format_string()
1109
except NotImplementedError:
1110
raise errors.IncompatibleFormat(repository_format, self._format)
1112
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1113
except errors.FileExists:
1115
return self.transport.clone('repository')
1117
def get_workingtree_transport(self, workingtree_format):
1118
"""See BzrDir.get_workingtree_transport()."""
1119
if workingtree_format is None:
1120
return self.transport.clone('checkout')
1122
workingtree_format.get_format_string()
1123
except NotImplementedError:
1124
raise errors.IncompatibleFormat(workingtree_format, self._format)
1126
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1127
except errors.FileExists:
1129
return self.transport.clone('checkout')
1131
def needs_format_conversion(self, format=None):
1132
"""See BzrDir.needs_format_conversion()."""
1134
format = BzrDirFormat.get_default_format()
1135
if not isinstance(self._format, format.__class__):
1136
# it is not a meta dir format, conversion is needed.
1138
# we might want to push this down to the repository?
1140
if not isinstance(self.open_repository()._format,
1141
format.repository_format.__class__):
1142
# the repository needs an upgrade.
1144
except errors.NoRepositoryPresent:
1147
if not isinstance(self.open_branch()._format,
1148
format.get_branch_format().__class__):
1149
# the branch needs an upgrade.
1151
except errors.NotBranchError:
1154
my_wt = self.open_workingtree(recommend_upgrade=False)
1155
if not isinstance(my_wt._format,
1156
format.workingtree_format.__class__):
1157
# the workingtree needs an upgrade.
1159
except (errors.NoWorkingTree, errors.NotLocalUrl):
1163
def open_branch(self, unsupported=False):
1164
"""See BzrDir.open_branch."""
1165
format = self.find_branch_format()
1166
self._check_supported(format, unsupported)
1167
return format.open(self, _found=True)
1169
def open_repository(self, unsupported=False):
1170
"""See BzrDir.open_repository."""
1171
from bzrlib.repository import RepositoryFormat
1172
format = RepositoryFormat.find_format(self)
1173
self._check_supported(format, unsupported)
1174
return format.open(self, _found=True)
1176
def open_workingtree(self, unsupported=False,
1177
recommend_upgrade=True):
1178
"""See BzrDir.open_workingtree."""
1179
from bzrlib.workingtree import WorkingTreeFormat
1180
format = WorkingTreeFormat.find_format(self)
1181
self._check_supported(format, unsupported,
1183
basedir=self.root_transport.base)
1184
return format.open(self, _found=True)
1187
class BzrDirFormat(object):
1188
"""An encapsulation of the initialization and open routines for a format.
1190
Formats provide three things:
1191
* An initialization routine,
1195
Formats are placed in an dict by their format string for reference
1196
during bzrdir opening. These should be subclasses of BzrDirFormat
1199
Once a format is deprecated, just deprecate the initialize and open
1200
methods on the format class. Do not deprecate the object, as the
1201
object will be created every system load.
1204
_default_format = None
1205
"""The default format used for new .bzr dirs."""
1208
"""The known formats."""
1210
_control_formats = []
1211
"""The registered control formats - .bzr, ....
1213
This is a list of BzrDirFormat objects.
1216
_lock_file_name = 'branch-lock'
1218
# _lock_class must be set in subclasses to the lock type, typ.
1219
# TransportLock or LockDir
1222
def find_format(klass, transport):
1223
"""Return the format present at transport."""
1224
for format in klass._control_formats:
1226
return format.probe_transport(transport)
1227
except errors.NotBranchError:
1228
# this format does not find a control dir here.
1230
raise errors.NotBranchError(path=transport.base)
1233
def probe_transport(klass, transport):
1234
"""Return the .bzrdir style format present in a directory."""
1236
format_string = transport.get(".bzr/branch-format").read()
1237
except errors.NoSuchFile:
1238
raise errors.NotBranchError(path=transport.base)
1241
return klass._formats[format_string]
1243
raise errors.UnknownFormatError(format=format_string)
1246
def get_default_format(klass):
1247
"""Return the current default format."""
1248
return klass._default_format
1250
def get_format_string(self):
1251
"""Return the ASCII format string that identifies this format."""
1252
raise NotImplementedError(self.get_format_string)
1254
def get_format_description(self):
1255
"""Return the short description for this format."""
1256
raise NotImplementedError(self.get_format_description)
1258
def get_converter(self, format=None):
1259
"""Return the converter to use to convert bzrdirs needing converts.
1261
This returns a bzrlib.bzrdir.Converter object.
1263
This should return the best upgrader to step this format towards the
1264
current default format. In the case of plugins we can/should provide
1265
some means for them to extend the range of returnable converters.
1267
:param format: Optional format to override the default format of the
1270
raise NotImplementedError(self.get_converter)
1272
def initialize(self, url):
1273
"""Create a bzr control dir at this url and return an opened copy.
1275
Subclasses should typically override initialize_on_transport
1276
instead of this method.
1278
return self.initialize_on_transport(get_transport(url))
1280
def initialize_on_transport(self, transport):
1281
"""Initialize a new bzrdir in the base directory of a Transport."""
1282
# Since we don't have a .bzr directory, inherit the
1283
# mode from the root directory
1284
temp_control = lockable_files.LockableFiles(transport,
1285
'', lockable_files.TransportLock)
1286
temp_control._transport.mkdir('.bzr',
1287
# FIXME: RBC 20060121 don't peek under
1289
mode=temp_control._dir_mode)
1290
file_mode = temp_control._file_mode
1292
mutter('created control directory in ' + transport.base)
1293
control = transport.clone('.bzr')
1294
utf8_files = [('README',
1295
"This is a Bazaar-NG control directory.\n"
1296
"Do not change any files in this directory.\n"),
1297
('branch-format', self.get_format_string()),
1299
# NB: no need to escape relative paths that are url safe.
1300
control_files = lockable_files.LockableFiles(control,
1301
self._lock_file_name, self._lock_class)
1302
control_files.create_lock()
1303
control_files.lock_write()
1305
for file, content in utf8_files:
1306
control_files.put_utf8(file, content)
1308
control_files.unlock()
1309
return self.open(transport, _found=True)
1311
def is_supported(self):
1312
"""Is this format supported?
1314
Supported formats must be initializable and openable.
1315
Unsupported formats may not support initialization or committing or
1316
some other features depending on the reason for not being supported.
1320
def same_model(self, target_format):
1321
return (self.repository_format.rich_root_data ==
1322
target_format.rich_root_data)
1325
def known_formats(klass):
1326
"""Return all the known formats.
1328
Concrete formats should override _known_formats.
1330
# There is double indirection here to make sure that control
1331
# formats used by more than one dir format will only be probed
1332
# once. This can otherwise be quite expensive for remote connections.
1334
for format in klass._control_formats:
1335
result.update(format._known_formats())
1339
def _known_formats(klass):
1340
"""Return the known format instances for this control format."""
1341
return set(klass._formats.values())
1343
def open(self, transport, _found=False):
1344
"""Return an instance of this format for the dir transport points at.
1346
_found is a private parameter, do not use it.
1349
found_format = BzrDirFormat.find_format(transport)
1350
if not isinstance(found_format, self.__class__):
1351
raise AssertionError("%s was asked to open %s, but it seems to need "
1353
% (self, transport, found_format))
1354
return self._open(transport)
1356
def _open(self, transport):
1357
"""Template method helper for opening BzrDirectories.
1359
This performs the actual open and any additional logic or parameter
1362
raise NotImplementedError(self._open)
1365
def register_format(klass, format):
1366
klass._formats[format.get_format_string()] = format
1369
def register_control_format(klass, format):
1370
"""Register a format that does not use '.bzr' for its control dir.
1372
TODO: This should be pulled up into a 'ControlDirFormat' base class
1373
which BzrDirFormat can inherit from, and renamed to register_format
1374
there. It has been done without that for now for simplicity of
1377
klass._control_formats.append(format)
1380
def register_control_server_format(klass, format):
1381
"""Register a control format for client-server environments.
1383
These formats will be tried before ones registered with
1384
register_control_format. This gives implementations that decide to the
1385
chance to grab it before anything looks at the contents of the format
1388
klass._control_formats.insert(0, format)
1391
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1392
def set_default_format(klass, format):
1393
klass._set_default_format(format)
1396
def _set_default_format(klass, format):
1397
"""Set default format (for testing behavior of defaults only)"""
1398
klass._default_format = format
1401
return self.get_format_string()[:-1]
1404
def unregister_format(klass, format):
1405
assert klass._formats[format.get_format_string()] is format
1406
del klass._formats[format.get_format_string()]
1409
def unregister_control_format(klass, format):
1410
klass._control_formats.remove(format)
1413
class BzrDirFormat4(BzrDirFormat):
1414
"""Bzr dir format 4.
1416
This format is a combined format for working tree, branch and repository.
1418
- Format 1 working trees [always]
1419
- Format 4 branches [always]
1420
- Format 4 repositories [always]
1422
This format is deprecated: it indexes texts using a text it which is
1423
removed in format 5; write support for this format has been removed.
1426
_lock_class = lockable_files.TransportLock
1428
def get_format_string(self):
1429
"""See BzrDirFormat.get_format_string()."""
1430
return "Bazaar-NG branch, format 0.0.4\n"
1432
def get_format_description(self):
1433
"""See BzrDirFormat.get_format_description()."""
1434
return "All-in-one format 4"
1436
def get_converter(self, format=None):
1437
"""See BzrDirFormat.get_converter()."""
1438
# there is one and only one upgrade path here.
1439
return ConvertBzrDir4To5()
1441
def initialize_on_transport(self, transport):
1442
"""Format 4 branches cannot be created."""
1443
raise errors.UninitializableFormat(self)
1445
def is_supported(self):
1446
"""Format 4 is not supported.
1448
It is not supported because the model changed from 4 to 5 and the
1449
conversion logic is expensive - so doing it on the fly was not
1454
def _open(self, transport):
1455
"""See BzrDirFormat._open."""
1456
return BzrDir4(transport, self)
1458
def __return_repository_format(self):
1459
"""Circular import protection."""
1460
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1461
return RepositoryFormat4()
1462
repository_format = property(__return_repository_format)
1465
class BzrDirFormat5(BzrDirFormat):
1466
"""Bzr control format 5.
1468
This format is a combined format for working tree, branch and repository.
1470
- Format 2 working trees [always]
1471
- Format 4 branches [always]
1472
- Format 5 repositories [always]
1473
Unhashed stores in the repository.
1476
_lock_class = lockable_files.TransportLock
1478
def get_format_string(self):
1479
"""See BzrDirFormat.get_format_string()."""
1480
return "Bazaar-NG branch, format 5\n"
1482
def get_format_description(self):
1483
"""See BzrDirFormat.get_format_description()."""
1484
return "All-in-one format 5"
1486
def get_converter(self, format=None):
1487
"""See BzrDirFormat.get_converter()."""
1488
# there is one and only one upgrade path here.
1489
return ConvertBzrDir5To6()
1491
def _initialize_for_clone(self, url):
1492
return self.initialize_on_transport(get_transport(url), _cloning=True)
1494
def initialize_on_transport(self, transport, _cloning=False):
1495
"""Format 5 dirs always have working tree, branch and repository.
1497
Except when they are being cloned.
1499
from bzrlib.branch import BzrBranchFormat4
1500
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1501
from bzrlib.workingtree import WorkingTreeFormat2
1502
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1503
RepositoryFormat5().initialize(result, _internal=True)
1505
branch = BzrBranchFormat4().initialize(result)
1507
WorkingTreeFormat2().initialize(result)
1508
except errors.NotLocalUrl:
1509
# Even though we can't access the working tree, we need to
1510
# create its control files.
1511
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1514
def _open(self, transport):
1515
"""See BzrDirFormat._open."""
1516
return BzrDir5(transport, self)
1518
def __return_repository_format(self):
1519
"""Circular import protection."""
1520
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1521
return RepositoryFormat5()
1522
repository_format = property(__return_repository_format)
1525
class BzrDirFormat6(BzrDirFormat):
1526
"""Bzr control format 6.
1528
This format is a combined format for working tree, branch and repository.
1530
- Format 2 working trees [always]
1531
- Format 4 branches [always]
1532
- Format 6 repositories [always]
1535
_lock_class = lockable_files.TransportLock
1537
def get_format_string(self):
1538
"""See BzrDirFormat.get_format_string()."""
1539
return "Bazaar-NG branch, format 6\n"
1541
def get_format_description(self):
1542
"""See BzrDirFormat.get_format_description()."""
1543
return "All-in-one format 6"
1545
def get_converter(self, format=None):
1546
"""See BzrDirFormat.get_converter()."""
1547
# there is one and only one upgrade path here.
1548
return ConvertBzrDir6ToMeta()
1550
def _initialize_for_clone(self, url):
1551
return self.initialize_on_transport(get_transport(url), _cloning=True)
1553
def initialize_on_transport(self, transport, _cloning=False):
1554
"""Format 6 dirs always have working tree, branch and repository.
1556
Except when they are being cloned.
1558
from bzrlib.branch import BzrBranchFormat4
1559
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1560
from bzrlib.workingtree import WorkingTreeFormat2
1561
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1562
RepositoryFormat6().initialize(result, _internal=True)
1564
branch = BzrBranchFormat4().initialize(result)
1566
WorkingTreeFormat2().initialize(result)
1567
except errors.NotLocalUrl:
1568
# Even though we can't access the working tree, we need to
1569
# create its control files.
1570
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1573
def _open(self, transport):
1574
"""See BzrDirFormat._open."""
1575
return BzrDir6(transport, self)
1577
def __return_repository_format(self):
1578
"""Circular import protection."""
1579
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1580
return RepositoryFormat6()
1581
repository_format = property(__return_repository_format)
1584
class BzrDirMetaFormat1(BzrDirFormat):
1585
"""Bzr meta control format 1
1587
This is the first format with split out working tree, branch and repository
1590
- Format 3 working trees [optional]
1591
- Format 5 branches [optional]
1592
- Format 7 repositories [optional]
1595
_lock_class = lockdir.LockDir
1598
self._workingtree_format = None
1599
self._branch_format = None
1601
def __eq__(self, other):
1602
if other.__class__ is not self.__class__:
1604
if other.repository_format != self.repository_format:
1606
if other.workingtree_format != self.workingtree_format:
1610
def __ne__(self, other):
1611
return not self == other
1613
def get_branch_format(self):
1614
if self._branch_format is None:
1615
from bzrlib.branch import BranchFormat
1616
self._branch_format = BranchFormat.get_default_format()
1617
return self._branch_format
1619
def set_branch_format(self, format):
1620
self._branch_format = format
1622
def get_converter(self, format=None):
1623
"""See BzrDirFormat.get_converter()."""
1625
format = BzrDirFormat.get_default_format()
1626
if not isinstance(self, format.__class__):
1627
# converting away from metadir is not implemented
1628
raise NotImplementedError(self.get_converter)
1629
return ConvertMetaToMeta(format)
1631
def get_format_string(self):
1632
"""See BzrDirFormat.get_format_string()."""
1633
return "Bazaar-NG meta directory, format 1\n"
1635
def get_format_description(self):
1636
"""See BzrDirFormat.get_format_description()."""
1637
return "Meta directory format 1"
1639
def _open(self, transport):
1640
"""See BzrDirFormat._open."""
1641
return BzrDirMeta1(transport, self)
1643
def __return_repository_format(self):
1644
"""Circular import protection."""
1645
if getattr(self, '_repository_format', None):
1646
return self._repository_format
1647
from bzrlib.repository import RepositoryFormat
1648
return RepositoryFormat.get_default_format()
1650
def __set_repository_format(self, value):
1651
"""Allow changint the repository format for metadir formats."""
1652
self._repository_format = value
1654
repository_format = property(__return_repository_format, __set_repository_format)
1656
def __get_workingtree_format(self):
1657
if self._workingtree_format is None:
1658
from bzrlib.workingtree import WorkingTreeFormat
1659
self._workingtree_format = WorkingTreeFormat.get_default_format()
1660
return self._workingtree_format
1662
def __set_workingtree_format(self, wt_format):
1663
self._workingtree_format = wt_format
1665
workingtree_format = property(__get_workingtree_format,
1666
__set_workingtree_format)
1669
# Register bzr control format
1670
BzrDirFormat.register_control_format(BzrDirFormat)
1672
# Register bzr formats
1673
BzrDirFormat.register_format(BzrDirFormat4())
1674
BzrDirFormat.register_format(BzrDirFormat5())
1675
BzrDirFormat.register_format(BzrDirFormat6())
1676
__default_format = BzrDirMetaFormat1()
1677
BzrDirFormat.register_format(__default_format)
1678
BzrDirFormat._default_format = __default_format
1681
class BzrDirTestProviderAdapter(object):
1682
"""A tool to generate a suite testing multiple bzrdir formats at once.
1684
This is done by copying the test once for each transport and injecting
1685
the transport_server, transport_readonly_server, and bzrdir_format
1686
classes into each copy. Each copy is also given a new id() to make it
1690
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1692
"""Create an object to adapt tests.
1694
:param vfs_server: A factory to create a Transport Server which has
1695
all the VFS methods working, and is writable.
1697
self._vfs_factory = vfs_factory
1698
self._transport_server = transport_server
1699
self._transport_readonly_server = transport_readonly_server
1700
self._formats = formats
1702
def adapt(self, test):
1703
result = unittest.TestSuite()
1704
for format in self._formats:
1705
new_test = deepcopy(test)
1706
new_test.vfs_transport_factory = self._vfs_factory
1707
new_test.transport_server = self._transport_server
1708
new_test.transport_readonly_server = self._transport_readonly_server
1709
new_test.bzrdir_format = format
1710
def make_new_test_id():
1711
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1712
return lambda: new_id
1713
new_test.id = make_new_test_id()
1714
result.addTest(new_test)
1718
class Converter(object):
1719
"""Converts a disk format object from one format to another."""
1721
def convert(self, to_convert, pb):
1722
"""Perform the conversion of to_convert, giving feedback via pb.
1724
:param to_convert: The disk object to convert.
1725
:param pb: a progress bar to use for progress information.
1728
def step(self, message):
1729
"""Update the pb by a step."""
1731
self.pb.update(message, self.count, self.total)
1734
class ConvertBzrDir4To5(Converter):
1735
"""Converts format 4 bzr dirs to format 5."""
1738
super(ConvertBzrDir4To5, self).__init__()
1739
self.converted_revs = set()
1740
self.absent_revisions = set()
1744
def convert(self, to_convert, pb):
1745
"""See Converter.convert()."""
1746
self.bzrdir = to_convert
1748
self.pb.note('starting upgrade from format 4 to 5')
1749
if isinstance(self.bzrdir.transport, LocalTransport):
1750
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1751
self._convert_to_weaves()
1752
return BzrDir.open(self.bzrdir.root_transport.base)
1754
def _convert_to_weaves(self):
1755
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1758
stat = self.bzrdir.transport.stat('weaves')
1759
if not S_ISDIR(stat.st_mode):
1760
self.bzrdir.transport.delete('weaves')
1761
self.bzrdir.transport.mkdir('weaves')
1762
except errors.NoSuchFile:
1763
self.bzrdir.transport.mkdir('weaves')
1764
# deliberately not a WeaveFile as we want to build it up slowly.
1765
self.inv_weave = Weave('inventory')
1766
# holds in-memory weaves for all files
1767
self.text_weaves = {}
1768
self.bzrdir.transport.delete('branch-format')
1769
self.branch = self.bzrdir.open_branch()
1770
self._convert_working_inv()
1771
rev_history = self.branch.revision_history()
1772
# to_read is a stack holding the revisions we still need to process;
1773
# appending to it adds new highest-priority revisions
1774
self.known_revisions = set(rev_history)
1775
self.to_read = rev_history[-1:]
1777
rev_id = self.to_read.pop()
1778
if (rev_id not in self.revisions
1779
and rev_id not in self.absent_revisions):
1780
self._load_one_rev(rev_id)
1782
to_import = self._make_order()
1783
for i, rev_id in enumerate(to_import):
1784
self.pb.update('converting revision', i, len(to_import))
1785
self._convert_one_rev(rev_id)
1787
self._write_all_weaves()
1788
self._write_all_revs()
1789
self.pb.note('upgraded to weaves:')
1790
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1791
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1792
self.pb.note(' %6d texts', self.text_count)
1793
self._cleanup_spare_files_after_format4()
1794
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1796
def _cleanup_spare_files_after_format4(self):
1797
# FIXME working tree upgrade foo.
1798
for n in 'merged-patches', 'pending-merged-patches':
1800
## assert os.path.getsize(p) == 0
1801
self.bzrdir.transport.delete(n)
1802
except errors.NoSuchFile:
1804
self.bzrdir.transport.delete_tree('inventory-store')
1805
self.bzrdir.transport.delete_tree('text-store')
1807
def _convert_working_inv(self):
1808
inv = xml4.serializer_v4.read_inventory(
1809
self.branch.control_files.get('inventory'))
1810
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1811
# FIXME inventory is a working tree change.
1812
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1814
def _write_all_weaves(self):
1815
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1816
weave_transport = self.bzrdir.transport.clone('weaves')
1817
weaves = WeaveStore(weave_transport, prefixed=False)
1818
transaction = WriteTransaction()
1822
for file_id, file_weave in self.text_weaves.items():
1823
self.pb.update('writing weave', i, len(self.text_weaves))
1824
weaves._put_weave(file_id, file_weave, transaction)
1826
self.pb.update('inventory', 0, 1)
1827
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1828
self.pb.update('inventory', 1, 1)
1832
def _write_all_revs(self):
1833
"""Write all revisions out in new form."""
1834
self.bzrdir.transport.delete_tree('revision-store')
1835
self.bzrdir.transport.mkdir('revision-store')
1836
revision_transport = self.bzrdir.transport.clone('revision-store')
1838
_revision_store = TextRevisionStore(TextStore(revision_transport,
1842
transaction = WriteTransaction()
1843
for i, rev_id in enumerate(self.converted_revs):
1844
self.pb.update('write revision', i, len(self.converted_revs))
1845
_revision_store.add_revision(self.revisions[rev_id], transaction)
1849
def _load_one_rev(self, rev_id):
1850
"""Load a revision object into memory.
1852
Any parents not either loaded or abandoned get queued to be
1854
self.pb.update('loading revision',
1855
len(self.revisions),
1856
len(self.known_revisions))
1857
if not self.branch.repository.has_revision(rev_id):
1859
self.pb.note('revision {%s} not present in branch; '
1860
'will be converted as a ghost',
1862
self.absent_revisions.add(rev_id)
1864
rev = self.branch.repository._revision_store.get_revision(rev_id,
1865
self.branch.repository.get_transaction())
1866
for parent_id in rev.parent_ids:
1867
self.known_revisions.add(parent_id)
1868
self.to_read.append(parent_id)
1869
self.revisions[rev_id] = rev
1871
def _load_old_inventory(self, rev_id):
1872
assert rev_id not in self.converted_revs
1873
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1874
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1875
inv.revision_id = rev_id
1876
rev = self.revisions[rev_id]
1877
if rev.inventory_sha1:
1878
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1879
'inventory sha mismatch for {%s}' % rev_id
1882
def _load_updated_inventory(self, rev_id):
1883
assert rev_id in self.converted_revs
1884
inv_xml = self.inv_weave.get_text(rev_id)
1885
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1888
def _convert_one_rev(self, rev_id):
1889
"""Convert revision and all referenced objects to new format."""
1890
rev = self.revisions[rev_id]
1891
inv = self._load_old_inventory(rev_id)
1892
present_parents = [p for p in rev.parent_ids
1893
if p not in self.absent_revisions]
1894
self._convert_revision_contents(rev, inv, present_parents)
1895
self._store_new_weave(rev, inv, present_parents)
1896
self.converted_revs.add(rev_id)
1898
def _store_new_weave(self, rev, inv, present_parents):
1899
# the XML is now updated with text versions
1901
entries = inv.iter_entries()
1903
for path, ie in entries:
1904
assert getattr(ie, 'revision', None) is not None, \
1905
'no revision on {%s} in {%s}' % \
1906
(file_id, rev.revision_id)
1907
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1908
new_inv_sha1 = sha_string(new_inv_xml)
1909
self.inv_weave.add_lines(rev.revision_id,
1911
new_inv_xml.splitlines(True))
1912
rev.inventory_sha1 = new_inv_sha1
1914
def _convert_revision_contents(self, rev, inv, present_parents):
1915
"""Convert all the files within a revision.
1917
Also upgrade the inventory to refer to the text revision ids."""
1918
rev_id = rev.revision_id
1919
mutter('converting texts of revision {%s}',
1921
parent_invs = map(self._load_updated_inventory, present_parents)
1922
entries = inv.iter_entries()
1924
for path, ie in entries:
1925
self._convert_file_version(rev, ie, parent_invs)
1927
def _convert_file_version(self, rev, ie, parent_invs):
1928
"""Convert one version of one file.
1930
The file needs to be added into the weave if it is a merge
1931
of >=2 parents or if it's changed from its parent.
1933
file_id = ie.file_id
1934
rev_id = rev.revision_id
1935
w = self.text_weaves.get(file_id)
1938
self.text_weaves[file_id] = w
1939
text_changed = False
1940
previous_entries = ie.find_previous_heads(parent_invs,
1944
for old_revision in previous_entries:
1945
# if this fails, its a ghost ?
1946
assert old_revision in self.converted_revs, \
1947
"Revision {%s} not in converted_revs" % old_revision
1948
self.snapshot_ie(previous_entries, ie, w, rev_id)
1950
assert getattr(ie, 'revision', None) is not None
1952
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1953
# TODO: convert this logic, which is ~= snapshot to
1954
# a call to:. This needs the path figured out. rather than a work_tree
1955
# a v4 revision_tree can be given, or something that looks enough like
1956
# one to give the file content to the entry if it needs it.
1957
# and we need something that looks like a weave store for snapshot to
1959
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1960
if len(previous_revisions) == 1:
1961
previous_ie = previous_revisions.values()[0]
1962
if ie._unchanged(previous_ie):
1963
ie.revision = previous_ie.revision
1966
text = self.branch.repository.text_store.get(ie.text_id)
1967
file_lines = text.readlines()
1968
assert sha_strings(file_lines) == ie.text_sha1
1969
assert sum(map(len, file_lines)) == ie.text_size
1970
w.add_lines(rev_id, previous_revisions, file_lines)
1971
self.text_count += 1
1973
w.add_lines(rev_id, previous_revisions, [])
1974
ie.revision = rev_id
1976
def _make_order(self):
1977
"""Return a suitable order for importing revisions.
1979
The order must be such that an revision is imported after all
1980
its (present) parents.
1982
todo = set(self.revisions.keys())
1983
done = self.absent_revisions.copy()
1986
# scan through looking for a revision whose parents
1988
for rev_id in sorted(list(todo)):
1989
rev = self.revisions[rev_id]
1990
parent_ids = set(rev.parent_ids)
1991
if parent_ids.issubset(done):
1992
# can take this one now
1993
order.append(rev_id)
1999
class ConvertBzrDir5To6(Converter):
2000
"""Converts format 5 bzr dirs to format 6."""
2002
def convert(self, to_convert, pb):
2003
"""See Converter.convert()."""
2004
self.bzrdir = to_convert
2006
self.pb.note('starting upgrade from format 5 to 6')
2007
self._convert_to_prefixed()
2008
return BzrDir.open(self.bzrdir.root_transport.base)
2010
def _convert_to_prefixed(self):
2011
from bzrlib.store import TransportStore
2012
self.bzrdir.transport.delete('branch-format')
2013
for store_name in ["weaves", "revision-store"]:
2014
self.pb.note("adding prefixes to %s" % store_name)
2015
store_transport = self.bzrdir.transport.clone(store_name)
2016
store = TransportStore(store_transport, prefixed=True)
2017
for urlfilename in store_transport.list_dir('.'):
2018
filename = urlutils.unescape(urlfilename)
2019
if (filename.endswith(".weave") or
2020
filename.endswith(".gz") or
2021
filename.endswith(".sig")):
2022
file_id = os.path.splitext(filename)[0]
2025
prefix_dir = store.hash_prefix(file_id)
2026
# FIXME keep track of the dirs made RBC 20060121
2028
store_transport.move(filename, prefix_dir + '/' + filename)
2029
except errors.NoSuchFile: # catches missing dirs strangely enough
2030
store_transport.mkdir(prefix_dir)
2031
store_transport.move(filename, prefix_dir + '/' + filename)
2032
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2035
class ConvertBzrDir6ToMeta(Converter):
2036
"""Converts format 6 bzr dirs to metadirs."""
2038
def convert(self, to_convert, pb):
2039
"""See Converter.convert()."""
2040
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2041
from bzrlib.branch import BzrBranchFormat5
2042
self.bzrdir = to_convert
2045
self.total = 20 # the steps we know about
2046
self.garbage_inventories = []
2048
self.pb.note('starting upgrade from format 6 to metadir')
2049
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2050
# its faster to move specific files around than to open and use the apis...
2051
# first off, nuke ancestry.weave, it was never used.
2053
self.step('Removing ancestry.weave')
2054
self.bzrdir.transport.delete('ancestry.weave')
2055
except errors.NoSuchFile:
2057
# find out whats there
2058
self.step('Finding branch files')
2059
last_revision = self.bzrdir.open_branch().last_revision()
2060
bzrcontents = self.bzrdir.transport.list_dir('.')
2061
for name in bzrcontents:
2062
if name.startswith('basis-inventory.'):
2063
self.garbage_inventories.append(name)
2064
# create new directories for repository, working tree and branch
2065
self.dir_mode = self.bzrdir._control_files._dir_mode
2066
self.file_mode = self.bzrdir._control_files._file_mode
2067
repository_names = [('inventory.weave', True),
2068
('revision-store', True),
2070
self.step('Upgrading repository ')
2071
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2072
self.make_lock('repository')
2073
# we hard code the formats here because we are converting into
2074
# the meta format. The meta format upgrader can take this to a
2075
# future format within each component.
2076
self.put_format('repository', RepositoryFormat7())
2077
for entry in repository_names:
2078
self.move_entry('repository', entry)
2080
self.step('Upgrading branch ')
2081
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2082
self.make_lock('branch')
2083
self.put_format('branch', BzrBranchFormat5())
2084
branch_files = [('revision-history', True),
2085
('branch-name', True),
2087
for entry in branch_files:
2088
self.move_entry('branch', entry)
2090
checkout_files = [('pending-merges', True),
2091
('inventory', True),
2092
('stat-cache', False)]
2093
# If a mandatory checkout file is not present, the branch does not have
2094
# a functional checkout. Do not create a checkout in the converted
2096
for name, mandatory in checkout_files:
2097
if mandatory and name not in bzrcontents:
2098
has_checkout = False
2102
if not has_checkout:
2103
self.pb.note('No working tree.')
2104
# If some checkout files are there, we may as well get rid of them.
2105
for name, mandatory in checkout_files:
2106
if name in bzrcontents:
2107
self.bzrdir.transport.delete(name)
2109
from bzrlib.workingtree import WorkingTreeFormat3
2110
self.step('Upgrading working tree')
2111
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2112
self.make_lock('checkout')
2114
'checkout', WorkingTreeFormat3())
2115
self.bzrdir.transport.delete_multi(
2116
self.garbage_inventories, self.pb)
2117
for entry in checkout_files:
2118
self.move_entry('checkout', entry)
2119
if last_revision is not None:
2120
self.bzrdir._control_files.put_utf8(
2121
'checkout/last-revision', last_revision)
2122
self.bzrdir._control_files.put_utf8(
2123
'branch-format', BzrDirMetaFormat1().get_format_string())
2124
return BzrDir.open(self.bzrdir.root_transport.base)
2126
def make_lock(self, name):
2127
"""Make a lock for the new control dir name."""
2128
self.step('Make %s lock' % name)
2129
ld = lockdir.LockDir(self.bzrdir.transport,
2131
file_modebits=self.file_mode,
2132
dir_modebits=self.dir_mode)
2135
def move_entry(self, new_dir, entry):
2136
"""Move then entry name into new_dir."""
2138
mandatory = entry[1]
2139
self.step('Moving %s' % name)
2141
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2142
except errors.NoSuchFile:
2146
def put_format(self, dirname, format):
2147
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2150
class ConvertMetaToMeta(Converter):
2151
"""Converts the components of metadirs."""
2153
def __init__(self, target_format):
2154
"""Create a metadir to metadir converter.
2156
:param target_format: The final metadir format that is desired.
2158
self.target_format = target_format
2160
def convert(self, to_convert, pb):
2161
"""See Converter.convert()."""
2162
self.bzrdir = to_convert
2166
self.step('checking repository format')
2168
repo = self.bzrdir.open_repository()
2169
except errors.NoRepositoryPresent:
2172
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2173
from bzrlib.repository import CopyConverter
2174
self.pb.note('starting repository conversion')
2175
converter = CopyConverter(self.target_format.repository_format)
2176
converter.convert(repo, pb)
2178
branch = self.bzrdir.open_branch()
2179
except errors.NotBranchError:
2182
# TODO: conversions of Branch and Tree should be done by
2183
# InterXFormat lookups
2184
# Avoid circular imports
2185
from bzrlib import branch as _mod_branch
2186
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2187
self.target_format.get_branch_format().__class__ is
2188
_mod_branch.BzrBranchFormat6):
2189
branch_converter = _mod_branch.Converter5to6()
2190
branch_converter.convert(branch)
2192
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2193
except (errors.NoWorkingTree, errors.NotLocalUrl):
2196
# TODO: conversions of Branch and Tree should be done by
2197
# InterXFormat lookups
2198
if (isinstance(tree, workingtree.WorkingTree3) and
2199
not isinstance(tree, workingtree_4.WorkingTree4) and
2200
isinstance(self.target_format.workingtree_format,
2201
workingtree_4.WorkingTreeFormat4)):
2202
workingtree_4.Converter3to4().convert(tree)
2206
# This is not in remote.py because it's small, and needs to be registered.
2207
# Putting it in remote.py creates a circular import problem.
2208
# we can make it a lazy object if the control formats is turned into something
2210
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2211
"""Format representing bzrdirs accessed via a smart server"""
2213
def get_format_description(self):
2214
return 'bzr remote bzrdir'
2217
def probe_transport(klass, transport):
2218
"""Return a RemoteBzrDirFormat object if it looks possible."""
2220
transport.get_smart_client()
2221
except (NotImplementedError, AttributeError,
2222
errors.TransportNotPossible):
2223
# no smart server, so not a branch for this format type.
2224
raise errors.NotBranchError(path=transport.base)
2228
def initialize_on_transport(self, transport):
2230
# hand off the request to the smart server
2231
medium = transport.get_smart_medium()
2232
except errors.NoSmartMedium:
2233
# TODO: lookup the local format from a server hint.
2234
local_dir_format = BzrDirMetaFormat1()
2235
return local_dir_format.initialize_on_transport(transport)
2236
client = _SmartClient(medium)
2237
path = client.remote_path_from_transport(transport)
2238
response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
2239
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2240
return remote.RemoteBzrDir(transport)
2242
def _open(self, transport):
2243
return remote.RemoteBzrDir(transport)
2245
def __eq__(self, other):
2246
if not isinstance(other, RemoteBzrDirFormat):
2248
return self.get_format_description() == other.get_format_description()
2251
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2254
class BzrDirFormatInfo(object):
2256
def __init__(self, native, deprecated, hidden):
2257
self.deprecated = deprecated
2258
self.native = native
2259
self.hidden = hidden
2262
class BzrDirFormatRegistry(registry.Registry):
2263
"""Registry of user-selectable BzrDir subformats.
2265
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2266
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2269
def register_metadir(self, key,
2270
repository_format, help, native=True, deprecated=False,
2274
"""Register a metadir subformat.
2276
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2277
by the Repository format.
2279
:param repository_format: The fully-qualified repository format class
2281
:param branch_format: Fully-qualified branch format class name as
2283
:param tree_format: Fully-qualified tree format class name as
2286
# This should be expanded to support setting WorkingTree and Branch
2287
# formats, once BzrDirMetaFormat1 supports that.
2288
def _load(full_name):
2289
mod_name, factory_name = full_name.rsplit('.', 1)
2291
mod = __import__(mod_name, globals(), locals(),
2293
except ImportError, e:
2294
raise ImportError('failed to load %s: %s' % (full_name, e))
2296
factory = getattr(mod, factory_name)
2297
except AttributeError:
2298
raise AttributeError('no factory %s in module %r'
2303
bd = BzrDirMetaFormat1()
2304
if branch_format is not None:
2305
bd.set_branch_format(_load(branch_format))
2306
if tree_format is not None:
2307
bd.workingtree_format = _load(tree_format)
2308
if repository_format is not None:
2309
bd.repository_format = _load(repository_format)
2311
self.register(key, helper, help, native, deprecated, hidden)
2313
def register(self, key, factory, help, native=True, deprecated=False,
2315
"""Register a BzrDirFormat factory.
2317
The factory must be a callable that takes one parameter: the key.
2318
It must produce an instance of the BzrDirFormat when called.
2320
This function mainly exists to prevent the info object from being
2323
registry.Registry.register(self, key, factory, help,
2324
BzrDirFormatInfo(native, deprecated, hidden))
2326
def register_lazy(self, key, module_name, member_name, help, native=True,
2327
deprecated=False, hidden=False):
2328
registry.Registry.register_lazy(self, key, module_name, member_name,
2329
help, BzrDirFormatInfo(native, deprecated, hidden))
2331
def set_default(self, key):
2332
"""Set the 'default' key to be a clone of the supplied key.
2334
This method must be called once and only once.
2336
registry.Registry.register(self, 'default', self.get(key),
2337
self.get_help(key), info=self.get_info(key))
2339
def set_default_repository(self, key):
2340
"""Set the FormatRegistry default and Repository default.
2342
This is a transitional method while Repository.set_default_format
2345
if 'default' in self:
2346
self.remove('default')
2347
self.set_default(key)
2348
format = self.get('default')()
2349
assert isinstance(format, BzrDirMetaFormat1)
2351
def make_bzrdir(self, key):
2352
return self.get(key)()
2354
def help_topic(self, topic):
2355
output = textwrap.dedent("""\
2356
Bazaar directory formats
2357
------------------------
2359
These formats can be used for creating branches, working trees, and
2363
default_help = self.get_help('default')
2365
for key in self.keys():
2366
if key == 'default':
2368
help = self.get_help(key)
2369
if help == default_help:
2370
default_realkey = key
2372
help_pairs.append((key, help))
2374
def wrapped(key, help, info):
2376
help = '(native) ' + help
2377
return ' %s:\n%s\n\n' % (key,
2378
textwrap.fill(help, initial_indent=' ',
2379
subsequent_indent=' '))
2380
output += wrapped('%s/default' % default_realkey, default_help,
2381
self.get_info('default'))
2382
deprecated_pairs = []
2383
for key, help in help_pairs:
2384
info = self.get_info(key)
2387
elif info.deprecated:
2388
deprecated_pairs.append((key, help))
2390
output += wrapped(key, help, info)
2391
if len(deprecated_pairs) > 0:
2392
output += "Deprecated formats\n------------------\n\n"
2393
for key, help in deprecated_pairs:
2394
info = self.get_info(key)
2395
output += wrapped(key, help, info)
2400
format_registry = BzrDirFormatRegistry()
2401
format_registry.register('weave', BzrDirFormat6,
2402
'Pre-0.8 format. Slower than knit and does not'
2403
' support checkouts or shared repositories.',
2405
format_registry.register_metadir('knit',
2406
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2407
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2408
branch_format='bzrlib.branch.BzrBranchFormat5',
2409
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2410
format_registry.register_metadir('metaweave',
2411
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2412
'Transitional format in 0.8. Slower than knit.',
2413
branch_format='bzrlib.branch.BzrBranchFormat5',
2414
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2416
format_registry.register_metadir('dirstate',
2417
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2418
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2419
'above when accessed over the network.',
2420
branch_format='bzrlib.branch.BzrBranchFormat5',
2421
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2422
# directly from workingtree_4 triggers a circular import.
2423
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2425
format_registry.register_metadir('dirstate-tags',
2426
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2427
help='New in 0.15: Fast local operations and improved scaling for '
2428
'network operations. Additionally adds support for tags.'
2429
' Incompatible with bzr < 0.15.',
2430
branch_format='bzrlib.branch.BzrBranchFormat6',
2431
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2433
format_registry.register_metadir('dirstate-with-subtree',
2434
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2435
help='New in 0.15: Fast local operations and improved scaling for '
2436
'network operations. Additionally adds support for versioning nested '
2437
'bzr branches. Incompatible with bzr < 0.15.',
2438
branch_format='bzrlib.branch.BzrBranchFormat6',
2439
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2442
format_registry.set_default('dirstate')