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
22
Note: This module has a lot of ``open`` functions/methods that return
23
references to in-memory objects. As a rule, there are no matching ``close``
24
methods. To free any associated resources, simply stop referencing the
28
# TODO: Move old formats into a plugin to make this file smaller.
30
from cStringIO import StringIO
33
from bzrlib.lazy_import import lazy_import
34
lazy_import(globals(), """
35
from stat import S_ISDIR
37
from warnings import warn
47
revision as _mod_revision,
56
from bzrlib.osutils import (
60
from bzrlib.smart.client import _SmartClient
61
from bzrlib.smart import protocol
62
from bzrlib.store.revision.text import TextRevisionStore
63
from bzrlib.store.text import TextStore
64
from bzrlib.store.versioned import WeaveStore
65
from bzrlib.transactions import WriteTransaction
66
from bzrlib.transport import (
67
do_catching_redirections,
70
from bzrlib.weave import Weave
73
from bzrlib.trace import (
77
from bzrlib.transport.local import LocalTransport
78
from bzrlib.symbol_versioning import (
86
"""A .bzr control diretory.
88
BzrDir instances let you create or open any of the things that can be
89
found within .bzr - checkouts, branches and repositories.
92
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
94
a transport connected to the directory this bzr was opened from
95
(i.e. the parent directory holding the .bzr directory).
99
"""Invoke break_lock on the first object in the bzrdir.
101
If there is a tree, the tree is opened and break_lock() called.
102
Otherwise, branch is tried, and finally repository.
104
# XXX: This seems more like a UI function than something that really
105
# belongs in this class.
107
thing_to_unlock = self.open_workingtree()
108
except (errors.NotLocalUrl, errors.NoWorkingTree):
110
thing_to_unlock = self.open_branch()
111
except errors.NotBranchError:
113
thing_to_unlock = self.open_repository()
114
except errors.NoRepositoryPresent:
116
thing_to_unlock.break_lock()
118
def can_convert_format(self):
119
"""Return true if this bzrdir is one whose format we can convert from."""
122
def check_conversion_target(self, target_format):
123
target_repo_format = target_format.repository_format
124
source_repo_format = self._format.repository_format
125
source_repo_format.check_conversion_target(target_repo_format)
128
def _check_supported(format, allow_unsupported,
129
recommend_upgrade=True,
131
"""Give an error or warning on old formats.
133
:param format: may be any kind of format - workingtree, branch,
136
:param allow_unsupported: If true, allow opening
137
formats that are strongly deprecated, and which may
138
have limited functionality.
140
:param recommend_upgrade: If true (default), warn
141
the user through the ui object that they may wish
142
to upgrade the object.
144
# TODO: perhaps move this into a base Format class; it's not BzrDir
145
# specific. mbp 20070323
146
if not allow_unsupported and not format.is_supported():
147
# see open_downlevel to open legacy branches.
148
raise errors.UnsupportedFormatError(format=format)
149
if recommend_upgrade \
150
and getattr(format, 'upgrade_recommended', False):
151
ui.ui_factory.recommend_upgrade(
152
format.get_format_description(),
155
def clone(self, url, revision_id=None, force_new_repo=False):
156
"""Clone this bzrdir and its contents to url verbatim.
158
If url's last component does not exist, it will be created.
160
if revision_id is not None, then the clone operation may tune
161
itself to download less data.
162
:param force_new_repo: Do not use a shared repository for the target
163
even if one is available.
165
return self.clone_on_transport(get_transport(url),
166
revision_id=revision_id,
167
force_new_repo=force_new_repo)
169
def clone_on_transport(self, transport, revision_id=None,
170
force_new_repo=False):
171
"""Clone this bzrdir and its contents to transport verbatim.
173
If the target directory does not exist, it will be created.
175
if revision_id is not None, then the clone operation may tune
176
itself to download less data.
177
:param force_new_repo: Do not use a shared repository for the target
178
even if one is available.
180
transport.ensure_base()
181
result = self._format.initialize_on_transport(transport)
183
local_repo = self.find_repository()
184
except errors.NoRepositoryPresent:
187
# may need to copy content in
189
result_repo = local_repo.clone(
191
revision_id=revision_id)
192
result_repo.set_make_working_trees(local_repo.make_working_trees())
195
result_repo = result.find_repository()
196
# fetch content this dir needs.
197
result_repo.fetch(local_repo, revision_id=revision_id)
198
except errors.NoRepositoryPresent:
199
# needed to make one anyway.
200
result_repo = local_repo.clone(
202
revision_id=revision_id)
203
result_repo.set_make_working_trees(local_repo.make_working_trees())
204
# 1 if there is a branch present
205
# make sure its content is available in the target repository
208
self.open_branch().clone(result, revision_id=revision_id)
209
except errors.NotBranchError:
212
self.open_workingtree().clone(result)
213
except (errors.NoWorkingTree, errors.NotLocalUrl):
217
# TODO: This should be given a Transport, and should chdir up; otherwise
218
# this will open a new connection.
219
def _make_tail(self, url):
220
t = get_transport(url)
224
def create(cls, base, format=None, possible_transports=None):
225
"""Create a new BzrDir at the url 'base'.
227
:param format: If supplied, the format of branch to create. If not
228
supplied, the default is used.
229
:param possible_transports: If supplied, a list of transports that
230
can be reused to share a remote connection.
232
if cls is not BzrDir:
233
raise AssertionError("BzrDir.create always creates the default"
234
" format, not one of %r" % cls)
235
t = get_transport(base, possible_transports)
238
format = BzrDirFormat.get_default_format()
239
return format.initialize_on_transport(t)
241
def create_branch(self):
242
"""Create a branch in this BzrDir.
244
The bzrdir's format will control what branch format is created.
245
For more control see BranchFormatXX.create(a_bzrdir).
247
raise NotImplementedError(self.create_branch)
249
def destroy_branch(self):
250
"""Destroy the branch in this BzrDir"""
251
raise NotImplementedError(self.destroy_branch)
254
def create_branch_and_repo(base, force_new_repo=False, format=None):
255
"""Create a new BzrDir, Branch and Repository at the url 'base'.
257
This will use the current default BzrDirFormat unless one is
258
specified, and use whatever
259
repository format that that uses via bzrdir.create_branch and
260
create_repository. If a shared repository is available that is used
263
The created Branch object is returned.
265
:param base: The URL to create the branch at.
266
:param force_new_repo: If True a new repository is always created.
267
:param format: If supplied, the format of branch to create. If not
268
supplied, the default is used.
270
bzrdir = BzrDir.create(base, format)
271
bzrdir._find_or_create_repository(force_new_repo)
272
return bzrdir.create_branch()
274
def _find_or_create_repository(self, force_new_repo):
275
"""Create a new repository if needed, returning the repository."""
277
return self.create_repository()
279
return self.find_repository()
280
except errors.NoRepositoryPresent:
281
return self.create_repository()
284
def create_branch_convenience(base, force_new_repo=False,
285
force_new_tree=None, format=None,
286
possible_transports=None):
287
"""Create a new BzrDir, Branch and Repository at the url 'base'.
289
This is a convenience function - it will use an existing repository
290
if possible, can be told explicitly whether to create a working tree or
293
This will use the current default BzrDirFormat unless one is
294
specified, and use whatever
295
repository format that that uses via bzrdir.create_branch and
296
create_repository. If a shared repository is available that is used
297
preferentially. Whatever repository is used, its tree creation policy
300
The created Branch object is returned.
301
If a working tree cannot be made due to base not being a file:// url,
302
no error is raised unless force_new_tree is True, in which case no
303
data is created on disk and NotLocalUrl is raised.
305
:param base: The URL to create the branch at.
306
:param force_new_repo: If True a new repository is always created.
307
:param force_new_tree: If True or False force creation of a tree or
308
prevent such creation respectively.
309
:param format: Override for the bzrdir format to create.
310
:param possible_transports: An optional reusable transports list.
313
# check for non local urls
314
t = get_transport(base, possible_transports)
315
if not isinstance(t, LocalTransport):
316
raise errors.NotLocalUrl(base)
317
bzrdir = BzrDir.create(base, format, possible_transports)
318
repo = bzrdir._find_or_create_repository(force_new_repo)
319
result = bzrdir.create_branch()
320
if force_new_tree or (repo.make_working_trees() and
321
force_new_tree is None):
323
bzrdir.create_workingtree()
324
except errors.NotLocalUrl:
329
@deprecated_function(zero_ninetyone)
330
def create_repository(base, shared=False, format=None):
331
"""Create a new BzrDir and Repository at the url 'base'.
333
If no format is supplied, this will default to the current default
334
BzrDirFormat by default, and use whatever repository format that that
335
uses for bzrdirformat.create_repository.
337
:param shared: Create a shared repository rather than a standalone
339
The Repository object is returned.
341
This must be overridden as an instance method in child classes, where
342
it should take no parameters and construct whatever repository format
343
that child class desires.
345
This method is deprecated, please call create_repository on a bzrdir
348
bzrdir = BzrDir.create(base, format)
349
return bzrdir.create_repository(shared)
352
def create_standalone_workingtree(base, format=None):
353
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
355
'base' must be a local path or a file:// url.
357
This will use the current default BzrDirFormat unless one is
358
specified, and use whatever
359
repository format that that uses for bzrdirformat.create_workingtree,
360
create_branch and create_repository.
362
:param format: Override for the bzrdir format to create.
363
:return: The WorkingTree object.
365
t = get_transport(base)
366
if not isinstance(t, LocalTransport):
367
raise errors.NotLocalUrl(base)
368
bzrdir = BzrDir.create_branch_and_repo(base,
370
format=format).bzrdir
371
return bzrdir.create_workingtree()
373
def create_workingtree(self, revision_id=None):
374
"""Create a working tree at this BzrDir.
376
revision_id: create it as of this revision id.
378
raise NotImplementedError(self.create_workingtree)
380
def retire_bzrdir(self, limit=10000):
381
"""Permanently disable the bzrdir.
383
This is done by renaming it to give the user some ability to recover
384
if there was a problem.
386
This will have horrible consequences if anyone has anything locked or
388
:param limit: number of times to retry
393
to_path = '.bzr.retired.%d' % i
394
self.root_transport.rename('.bzr', to_path)
395
note("renamed %s to %s"
396
% (self.root_transport.abspath('.bzr'), to_path))
398
except (errors.TransportError, IOError, errors.PathError):
405
def destroy_workingtree(self):
406
"""Destroy the working tree at this BzrDir.
408
Formats that do not support this may raise UnsupportedOperation.
410
raise NotImplementedError(self.destroy_workingtree)
412
def destroy_workingtree_metadata(self):
413
"""Destroy the control files for the working tree at this BzrDir.
415
The contents of working tree files are not affected.
416
Formats that do not support this may raise UnsupportedOperation.
418
raise NotImplementedError(self.destroy_workingtree_metadata)
420
def find_repository(self):
421
"""Find the repository that should be used.
423
This does not require a branch as we use it to find the repo for
424
new branches as well as to hook existing branches up to their
428
return self.open_repository()
429
except errors.NoRepositoryPresent:
431
next_transport = self.root_transport.clone('..')
433
# find the next containing bzrdir
435
found_bzrdir = BzrDir.open_containing_from_transport(
437
except errors.NotBranchError:
439
raise errors.NoRepositoryPresent(self)
440
# does it have a repository ?
442
repository = found_bzrdir.open_repository()
443
except errors.NoRepositoryPresent:
444
next_transport = found_bzrdir.root_transport.clone('..')
445
if (found_bzrdir.root_transport.base == next_transport.base):
446
# top of the file system
450
if ((found_bzrdir.root_transport.base ==
451
self.root_transport.base) or repository.is_shared()):
454
raise errors.NoRepositoryPresent(self)
455
raise errors.NoRepositoryPresent(self)
457
def get_branch_reference(self):
458
"""Return the referenced URL for the branch in this bzrdir.
460
:raises NotBranchError: If there is no Branch.
461
:return: The URL the branch in this bzrdir references if it is a
462
reference branch, or None for regular branches.
466
def get_branch_transport(self, branch_format):
467
"""Get the transport for use by branch format in this BzrDir.
469
Note that bzr dirs that do not support format strings will raise
470
IncompatibleFormat if the branch format they are given has
471
a format string, and vice versa.
473
If branch_format is None, the transport is returned with no
474
checking. If it is not None, then the returned transport is
475
guaranteed to point to an existing directory ready for use.
477
raise NotImplementedError(self.get_branch_transport)
479
def get_repository_transport(self, repository_format):
480
"""Get the transport for use by repository format in this BzrDir.
482
Note that bzr dirs that do not support format strings will raise
483
IncompatibleFormat if the repository format they are given has
484
a format string, and vice versa.
486
If repository_format is None, the transport is returned with no
487
checking. If it is not None, then the returned transport is
488
guaranteed to point to an existing directory ready for use.
490
raise NotImplementedError(self.get_repository_transport)
492
def get_workingtree_transport(self, tree_format):
493
"""Get the transport for use by workingtree format in this BzrDir.
495
Note that bzr dirs that do not support format strings will raise
496
IncompatibleFormat if the workingtree format they are given has a
497
format string, and vice versa.
499
If workingtree_format is None, the transport is returned with no
500
checking. If it is not None, then the returned transport is
501
guaranteed to point to an existing directory ready for use.
503
raise NotImplementedError(self.get_workingtree_transport)
505
def __init__(self, _transport, _format):
506
"""Initialize a Bzr control dir object.
508
Only really common logic should reside here, concrete classes should be
509
made with varying behaviours.
511
:param _format: the format that is creating this BzrDir instance.
512
:param _transport: the transport this dir is based at.
514
self._format = _format
515
self.transport = _transport.clone('.bzr')
516
self.root_transport = _transport
518
def is_control_filename(self, filename):
519
"""True if filename is the name of a path which is reserved for bzrdir's.
521
:param filename: A filename within the root transport of this bzrdir.
523
This is true IF and ONLY IF the filename is part of the namespace reserved
524
for bzr control dirs. Currently this is the '.bzr' directory in the root
525
of the root_transport. it is expected that plugins will need to extend
526
this in the future - for instance to make bzr talk with svn working
529
# this might be better on the BzrDirFormat class because it refers to
530
# all the possible bzrdir disk formats.
531
# This method is tested via the workingtree is_control_filename tests-
532
# it was extracted from WorkingTree.is_control_filename. If the method's
533
# contract is extended beyond the current trivial implementation, please
534
# add new tests for it to the appropriate place.
535
return filename == '.bzr' or filename.startswith('.bzr/')
537
def needs_format_conversion(self, format=None):
538
"""Return true if this bzrdir needs convert_format run on it.
540
For instance, if the repository format is out of date but the
541
branch and working tree are not, this should return True.
543
:param format: Optional parameter indicating a specific desired
544
format we plan to arrive at.
546
raise NotImplementedError(self.needs_format_conversion)
549
def open_unsupported(base):
550
"""Open a branch which is not supported."""
551
return BzrDir.open(base, _unsupported=True)
554
def open(base, _unsupported=False, possible_transports=None):
555
"""Open an existing bzrdir, rooted at 'base' (url).
557
:param _unsupported: a private parameter to the BzrDir class.
559
t = get_transport(base, possible_transports=possible_transports)
560
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
563
def open_from_transport(transport, _unsupported=False,
564
_server_formats=True):
565
"""Open a bzrdir within a particular directory.
567
:param transport: Transport containing the bzrdir.
568
:param _unsupported: private.
570
base = transport.base
572
def find_format(transport):
573
return transport, BzrDirFormat.find_format(
574
transport, _server_formats=_server_formats)
576
def redirected(transport, e, redirection_notice):
577
qualified_source = e.get_source_url()
578
relpath = transport.relpath(qualified_source)
579
if not e.target.endswith(relpath):
580
# Not redirected to a branch-format, not a branch
581
raise errors.NotBranchError(path=e.target)
582
target = e.target[:-len(relpath)]
583
note('%s is%s redirected to %s',
584
transport.base, e.permanently, target)
585
# Let's try with a new transport
586
# FIXME: If 'transport' has a qualifier, this should
587
# be applied again to the new transport *iff* the
588
# schemes used are the same. Uncomment this code
589
# once the function (and tests) exist.
591
#target = urlutils.copy_url_qualifiers(original, target)
592
return get_transport(target)
595
transport, format = do_catching_redirections(find_format,
598
except errors.TooManyRedirections:
599
raise errors.NotBranchError(base)
601
BzrDir._check_supported(format, _unsupported)
602
return format.open(transport, _found=True)
604
def open_branch(self, unsupported=False):
605
"""Open the branch object at this BzrDir if one is present.
607
If unsupported is True, then no longer supported branch formats can
610
TODO: static convenience version of this?
612
raise NotImplementedError(self.open_branch)
615
def open_containing(url, possible_transports=None):
616
"""Open an existing branch which contains url.
618
:param url: url to search from.
619
See open_containing_from_transport for more detail.
621
transport = get_transport(url, possible_transports)
622
return BzrDir.open_containing_from_transport(transport)
625
def open_containing_from_transport(a_transport):
626
"""Open an existing branch which contains a_transport.base.
628
This probes for a branch at a_transport, and searches upwards from there.
630
Basically we keep looking up until we find the control directory or
631
run into the root. If there isn't one, raises NotBranchError.
632
If there is one and it is either an unrecognised format or an unsupported
633
format, UnknownFormatError or UnsupportedFormatError are raised.
634
If there is one, it is returned, along with the unused portion of url.
636
:return: The BzrDir that contains the path, and a Unicode path
637
for the rest of the URL.
639
# this gets the normalised url back. I.e. '.' -> the full path.
640
url = a_transport.base
643
result = BzrDir.open_from_transport(a_transport)
644
return result, urlutils.unescape(a_transport.relpath(url))
645
except errors.NotBranchError, e:
648
new_t = a_transport.clone('..')
649
except errors.InvalidURLJoin:
650
# reached the root, whatever that may be
651
raise errors.NotBranchError(path=url)
652
if new_t.base == a_transport.base:
653
# reached the root, whatever that may be
654
raise errors.NotBranchError(path=url)
658
def open_containing_tree_or_branch(klass, location):
659
"""Return the branch and working tree contained by a location.
661
Returns (tree, branch, relpath).
662
If there is no tree at containing the location, tree will be None.
663
If there is no branch containing the location, an exception will be
665
relpath is the portion of the path that is contained by the branch.
667
bzrdir, relpath = klass.open_containing(location)
669
tree = bzrdir.open_workingtree()
670
except (errors.NoWorkingTree, errors.NotLocalUrl):
672
branch = bzrdir.open_branch()
675
return tree, branch, relpath
677
def open_repository(self, _unsupported=False):
678
"""Open the repository object at this BzrDir if one is present.
680
This will not follow the Branch object pointer - it's strictly a direct
681
open facility. Most client code should use open_branch().repository to
684
:param _unsupported: a private parameter, not part of the api.
685
TODO: static convenience version of this?
687
raise NotImplementedError(self.open_repository)
689
def open_workingtree(self, _unsupported=False,
690
recommend_upgrade=True):
691
"""Open the workingtree object at this BzrDir if one is present.
693
:param recommend_upgrade: Optional keyword parameter, when True (the
694
default), emit through the ui module a recommendation that the user
695
upgrade the working tree when the workingtree being opened is old
696
(but still fully supported).
698
raise NotImplementedError(self.open_workingtree)
700
def has_branch(self):
701
"""Tell if this bzrdir contains a branch.
703
Note: if you're going to open the branch, you should just go ahead
704
and try, and not ask permission first. (This method just opens the
705
branch and discards it, and that's somewhat expensive.)
710
except errors.NotBranchError:
713
def has_workingtree(self):
714
"""Tell if this bzrdir contains a working tree.
716
This will still raise an exception if the bzrdir has a workingtree that
717
is remote & inaccessible.
719
Note: if you're going to open the working tree, you should just go ahead
720
and try, and not ask permission first. (This method just opens the
721
workingtree and discards it, and that's somewhat expensive.)
724
self.open_workingtree(recommend_upgrade=False)
726
except errors.NoWorkingTree:
729
def _cloning_metadir(self):
730
"""Produce a metadir suitable for cloning with."""
731
result_format = self._format.__class__()
734
branch = self.open_branch()
735
source_repository = branch.repository
736
except errors.NotBranchError:
738
source_repository = self.open_repository()
739
except errors.NoRepositoryPresent:
740
source_repository = None
742
# XXX TODO: This isinstance is here because we have not implemented
743
# the fix recommended in bug # 103195 - to delegate this choice the
745
repo_format = source_repository._format
746
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
747
result_format.repository_format = repo_format
749
# TODO: Couldn't we just probe for the format in these cases,
750
# rather than opening the whole tree? It would be a little
751
# faster. mbp 20070401
752
tree = self.open_workingtree(recommend_upgrade=False)
753
except (errors.NoWorkingTree, errors.NotLocalUrl):
754
result_format.workingtree_format = None
756
result_format.workingtree_format = tree._format.__class__()
757
return result_format, source_repository
759
def cloning_metadir(self):
760
"""Produce a metadir suitable for cloning or sprouting with.
762
These operations may produce workingtrees (yes, even though they're
763
"cloning" something that doesn't have a tree), so a viable workingtree
764
format must be selected.
766
format, repository = self._cloning_metadir()
767
if format._workingtree_format is None:
768
if repository is None:
770
tree_format = repository._format._matchingbzrdir.workingtree_format
771
format.workingtree_format = tree_format.__class__()
774
def checkout_metadir(self):
775
return self.cloning_metadir()
777
def sprout(self, url, revision_id=None, force_new_repo=False,
778
recurse='down', possible_transports=None):
779
"""Create a copy of this bzrdir prepared for use as a new line of
782
If url's last component does not exist, it will be created.
784
Attributes related to the identity of the source branch like
785
branch nickname will be cleaned, a working tree is created
786
whether one existed before or not; and a local branch is always
789
if revision_id is not None, then the clone operation may tune
790
itself to download less data.
792
target_transport = get_transport(url, possible_transports)
793
target_transport.ensure_base()
794
cloning_format = self.cloning_metadir()
795
result = cloning_format.initialize_on_transport(target_transport)
797
source_branch = self.open_branch()
798
source_repository = source_branch.repository
799
except errors.NotBranchError:
802
source_repository = self.open_repository()
803
except errors.NoRepositoryPresent:
804
source_repository = None
809
result_repo = result.find_repository()
810
except errors.NoRepositoryPresent:
812
if source_repository is None and result_repo is not None:
814
elif source_repository is None and result_repo is None:
815
# no repo available, make a new one
816
result.create_repository()
817
elif source_repository is not None and result_repo is None:
818
# have source, and want to make a new target repo
819
result_repo = source_repository.sprout(result,
820
revision_id=revision_id)
822
# fetch needed content into target.
823
if source_repository is not None:
825
# source_repository.copy_content_into(result_repo,
826
# revision_id=revision_id)
827
# so we can override the copy method
828
result_repo.fetch(source_repository, revision_id=revision_id)
829
if source_branch is not None:
830
source_branch.sprout(result, revision_id=revision_id)
832
result.create_branch()
833
if isinstance(target_transport, LocalTransport) and (
834
result_repo is None or result_repo.make_working_trees()):
835
wt = result.create_workingtree()
838
if wt.path2id('') is None:
840
wt.set_root_id(self.open_workingtree.get_root_id())
841
except errors.NoWorkingTree:
847
if recurse == 'down':
849
basis = wt.basis_tree()
851
subtrees = basis.iter_references()
852
recurse_branch = wt.branch
853
elif source_branch is not None:
854
basis = source_branch.basis_tree()
856
subtrees = basis.iter_references()
857
recurse_branch = source_branch
862
for path, file_id in subtrees:
863
target = urlutils.join(url, urlutils.escape(path))
864
sublocation = source_branch.reference_parent(file_id, path)
865
sublocation.bzrdir.sprout(target,
866
basis.get_reference_revision(file_id, path),
867
force_new_repo=force_new_repo, recurse=recurse)
869
if basis is not None:
874
class BzrDirPreSplitOut(BzrDir):
875
"""A common class for the all-in-one formats."""
877
def __init__(self, _transport, _format):
878
"""See BzrDir.__init__."""
879
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
880
assert self._format._lock_class == lockable_files.TransportLock
881
assert self._format._lock_file_name == 'branch-lock'
882
self._control_files = lockable_files.LockableFiles(
883
self.get_branch_transport(None),
884
self._format._lock_file_name,
885
self._format._lock_class)
887
def break_lock(self):
888
"""Pre-splitout bzrdirs do not suffer from stale locks."""
889
raise NotImplementedError(self.break_lock)
891
def clone(self, url, revision_id=None, force_new_repo=False):
892
"""See BzrDir.clone()."""
893
from bzrlib.workingtree import WorkingTreeFormat2
895
result = self._format._initialize_for_clone(url)
896
self.open_repository().clone(result, revision_id=revision_id)
897
from_branch = self.open_branch()
898
from_branch.clone(result, revision_id=revision_id)
900
self.open_workingtree().clone(result)
901
except errors.NotLocalUrl:
902
# make a new one, this format always has to have one.
904
WorkingTreeFormat2().initialize(result)
905
except errors.NotLocalUrl:
906
# but we cannot do it for remote trees.
907
to_branch = result.open_branch()
908
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
911
def create_branch(self):
912
"""See BzrDir.create_branch."""
913
return self.open_branch()
915
def destroy_branch(self):
916
"""See BzrDir.destroy_branch."""
917
raise errors.UnsupportedOperation(self.destroy_branch, self)
919
def create_repository(self, shared=False):
920
"""See BzrDir.create_repository."""
922
raise errors.IncompatibleFormat('shared repository', self._format)
923
return self.open_repository()
925
def create_workingtree(self, revision_id=None):
926
"""See BzrDir.create_workingtree."""
927
# this looks buggy but is not -really-
928
# because this format creates the workingtree when the bzrdir is
930
# clone and sprout will have set the revision_id
931
# and that will have set it for us, its only
932
# specific uses of create_workingtree in isolation
933
# that can do wonky stuff here, and that only
934
# happens for creating checkouts, which cannot be
935
# done on this format anyway. So - acceptable wart.
936
result = self.open_workingtree(recommend_upgrade=False)
937
if revision_id is not None:
938
if revision_id == _mod_revision.NULL_REVISION:
939
result.set_parent_ids([])
941
result.set_parent_ids([revision_id])
944
def destroy_workingtree(self):
945
"""See BzrDir.destroy_workingtree."""
946
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
948
def destroy_workingtree_metadata(self):
949
"""See BzrDir.destroy_workingtree_metadata."""
950
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
953
def get_branch_transport(self, branch_format):
954
"""See BzrDir.get_branch_transport()."""
955
if branch_format is None:
956
return self.transport
958
branch_format.get_format_string()
959
except NotImplementedError:
960
return self.transport
961
raise errors.IncompatibleFormat(branch_format, self._format)
963
def get_repository_transport(self, repository_format):
964
"""See BzrDir.get_repository_transport()."""
965
if repository_format is None:
966
return self.transport
968
repository_format.get_format_string()
969
except NotImplementedError:
970
return self.transport
971
raise errors.IncompatibleFormat(repository_format, self._format)
973
def get_workingtree_transport(self, workingtree_format):
974
"""See BzrDir.get_workingtree_transport()."""
975
if workingtree_format is None:
976
return self.transport
978
workingtree_format.get_format_string()
979
except NotImplementedError:
980
return self.transport
981
raise errors.IncompatibleFormat(workingtree_format, self._format)
983
def needs_format_conversion(self, format=None):
984
"""See BzrDir.needs_format_conversion()."""
985
# if the format is not the same as the system default,
986
# an upgrade is needed.
988
format = BzrDirFormat.get_default_format()
989
return not isinstance(self._format, format.__class__)
991
def open_branch(self, unsupported=False):
992
"""See BzrDir.open_branch."""
993
from bzrlib.branch import BzrBranchFormat4
994
format = BzrBranchFormat4()
995
self._check_supported(format, unsupported)
996
return format.open(self, _found=True)
998
def sprout(self, url, revision_id=None, force_new_repo=False,
999
possible_transports=None):
1000
"""See BzrDir.sprout()."""
1001
from bzrlib.workingtree import WorkingTreeFormat2
1002
self._make_tail(url)
1003
result = self._format._initialize_for_clone(url)
1005
self.open_repository().clone(result, revision_id=revision_id)
1006
except errors.NoRepositoryPresent:
1009
self.open_branch().sprout(result, revision_id=revision_id)
1010
except errors.NotBranchError:
1012
# we always want a working tree
1013
WorkingTreeFormat2().initialize(result)
1017
class BzrDir4(BzrDirPreSplitOut):
1018
"""A .bzr version 4 control object.
1020
This is a deprecated format and may be removed after sept 2006.
1023
def create_repository(self, shared=False):
1024
"""See BzrDir.create_repository."""
1025
return self._format.repository_format.initialize(self, shared)
1027
def needs_format_conversion(self, format=None):
1028
"""Format 4 dirs are always in need of conversion."""
1031
def open_repository(self):
1032
"""See BzrDir.open_repository."""
1033
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1034
return RepositoryFormat4().open(self, _found=True)
1037
class BzrDir5(BzrDirPreSplitOut):
1038
"""A .bzr version 5 control object.
1040
This is a deprecated format and may be removed after sept 2006.
1043
def open_repository(self):
1044
"""See BzrDir.open_repository."""
1045
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1046
return RepositoryFormat5().open(self, _found=True)
1048
def open_workingtree(self, _unsupported=False,
1049
recommend_upgrade=True):
1050
"""See BzrDir.create_workingtree."""
1051
from bzrlib.workingtree import WorkingTreeFormat2
1052
wt_format = WorkingTreeFormat2()
1053
# we don't warn here about upgrades; that ought to be handled for the
1055
return wt_format.open(self, _found=True)
1058
class BzrDir6(BzrDirPreSplitOut):
1059
"""A .bzr version 6 control object.
1061
This is a deprecated format and may be removed after sept 2006.
1064
def open_repository(self):
1065
"""See BzrDir.open_repository."""
1066
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1067
return RepositoryFormat6().open(self, _found=True)
1069
def open_workingtree(self, _unsupported=False,
1070
recommend_upgrade=True):
1071
"""See BzrDir.create_workingtree."""
1072
# we don't warn here about upgrades; that ought to be handled for the
1074
from bzrlib.workingtree import WorkingTreeFormat2
1075
return WorkingTreeFormat2().open(self, _found=True)
1078
class BzrDirMeta1(BzrDir):
1079
"""A .bzr meta version 1 control object.
1081
This is the first control object where the
1082
individual aspects are really split out: there are separate repository,
1083
workingtree and branch subdirectories and any subset of the three can be
1084
present within a BzrDir.
1087
def can_convert_format(self):
1088
"""See BzrDir.can_convert_format()."""
1091
def create_branch(self):
1092
"""See BzrDir.create_branch."""
1093
return self._format.get_branch_format().initialize(self)
1095
def destroy_branch(self):
1096
"""See BzrDir.create_branch."""
1097
self.transport.delete_tree('branch')
1099
def create_repository(self, shared=False):
1100
"""See BzrDir.create_repository."""
1101
return self._format.repository_format.initialize(self, shared)
1103
def create_workingtree(self, revision_id=None):
1104
"""See BzrDir.create_workingtree."""
1105
from bzrlib.workingtree import WorkingTreeFormat
1106
return self._format.workingtree_format.initialize(self, revision_id)
1108
def destroy_workingtree(self):
1109
"""See BzrDir.destroy_workingtree."""
1110
wt = self.open_workingtree(recommend_upgrade=False)
1111
repository = wt.branch.repository
1112
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1113
wt.revert(old_tree=empty)
1114
self.destroy_workingtree_metadata()
1116
def destroy_workingtree_metadata(self):
1117
self.transport.delete_tree('checkout')
1119
def find_branch_format(self):
1120
"""Find the branch 'format' for this bzrdir.
1122
This might be a synthetic object for e.g. RemoteBranch and SVN.
1124
from bzrlib.branch import BranchFormat
1125
return BranchFormat.find_format(self)
1127
def _get_mkdir_mode(self):
1128
"""Figure out the mode to use when creating a bzrdir subdir."""
1129
temp_control = lockable_files.LockableFiles(self.transport, '',
1130
lockable_files.TransportLock)
1131
return temp_control._dir_mode
1133
def get_branch_reference(self):
1134
"""See BzrDir.get_branch_reference()."""
1135
from bzrlib.branch import BranchFormat
1136
format = BranchFormat.find_format(self)
1137
return format.get_reference(self)
1139
def get_branch_transport(self, branch_format):
1140
"""See BzrDir.get_branch_transport()."""
1141
if branch_format is None:
1142
return self.transport.clone('branch')
1144
branch_format.get_format_string()
1145
except NotImplementedError:
1146
raise errors.IncompatibleFormat(branch_format, self._format)
1148
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1149
except errors.FileExists:
1151
return self.transport.clone('branch')
1153
def get_repository_transport(self, repository_format):
1154
"""See BzrDir.get_repository_transport()."""
1155
if repository_format is None:
1156
return self.transport.clone('repository')
1158
repository_format.get_format_string()
1159
except NotImplementedError:
1160
raise errors.IncompatibleFormat(repository_format, self._format)
1162
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1163
except errors.FileExists:
1165
return self.transport.clone('repository')
1167
def get_workingtree_transport(self, workingtree_format):
1168
"""See BzrDir.get_workingtree_transport()."""
1169
if workingtree_format is None:
1170
return self.transport.clone('checkout')
1172
workingtree_format.get_format_string()
1173
except NotImplementedError:
1174
raise errors.IncompatibleFormat(workingtree_format, self._format)
1176
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1177
except errors.FileExists:
1179
return self.transport.clone('checkout')
1181
def needs_format_conversion(self, format=None):
1182
"""See BzrDir.needs_format_conversion()."""
1184
format = BzrDirFormat.get_default_format()
1185
if not isinstance(self._format, format.__class__):
1186
# it is not a meta dir format, conversion is needed.
1188
# we might want to push this down to the repository?
1190
if not isinstance(self.open_repository()._format,
1191
format.repository_format.__class__):
1192
# the repository needs an upgrade.
1194
except errors.NoRepositoryPresent:
1197
if not isinstance(self.open_branch()._format,
1198
format.get_branch_format().__class__):
1199
# the branch needs an upgrade.
1201
except errors.NotBranchError:
1204
my_wt = self.open_workingtree(recommend_upgrade=False)
1205
if not isinstance(my_wt._format,
1206
format.workingtree_format.__class__):
1207
# the workingtree needs an upgrade.
1209
except (errors.NoWorkingTree, errors.NotLocalUrl):
1213
def open_branch(self, unsupported=False):
1214
"""See BzrDir.open_branch."""
1215
format = self.find_branch_format()
1216
self._check_supported(format, unsupported)
1217
return format.open(self, _found=True)
1219
def open_repository(self, unsupported=False):
1220
"""See BzrDir.open_repository."""
1221
from bzrlib.repository import RepositoryFormat
1222
format = RepositoryFormat.find_format(self)
1223
self._check_supported(format, unsupported)
1224
return format.open(self, _found=True)
1226
def open_workingtree(self, unsupported=False,
1227
recommend_upgrade=True):
1228
"""See BzrDir.open_workingtree."""
1229
from bzrlib.workingtree import WorkingTreeFormat
1230
format = WorkingTreeFormat.find_format(self)
1231
self._check_supported(format, unsupported,
1233
basedir=self.root_transport.base)
1234
return format.open(self, _found=True)
1237
class BzrDirFormat(object):
1238
"""An encapsulation of the initialization and open routines for a format.
1240
Formats provide three things:
1241
* An initialization routine,
1245
Formats are placed in a dict by their format string for reference
1246
during bzrdir opening. These should be subclasses of BzrDirFormat
1249
Once a format is deprecated, just deprecate the initialize and open
1250
methods on the format class. Do not deprecate the object, as the
1251
object will be created every system load.
1254
_default_format = None
1255
"""The default format used for new .bzr dirs."""
1258
"""The known formats."""
1260
_control_formats = []
1261
"""The registered control formats - .bzr, ....
1263
This is a list of BzrDirFormat objects.
1266
_control_server_formats = []
1267
"""The registered control server formats, e.g. RemoteBzrDirs.
1269
This is a list of BzrDirFormat objects.
1272
_lock_file_name = 'branch-lock'
1274
# _lock_class must be set in subclasses to the lock type, typ.
1275
# TransportLock or LockDir
1278
def find_format(klass, transport, _server_formats=True):
1279
"""Return the format present at transport."""
1281
formats = klass._control_server_formats + klass._control_formats
1283
formats = klass._control_formats
1284
for format in formats:
1286
return format.probe_transport(transport)
1287
except errors.NotBranchError:
1288
# this format does not find a control dir here.
1290
raise errors.NotBranchError(path=transport.base)
1293
def probe_transport(klass, transport):
1294
"""Return the .bzrdir style format present in a directory."""
1296
format_string = transport.get(".bzr/branch-format").read()
1297
except errors.NoSuchFile:
1298
raise errors.NotBranchError(path=transport.base)
1301
return klass._formats[format_string]
1303
raise errors.UnknownFormatError(format=format_string)
1306
def get_default_format(klass):
1307
"""Return the current default format."""
1308
return klass._default_format
1310
def get_format_string(self):
1311
"""Return the ASCII format string that identifies this format."""
1312
raise NotImplementedError(self.get_format_string)
1314
def get_format_description(self):
1315
"""Return the short description for this format."""
1316
raise NotImplementedError(self.get_format_description)
1318
def get_converter(self, format=None):
1319
"""Return the converter to use to convert bzrdirs needing converts.
1321
This returns a bzrlib.bzrdir.Converter object.
1323
This should return the best upgrader to step this format towards the
1324
current default format. In the case of plugins we can/should provide
1325
some means for them to extend the range of returnable converters.
1327
:param format: Optional format to override the default format of the
1330
raise NotImplementedError(self.get_converter)
1332
def initialize(self, url, possible_transports=None):
1333
"""Create a bzr control dir at this url and return an opened copy.
1335
Subclasses should typically override initialize_on_transport
1336
instead of this method.
1338
return self.initialize_on_transport(get_transport(url,
1339
possible_transports))
1341
def initialize_on_transport(self, transport):
1342
"""Initialize a new bzrdir in the base directory of a Transport."""
1343
# Since we don't have a .bzr directory, inherit the
1344
# mode from the root directory
1345
temp_control = lockable_files.LockableFiles(transport,
1346
'', lockable_files.TransportLock)
1347
temp_control._transport.mkdir('.bzr',
1348
# FIXME: RBC 20060121 don't peek under
1350
mode=temp_control._dir_mode)
1351
file_mode = temp_control._file_mode
1353
mutter('created control directory in ' + transport.base)
1354
control = transport.clone('.bzr')
1355
utf8_files = [('README',
1356
"This is a Bazaar-NG control directory.\n"
1357
"Do not change any files in this directory.\n"),
1358
('branch-format', self.get_format_string()),
1360
# NB: no need to escape relative paths that are url safe.
1361
control_files = lockable_files.LockableFiles(control,
1362
self._lock_file_name, self._lock_class)
1363
control_files.create_lock()
1364
control_files.lock_write()
1366
for file, content in utf8_files:
1367
control_files.put_utf8(file, content)
1369
control_files.unlock()
1370
return self.open(transport, _found=True)
1372
def is_supported(self):
1373
"""Is this format supported?
1375
Supported formats must be initializable and openable.
1376
Unsupported formats may not support initialization or committing or
1377
some other features depending on the reason for not being supported.
1381
def same_model(self, target_format):
1382
return (self.repository_format.rich_root_data ==
1383
target_format.rich_root_data)
1386
def known_formats(klass):
1387
"""Return all the known formats.
1389
Concrete formats should override _known_formats.
1391
# There is double indirection here to make sure that control
1392
# formats used by more than one dir format will only be probed
1393
# once. This can otherwise be quite expensive for remote connections.
1395
for format in klass._control_formats:
1396
result.update(format._known_formats())
1400
def _known_formats(klass):
1401
"""Return the known format instances for this control format."""
1402
return set(klass._formats.values())
1404
def open(self, transport, _found=False):
1405
"""Return an instance of this format for the dir transport points at.
1407
_found is a private parameter, do not use it.
1410
found_format = BzrDirFormat.find_format(transport)
1411
if not isinstance(found_format, self.__class__):
1412
raise AssertionError("%s was asked to open %s, but it seems to need "
1414
% (self, transport, found_format))
1415
return self._open(transport)
1417
def _open(self, transport):
1418
"""Template method helper for opening BzrDirectories.
1420
This performs the actual open and any additional logic or parameter
1423
raise NotImplementedError(self._open)
1426
def register_format(klass, format):
1427
klass._formats[format.get_format_string()] = format
1430
def register_control_format(klass, format):
1431
"""Register a format that does not use '.bzr' for its control dir.
1433
TODO: This should be pulled up into a 'ControlDirFormat' base class
1434
which BzrDirFormat can inherit from, and renamed to register_format
1435
there. It has been done without that for now for simplicity of
1438
klass._control_formats.append(format)
1441
def register_control_server_format(klass, format):
1442
"""Register a control format for client-server environments.
1444
These formats will be tried before ones registered with
1445
register_control_format. This gives implementations that decide to the
1446
chance to grab it before anything looks at the contents of the format
1449
klass._control_server_formats.append(format)
1452
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1453
def set_default_format(klass, format):
1454
klass._set_default_format(format)
1457
def _set_default_format(klass, format):
1458
"""Set default format (for testing behavior of defaults only)"""
1459
klass._default_format = format
1463
return self.get_format_string().rstrip()
1466
def unregister_format(klass, format):
1467
assert klass._formats[format.get_format_string()] is format
1468
del klass._formats[format.get_format_string()]
1471
def unregister_control_format(klass, format):
1472
klass._control_formats.remove(format)
1475
class BzrDirFormat4(BzrDirFormat):
1476
"""Bzr dir format 4.
1478
This format is a combined format for working tree, branch and repository.
1480
- Format 1 working trees [always]
1481
- Format 4 branches [always]
1482
- Format 4 repositories [always]
1484
This format is deprecated: it indexes texts using a text it which is
1485
removed in format 5; write support for this format has been removed.
1488
_lock_class = lockable_files.TransportLock
1490
def get_format_string(self):
1491
"""See BzrDirFormat.get_format_string()."""
1492
return "Bazaar-NG branch, format 0.0.4\n"
1494
def get_format_description(self):
1495
"""See BzrDirFormat.get_format_description()."""
1496
return "All-in-one format 4"
1498
def get_converter(self, format=None):
1499
"""See BzrDirFormat.get_converter()."""
1500
# there is one and only one upgrade path here.
1501
return ConvertBzrDir4To5()
1503
def initialize_on_transport(self, transport):
1504
"""Format 4 branches cannot be created."""
1505
raise errors.UninitializableFormat(self)
1507
def is_supported(self):
1508
"""Format 4 is not supported.
1510
It is not supported because the model changed from 4 to 5 and the
1511
conversion logic is expensive - so doing it on the fly was not
1516
def _open(self, transport):
1517
"""See BzrDirFormat._open."""
1518
return BzrDir4(transport, self)
1520
def __return_repository_format(self):
1521
"""Circular import protection."""
1522
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1523
return RepositoryFormat4()
1524
repository_format = property(__return_repository_format)
1527
class BzrDirFormat5(BzrDirFormat):
1528
"""Bzr control format 5.
1530
This format is a combined format for working tree, branch and repository.
1532
- Format 2 working trees [always]
1533
- Format 4 branches [always]
1534
- Format 5 repositories [always]
1535
Unhashed stores in the repository.
1538
_lock_class = lockable_files.TransportLock
1540
def get_format_string(self):
1541
"""See BzrDirFormat.get_format_string()."""
1542
return "Bazaar-NG branch, format 5\n"
1544
def get_format_description(self):
1545
"""See BzrDirFormat.get_format_description()."""
1546
return "All-in-one format 5"
1548
def get_converter(self, format=None):
1549
"""See BzrDirFormat.get_converter()."""
1550
# there is one and only one upgrade path here.
1551
return ConvertBzrDir5To6()
1553
def _initialize_for_clone(self, url):
1554
return self.initialize_on_transport(get_transport(url), _cloning=True)
1556
def initialize_on_transport(self, transport, _cloning=False):
1557
"""Format 5 dirs always have working tree, branch and repository.
1559
Except when they are being cloned.
1561
from bzrlib.branch import BzrBranchFormat4
1562
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1563
from bzrlib.workingtree import WorkingTreeFormat2
1564
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1565
RepositoryFormat5().initialize(result, _internal=True)
1567
branch = BzrBranchFormat4().initialize(result)
1569
WorkingTreeFormat2().initialize(result)
1570
except errors.NotLocalUrl:
1571
# Even though we can't access the working tree, we need to
1572
# create its control files.
1573
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1576
def _open(self, transport):
1577
"""See BzrDirFormat._open."""
1578
return BzrDir5(transport, self)
1580
def __return_repository_format(self):
1581
"""Circular import protection."""
1582
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1583
return RepositoryFormat5()
1584
repository_format = property(__return_repository_format)
1587
class BzrDirFormat6(BzrDirFormat):
1588
"""Bzr control format 6.
1590
This format is a combined format for working tree, branch and repository.
1592
- Format 2 working trees [always]
1593
- Format 4 branches [always]
1594
- Format 6 repositories [always]
1597
_lock_class = lockable_files.TransportLock
1599
def get_format_string(self):
1600
"""See BzrDirFormat.get_format_string()."""
1601
return "Bazaar-NG branch, format 6\n"
1603
def get_format_description(self):
1604
"""See BzrDirFormat.get_format_description()."""
1605
return "All-in-one format 6"
1607
def get_converter(self, format=None):
1608
"""See BzrDirFormat.get_converter()."""
1609
# there is one and only one upgrade path here.
1610
return ConvertBzrDir6ToMeta()
1612
def _initialize_for_clone(self, url):
1613
return self.initialize_on_transport(get_transport(url), _cloning=True)
1615
def initialize_on_transport(self, transport, _cloning=False):
1616
"""Format 6 dirs always have working tree, branch and repository.
1618
Except when they are being cloned.
1620
from bzrlib.branch import BzrBranchFormat4
1621
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1622
from bzrlib.workingtree import WorkingTreeFormat2
1623
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1624
RepositoryFormat6().initialize(result, _internal=True)
1626
branch = BzrBranchFormat4().initialize(result)
1628
WorkingTreeFormat2().initialize(result)
1629
except errors.NotLocalUrl:
1630
# Even though we can't access the working tree, we need to
1631
# create its control files.
1632
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1635
def _open(self, transport):
1636
"""See BzrDirFormat._open."""
1637
return BzrDir6(transport, self)
1639
def __return_repository_format(self):
1640
"""Circular import protection."""
1641
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1642
return RepositoryFormat6()
1643
repository_format = property(__return_repository_format)
1646
class BzrDirMetaFormat1(BzrDirFormat):
1647
"""Bzr meta control format 1
1649
This is the first format with split out working tree, branch and repository
1652
- Format 3 working trees [optional]
1653
- Format 5 branches [optional]
1654
- Format 7 repositories [optional]
1657
_lock_class = lockdir.LockDir
1660
self._workingtree_format = None
1661
self._branch_format = None
1663
def __eq__(self, other):
1664
if other.__class__ is not self.__class__:
1666
if other.repository_format != self.repository_format:
1668
if other.workingtree_format != self.workingtree_format:
1672
def __ne__(self, other):
1673
return not self == other
1675
def get_branch_format(self):
1676
if self._branch_format is None:
1677
from bzrlib.branch import BranchFormat
1678
self._branch_format = BranchFormat.get_default_format()
1679
return self._branch_format
1681
def set_branch_format(self, format):
1682
self._branch_format = format
1684
def get_converter(self, format=None):
1685
"""See BzrDirFormat.get_converter()."""
1687
format = BzrDirFormat.get_default_format()
1688
if not isinstance(self, format.__class__):
1689
# converting away from metadir is not implemented
1690
raise NotImplementedError(self.get_converter)
1691
return ConvertMetaToMeta(format)
1693
def get_format_string(self):
1694
"""See BzrDirFormat.get_format_string()."""
1695
return "Bazaar-NG meta directory, format 1\n"
1697
def get_format_description(self):
1698
"""See BzrDirFormat.get_format_description()."""
1699
return "Meta directory format 1"
1701
def _open(self, transport):
1702
"""See BzrDirFormat._open."""
1703
return BzrDirMeta1(transport, self)
1705
def __return_repository_format(self):
1706
"""Circular import protection."""
1707
if getattr(self, '_repository_format', None):
1708
return self._repository_format
1709
from bzrlib.repository import RepositoryFormat
1710
return RepositoryFormat.get_default_format()
1712
def __set_repository_format(self, value):
1713
"""Allow changint the repository format for metadir formats."""
1714
self._repository_format = value
1716
repository_format = property(__return_repository_format, __set_repository_format)
1718
def __get_workingtree_format(self):
1719
if self._workingtree_format is None:
1720
from bzrlib.workingtree import WorkingTreeFormat
1721
self._workingtree_format = WorkingTreeFormat.get_default_format()
1722
return self._workingtree_format
1724
def __set_workingtree_format(self, wt_format):
1725
self._workingtree_format = wt_format
1727
workingtree_format = property(__get_workingtree_format,
1728
__set_workingtree_format)
1731
# Register bzr control format
1732
BzrDirFormat.register_control_format(BzrDirFormat)
1734
# Register bzr formats
1735
BzrDirFormat.register_format(BzrDirFormat4())
1736
BzrDirFormat.register_format(BzrDirFormat5())
1737
BzrDirFormat.register_format(BzrDirFormat6())
1738
__default_format = BzrDirMetaFormat1()
1739
BzrDirFormat.register_format(__default_format)
1740
BzrDirFormat._default_format = __default_format
1743
class Converter(object):
1744
"""Converts a disk format object from one format to another."""
1746
def convert(self, to_convert, pb):
1747
"""Perform the conversion of to_convert, giving feedback via pb.
1749
:param to_convert: The disk object to convert.
1750
:param pb: a progress bar to use for progress information.
1753
def step(self, message):
1754
"""Update the pb by a step."""
1756
self.pb.update(message, self.count, self.total)
1759
class ConvertBzrDir4To5(Converter):
1760
"""Converts format 4 bzr dirs to format 5."""
1763
super(ConvertBzrDir4To5, self).__init__()
1764
self.converted_revs = set()
1765
self.absent_revisions = set()
1769
def convert(self, to_convert, pb):
1770
"""See Converter.convert()."""
1771
self.bzrdir = to_convert
1773
self.pb.note('starting upgrade from format 4 to 5')
1774
if isinstance(self.bzrdir.transport, LocalTransport):
1775
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1776
self._convert_to_weaves()
1777
return BzrDir.open(self.bzrdir.root_transport.base)
1779
def _convert_to_weaves(self):
1780
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1783
stat = self.bzrdir.transport.stat('weaves')
1784
if not S_ISDIR(stat.st_mode):
1785
self.bzrdir.transport.delete('weaves')
1786
self.bzrdir.transport.mkdir('weaves')
1787
except errors.NoSuchFile:
1788
self.bzrdir.transport.mkdir('weaves')
1789
# deliberately not a WeaveFile as we want to build it up slowly.
1790
self.inv_weave = Weave('inventory')
1791
# holds in-memory weaves for all files
1792
self.text_weaves = {}
1793
self.bzrdir.transport.delete('branch-format')
1794
self.branch = self.bzrdir.open_branch()
1795
self._convert_working_inv()
1796
rev_history = self.branch.revision_history()
1797
# to_read is a stack holding the revisions we still need to process;
1798
# appending to it adds new highest-priority revisions
1799
self.known_revisions = set(rev_history)
1800
self.to_read = rev_history[-1:]
1802
rev_id = self.to_read.pop()
1803
if (rev_id not in self.revisions
1804
and rev_id not in self.absent_revisions):
1805
self._load_one_rev(rev_id)
1807
to_import = self._make_order()
1808
for i, rev_id in enumerate(to_import):
1809
self.pb.update('converting revision', i, len(to_import))
1810
self._convert_one_rev(rev_id)
1812
self._write_all_weaves()
1813
self._write_all_revs()
1814
self.pb.note('upgraded to weaves:')
1815
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1816
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1817
self.pb.note(' %6d texts', self.text_count)
1818
self._cleanup_spare_files_after_format4()
1819
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1821
def _cleanup_spare_files_after_format4(self):
1822
# FIXME working tree upgrade foo.
1823
for n in 'merged-patches', 'pending-merged-patches':
1825
## assert os.path.getsize(p) == 0
1826
self.bzrdir.transport.delete(n)
1827
except errors.NoSuchFile:
1829
self.bzrdir.transport.delete_tree('inventory-store')
1830
self.bzrdir.transport.delete_tree('text-store')
1832
def _convert_working_inv(self):
1833
inv = xml4.serializer_v4.read_inventory(
1834
self.branch.control_files.get('inventory'))
1835
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1836
# FIXME inventory is a working tree change.
1837
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1839
def _write_all_weaves(self):
1840
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1841
weave_transport = self.bzrdir.transport.clone('weaves')
1842
weaves = WeaveStore(weave_transport, prefixed=False)
1843
transaction = WriteTransaction()
1847
for file_id, file_weave in self.text_weaves.items():
1848
self.pb.update('writing weave', i, len(self.text_weaves))
1849
weaves._put_weave(file_id, file_weave, transaction)
1851
self.pb.update('inventory', 0, 1)
1852
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1853
self.pb.update('inventory', 1, 1)
1857
def _write_all_revs(self):
1858
"""Write all revisions out in new form."""
1859
self.bzrdir.transport.delete_tree('revision-store')
1860
self.bzrdir.transport.mkdir('revision-store')
1861
revision_transport = self.bzrdir.transport.clone('revision-store')
1863
_revision_store = TextRevisionStore(TextStore(revision_transport,
1867
transaction = WriteTransaction()
1868
for i, rev_id in enumerate(self.converted_revs):
1869
self.pb.update('write revision', i, len(self.converted_revs))
1870
_revision_store.add_revision(self.revisions[rev_id], transaction)
1874
def _load_one_rev(self, rev_id):
1875
"""Load a revision object into memory.
1877
Any parents not either loaded or abandoned get queued to be
1879
self.pb.update('loading revision',
1880
len(self.revisions),
1881
len(self.known_revisions))
1882
if not self.branch.repository.has_revision(rev_id):
1884
self.pb.note('revision {%s} not present in branch; '
1885
'will be converted as a ghost',
1887
self.absent_revisions.add(rev_id)
1889
rev = self.branch.repository._revision_store.get_revision(rev_id,
1890
self.branch.repository.get_transaction())
1891
for parent_id in rev.parent_ids:
1892
self.known_revisions.add(parent_id)
1893
self.to_read.append(parent_id)
1894
self.revisions[rev_id] = rev
1896
def _load_old_inventory(self, rev_id):
1897
assert rev_id not in self.converted_revs
1898
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1899
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1900
inv.revision_id = rev_id
1901
rev = self.revisions[rev_id]
1902
if rev.inventory_sha1:
1903
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1904
'inventory sha mismatch for {%s}' % rev_id
1907
def _load_updated_inventory(self, rev_id):
1908
assert rev_id in self.converted_revs
1909
inv_xml = self.inv_weave.get_text(rev_id)
1910
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1913
def _convert_one_rev(self, rev_id):
1914
"""Convert revision and all referenced objects to new format."""
1915
rev = self.revisions[rev_id]
1916
inv = self._load_old_inventory(rev_id)
1917
present_parents = [p for p in rev.parent_ids
1918
if p not in self.absent_revisions]
1919
self._convert_revision_contents(rev, inv, present_parents)
1920
self._store_new_inv(rev, inv, present_parents)
1921
self.converted_revs.add(rev_id)
1923
def _store_new_inv(self, rev, inv, present_parents):
1924
# the XML is now updated with text versions
1926
entries = inv.iter_entries()
1928
for path, ie in entries:
1929
assert getattr(ie, 'revision', None) is not None, \
1930
'no revision on {%s} in {%s}' % \
1931
(file_id, rev.revision_id)
1932
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1933
new_inv_sha1 = sha_string(new_inv_xml)
1934
self.inv_weave.add_lines(rev.revision_id,
1936
new_inv_xml.splitlines(True))
1937
rev.inventory_sha1 = new_inv_sha1
1939
def _convert_revision_contents(self, rev, inv, present_parents):
1940
"""Convert all the files within a revision.
1942
Also upgrade the inventory to refer to the text revision ids."""
1943
rev_id = rev.revision_id
1944
mutter('converting texts of revision {%s}',
1946
parent_invs = map(self._load_updated_inventory, present_parents)
1947
entries = inv.iter_entries()
1949
for path, ie in entries:
1950
self._convert_file_version(rev, ie, parent_invs)
1952
def _convert_file_version(self, rev, ie, parent_invs):
1953
"""Convert one version of one file.
1955
The file needs to be added into the weave if it is a merge
1956
of >=2 parents or if it's changed from its parent.
1958
file_id = ie.file_id
1959
rev_id = rev.revision_id
1960
w = self.text_weaves.get(file_id)
1963
self.text_weaves[file_id] = w
1964
text_changed = False
1965
parent_candiate_entries = ie.parent_candidates(parent_invs)
1966
for old_revision in parent_candiate_entries.keys():
1967
# if this fails, its a ghost ?
1968
assert old_revision in self.converted_revs, \
1969
"Revision {%s} not in converted_revs" % old_revision
1970
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1971
# XXX: Note that this is unordered - and this is tolerable because
1972
# the previous code was also unordered.
1973
previous_entries = dict((head, parent_candiate_entries[head]) for head
1975
self.snapshot_ie(previous_entries, ie, w, rev_id)
1977
assert getattr(ie, 'revision', None) is not None
1979
def get_parents(self, revision_ids):
1980
for revision_id in revision_ids:
1981
yield self.revisions[revision_id].parent_ids
1983
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1984
# TODO: convert this logic, which is ~= snapshot to
1985
# a call to:. This needs the path figured out. rather than a work_tree
1986
# a v4 revision_tree can be given, or something that looks enough like
1987
# one to give the file content to the entry if it needs it.
1988
# and we need something that looks like a weave store for snapshot to
1990
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1991
if len(previous_revisions) == 1:
1992
previous_ie = previous_revisions.values()[0]
1993
if ie._unchanged(previous_ie):
1994
ie.revision = previous_ie.revision
1997
text = self.branch.repository.weave_store.get(ie.text_id)
1998
file_lines = text.readlines()
1999
assert sha_strings(file_lines) == ie.text_sha1
2000
assert sum(map(len, file_lines)) == ie.text_size
2001
w.add_lines(rev_id, previous_revisions, file_lines)
2002
self.text_count += 1
2004
w.add_lines(rev_id, previous_revisions, [])
2005
ie.revision = rev_id
2007
def _make_order(self):
2008
"""Return a suitable order for importing revisions.
2010
The order must be such that an revision is imported after all
2011
its (present) parents.
2013
todo = set(self.revisions.keys())
2014
done = self.absent_revisions.copy()
2017
# scan through looking for a revision whose parents
2019
for rev_id in sorted(list(todo)):
2020
rev = self.revisions[rev_id]
2021
parent_ids = set(rev.parent_ids)
2022
if parent_ids.issubset(done):
2023
# can take this one now
2024
order.append(rev_id)
2030
class ConvertBzrDir5To6(Converter):
2031
"""Converts format 5 bzr dirs to format 6."""
2033
def convert(self, to_convert, pb):
2034
"""See Converter.convert()."""
2035
self.bzrdir = to_convert
2037
self.pb.note('starting upgrade from format 5 to 6')
2038
self._convert_to_prefixed()
2039
return BzrDir.open(self.bzrdir.root_transport.base)
2041
def _convert_to_prefixed(self):
2042
from bzrlib.store import TransportStore
2043
self.bzrdir.transport.delete('branch-format')
2044
for store_name in ["weaves", "revision-store"]:
2045
self.pb.note("adding prefixes to %s" % store_name)
2046
store_transport = self.bzrdir.transport.clone(store_name)
2047
store = TransportStore(store_transport, prefixed=True)
2048
for urlfilename in store_transport.list_dir('.'):
2049
filename = urlutils.unescape(urlfilename)
2050
if (filename.endswith(".weave") or
2051
filename.endswith(".gz") or
2052
filename.endswith(".sig")):
2053
file_id = os.path.splitext(filename)[0]
2056
prefix_dir = store.hash_prefix(file_id)
2057
# FIXME keep track of the dirs made RBC 20060121
2059
store_transport.move(filename, prefix_dir + '/' + filename)
2060
except errors.NoSuchFile: # catches missing dirs strangely enough
2061
store_transport.mkdir(prefix_dir)
2062
store_transport.move(filename, prefix_dir + '/' + filename)
2063
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2066
class ConvertBzrDir6ToMeta(Converter):
2067
"""Converts format 6 bzr dirs to metadirs."""
2069
def convert(self, to_convert, pb):
2070
"""See Converter.convert()."""
2071
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2072
from bzrlib.branch import BzrBranchFormat5
2073
self.bzrdir = to_convert
2076
self.total = 20 # the steps we know about
2077
self.garbage_inventories = []
2079
self.pb.note('starting upgrade from format 6 to metadir')
2080
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2081
# its faster to move specific files around than to open and use the apis...
2082
# first off, nuke ancestry.weave, it was never used.
2084
self.step('Removing ancestry.weave')
2085
self.bzrdir.transport.delete('ancestry.weave')
2086
except errors.NoSuchFile:
2088
# find out whats there
2089
self.step('Finding branch files')
2090
last_revision = self.bzrdir.open_branch().last_revision()
2091
bzrcontents = self.bzrdir.transport.list_dir('.')
2092
for name in bzrcontents:
2093
if name.startswith('basis-inventory.'):
2094
self.garbage_inventories.append(name)
2095
# create new directories for repository, working tree and branch
2096
self.dir_mode = self.bzrdir._control_files._dir_mode
2097
self.file_mode = self.bzrdir._control_files._file_mode
2098
repository_names = [('inventory.weave', True),
2099
('revision-store', True),
2101
self.step('Upgrading repository ')
2102
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2103
self.make_lock('repository')
2104
# we hard code the formats here because we are converting into
2105
# the meta format. The meta format upgrader can take this to a
2106
# future format within each component.
2107
self.put_format('repository', RepositoryFormat7())
2108
for entry in repository_names:
2109
self.move_entry('repository', entry)
2111
self.step('Upgrading branch ')
2112
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2113
self.make_lock('branch')
2114
self.put_format('branch', BzrBranchFormat5())
2115
branch_files = [('revision-history', True),
2116
('branch-name', True),
2118
for entry in branch_files:
2119
self.move_entry('branch', entry)
2121
checkout_files = [('pending-merges', True),
2122
('inventory', True),
2123
('stat-cache', False)]
2124
# If a mandatory checkout file is not present, the branch does not have
2125
# a functional checkout. Do not create a checkout in the converted
2127
for name, mandatory in checkout_files:
2128
if mandatory and name not in bzrcontents:
2129
has_checkout = False
2133
if not has_checkout:
2134
self.pb.note('No working tree.')
2135
# If some checkout files are there, we may as well get rid of them.
2136
for name, mandatory in checkout_files:
2137
if name in bzrcontents:
2138
self.bzrdir.transport.delete(name)
2140
from bzrlib.workingtree import WorkingTreeFormat3
2141
self.step('Upgrading working tree')
2142
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2143
self.make_lock('checkout')
2145
'checkout', WorkingTreeFormat3())
2146
self.bzrdir.transport.delete_multi(
2147
self.garbage_inventories, self.pb)
2148
for entry in checkout_files:
2149
self.move_entry('checkout', entry)
2150
if last_revision is not None:
2151
self.bzrdir._control_files.put_utf8(
2152
'checkout/last-revision', last_revision)
2153
self.bzrdir._control_files.put_utf8(
2154
'branch-format', BzrDirMetaFormat1().get_format_string())
2155
return BzrDir.open(self.bzrdir.root_transport.base)
2157
def make_lock(self, name):
2158
"""Make a lock for the new control dir name."""
2159
self.step('Make %s lock' % name)
2160
ld = lockdir.LockDir(self.bzrdir.transport,
2162
file_modebits=self.file_mode,
2163
dir_modebits=self.dir_mode)
2166
def move_entry(self, new_dir, entry):
2167
"""Move then entry name into new_dir."""
2169
mandatory = entry[1]
2170
self.step('Moving %s' % name)
2172
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2173
except errors.NoSuchFile:
2177
def put_format(self, dirname, format):
2178
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2181
class ConvertMetaToMeta(Converter):
2182
"""Converts the components of metadirs."""
2184
def __init__(self, target_format):
2185
"""Create a metadir to metadir converter.
2187
:param target_format: The final metadir format that is desired.
2189
self.target_format = target_format
2191
def convert(self, to_convert, pb):
2192
"""See Converter.convert()."""
2193
self.bzrdir = to_convert
2197
self.step('checking repository format')
2199
repo = self.bzrdir.open_repository()
2200
except errors.NoRepositoryPresent:
2203
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2204
from bzrlib.repository import CopyConverter
2205
self.pb.note('starting repository conversion')
2206
converter = CopyConverter(self.target_format.repository_format)
2207
converter.convert(repo, pb)
2209
branch = self.bzrdir.open_branch()
2210
except errors.NotBranchError:
2213
# TODO: conversions of Branch and Tree should be done by
2214
# InterXFormat lookups
2215
# Avoid circular imports
2216
from bzrlib import branch as _mod_branch
2217
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2218
self.target_format.get_branch_format().__class__ is
2219
_mod_branch.BzrBranchFormat6):
2220
branch_converter = _mod_branch.Converter5to6()
2221
branch_converter.convert(branch)
2223
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2224
except (errors.NoWorkingTree, errors.NotLocalUrl):
2227
# TODO: conversions of Branch and Tree should be done by
2228
# InterXFormat lookups
2229
if (isinstance(tree, workingtree.WorkingTree3) and
2230
not isinstance(tree, workingtree_4.WorkingTree4) and
2231
isinstance(self.target_format.workingtree_format,
2232
workingtree_4.WorkingTreeFormat4)):
2233
workingtree_4.Converter3to4().convert(tree)
2237
# This is not in remote.py because it's small, and needs to be registered.
2238
# Putting it in remote.py creates a circular import problem.
2239
# we can make it a lazy object if the control formats is turned into something
2241
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2242
"""Format representing bzrdirs accessed via a smart server"""
2244
def get_format_description(self):
2245
return 'bzr remote bzrdir'
2248
def probe_transport(klass, transport):
2249
"""Return a RemoteBzrDirFormat object if it looks possible."""
2251
client = transport.get_smart_client()
2252
except (NotImplementedError, AttributeError,
2253
errors.TransportNotPossible):
2254
# no smart server, so not a branch for this format type.
2255
raise errors.NotBranchError(path=transport.base)
2257
# Send a 'hello' request in protocol version one, and decline to
2258
# open it if the server doesn't support our required version (2) so
2259
# that the VFS-based transport will do it.
2260
request = client.get_request()
2261
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2262
server_version = smart_protocol.query_version()
2263
if server_version != 2:
2264
raise errors.NotBranchError(path=transport.base)
2267
def initialize_on_transport(self, transport):
2269
# hand off the request to the smart server
2270
shared_medium = transport.get_shared_medium()
2271
except errors.NoSmartMedium:
2272
# TODO: lookup the local format from a server hint.
2273
local_dir_format = BzrDirMetaFormat1()
2274
return local_dir_format.initialize_on_transport(transport)
2275
client = _SmartClient(shared_medium)
2276
path = client.remote_path_from_transport(transport)
2277
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2279
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2280
return remote.RemoteBzrDir(transport)
2282
def _open(self, transport):
2283
return remote.RemoteBzrDir(transport)
2285
def __eq__(self, other):
2286
if not isinstance(other, RemoteBzrDirFormat):
2288
return self.get_format_description() == other.get_format_description()
2291
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2294
class BzrDirFormatInfo(object):
2296
def __init__(self, native, deprecated, hidden, experimental):
2297
self.deprecated = deprecated
2298
self.native = native
2299
self.hidden = hidden
2300
self.experimental = experimental
2303
class BzrDirFormatRegistry(registry.Registry):
2304
"""Registry of user-selectable BzrDir subformats.
2306
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2307
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2310
def register_metadir(self, key,
2311
repository_format, help, native=True, deprecated=False,
2315
experimental=False):
2316
"""Register a metadir subformat.
2318
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2319
by the Repository format.
2321
:param repository_format: The fully-qualified repository format class
2323
:param branch_format: Fully-qualified branch format class name as
2325
:param tree_format: Fully-qualified tree format class name as
2328
# This should be expanded to support setting WorkingTree and Branch
2329
# formats, once BzrDirMetaFormat1 supports that.
2330
def _load(full_name):
2331
mod_name, factory_name = full_name.rsplit('.', 1)
2333
mod = __import__(mod_name, globals(), locals(),
2335
except ImportError, e:
2336
raise ImportError('failed to load %s: %s' % (full_name, e))
2338
factory = getattr(mod, factory_name)
2339
except AttributeError:
2340
raise AttributeError('no factory %s in module %r'
2345
bd = BzrDirMetaFormat1()
2346
if branch_format is not None:
2347
bd.set_branch_format(_load(branch_format))
2348
if tree_format is not None:
2349
bd.workingtree_format = _load(tree_format)
2350
if repository_format is not None:
2351
bd.repository_format = _load(repository_format)
2353
self.register(key, helper, help, native, deprecated, hidden,
2356
def register(self, key, factory, help, native=True, deprecated=False,
2357
hidden=False, experimental=False):
2358
"""Register a BzrDirFormat factory.
2360
The factory must be a callable that takes one parameter: the key.
2361
It must produce an instance of the BzrDirFormat when called.
2363
This function mainly exists to prevent the info object from being
2366
registry.Registry.register(self, key, factory, help,
2367
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2369
def register_lazy(self, key, module_name, member_name, help, native=True,
2370
deprecated=False, hidden=False, experimental=False):
2371
registry.Registry.register_lazy(self, key, module_name, member_name,
2372
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2374
def set_default(self, key):
2375
"""Set the 'default' key to be a clone of the supplied key.
2377
This method must be called once and only once.
2379
registry.Registry.register(self, 'default', self.get(key),
2380
self.get_help(key), info=self.get_info(key))
2382
def set_default_repository(self, key):
2383
"""Set the FormatRegistry default and Repository default.
2385
This is a transitional method while Repository.set_default_format
2388
if 'default' in self:
2389
self.remove('default')
2390
self.set_default(key)
2391
format = self.get('default')()
2392
assert isinstance(format, BzrDirMetaFormat1)
2394
def make_bzrdir(self, key):
2395
return self.get(key)()
2397
def help_topic(self, topic):
2398
output = textwrap.dedent("""\
2399
These formats can be used for creating branches, working trees, and
2403
default_realkey = None
2404
default_help = self.get_help('default')
2406
for key in self.keys():
2407
if key == 'default':
2409
help = self.get_help(key)
2410
if help == default_help:
2411
default_realkey = key
2413
help_pairs.append((key, help))
2415
def wrapped(key, help, info):
2417
help = '(native) ' + help
2418
return ':%s:\n%s\n\n' % (key,
2419
textwrap.fill(help, initial_indent=' ',
2420
subsequent_indent=' '))
2421
if default_realkey is not None:
2422
output += wrapped(default_realkey, '(default) %s' % default_help,
2423
self.get_info('default'))
2424
deprecated_pairs = []
2425
experimental_pairs = []
2426
for key, help in help_pairs:
2427
info = self.get_info(key)
2430
elif info.deprecated:
2431
deprecated_pairs.append((key, help))
2432
elif info.experimental:
2433
experimental_pairs.append((key, help))
2435
output += wrapped(key, help, info)
2436
if len(experimental_pairs) > 0:
2437
output += "Experimental formats are shown below.\n\n"
2438
for key, help in experimental_pairs:
2439
info = self.get_info(key)
2440
output += wrapped(key, help, info)
2441
if len(deprecated_pairs) > 0:
2442
output += "Deprecated formats are shown below.\n\n"
2443
for key, help in deprecated_pairs:
2444
info = self.get_info(key)
2445
output += wrapped(key, help, info)
2450
format_registry = BzrDirFormatRegistry()
2451
format_registry.register('weave', BzrDirFormat6,
2452
'Pre-0.8 format. Slower than knit and does not'
2453
' support checkouts or shared repositories.',
2455
format_registry.register_metadir('knit',
2456
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2457
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2458
branch_format='bzrlib.branch.BzrBranchFormat5',
2459
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2460
format_registry.register_metadir('metaweave',
2461
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2462
'Transitional format in 0.8. Slower than knit.',
2463
branch_format='bzrlib.branch.BzrBranchFormat5',
2464
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2466
format_registry.register_metadir('dirstate',
2467
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2468
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2469
'above when accessed over the network.',
2470
branch_format='bzrlib.branch.BzrBranchFormat5',
2471
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2472
# directly from workingtree_4 triggers a circular import.
2473
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2475
format_registry.register_metadir('dirstate-tags',
2476
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2477
help='New in 0.15: Fast local operations and improved scaling for '
2478
'network operations. Additionally adds support for tags.'
2479
' Incompatible with bzr < 0.15.',
2480
branch_format='bzrlib.branch.BzrBranchFormat6',
2481
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2483
format_registry.register_metadir('dirstate-with-subtree',
2484
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2485
help='New in 0.15: Fast local operations and improved scaling for '
2486
'network operations. Additionally adds support for versioning nested '
2487
'bzr branches. Incompatible with bzr < 0.15.',
2488
branch_format='bzrlib.branch.BzrBranchFormat6',
2489
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2492
format_registry.register_metadir('knitpack-experimental',
2493
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2494
help='New in 0.92: Pack-based format with data compatible with '
2495
'dirstate-tags format repositories. Interoperates with '
2496
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2497
'NOTE: This format is experimental. Before using it, please read '
2498
'http://doc.bazaar-vcs.org/latest/developers/knitpack.html.',
2499
branch_format='bzrlib.branch.BzrBranchFormat6',
2500
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2503
format_registry.register_metadir('knitpack-subtree-experimental',
2504
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2505
help='New in 0.92: Pack-based format with data compatible with '
2506
'dirstate-with-subtree format repositories. Interoperates with '
2507
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2508
'NOTE: This format is experimental. Before using it, please read '
2509
'http://doc.bazaar-vcs.org/latest/developers/knitpack.html.',
2510
branch_format='bzrlib.branch.BzrBranchFormat6',
2511
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2515
format_registry.set_default('dirstate-tags')