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
result_repo = result.find_repository()
213
except errors.NoRepositoryPresent:
215
if result_repo is None or result_repo.make_working_trees():
217
self.open_workingtree().clone(result)
218
except (errors.NoWorkingTree, errors.NotLocalUrl):
222
# TODO: This should be given a Transport, and should chdir up; otherwise
223
# this will open a new connection.
224
def _make_tail(self, url):
225
t = get_transport(url)
229
def create(cls, base, format=None, possible_transports=None):
230
"""Create a new BzrDir at the url 'base'.
232
:param format: If supplied, the format of branch to create. If not
233
supplied, the default is used.
234
:param possible_transports: If supplied, a list of transports that
235
can be reused to share a remote connection.
237
if cls is not BzrDir:
238
raise AssertionError("BzrDir.create always creates the default"
239
" format, not one of %r" % cls)
240
t = get_transport(base, possible_transports)
243
format = BzrDirFormat.get_default_format()
244
return format.initialize_on_transport(t)
246
def create_branch(self):
247
"""Create a branch in this BzrDir.
249
The bzrdir's format will control what branch format is created.
250
For more control see BranchFormatXX.create(a_bzrdir).
252
raise NotImplementedError(self.create_branch)
254
def destroy_branch(self):
255
"""Destroy the branch in this BzrDir"""
256
raise NotImplementedError(self.destroy_branch)
259
def create_branch_and_repo(base, force_new_repo=False, format=None):
260
"""Create a new BzrDir, Branch and Repository at the url 'base'.
262
This will use the current default BzrDirFormat unless one is
263
specified, and use whatever
264
repository format that that uses via bzrdir.create_branch and
265
create_repository. If a shared repository is available that is used
268
The created Branch object is returned.
270
:param base: The URL to create the branch at.
271
:param force_new_repo: If True a new repository is always created.
272
:param format: If supplied, the format of branch to create. If not
273
supplied, the default is used.
275
bzrdir = BzrDir.create(base, format)
276
bzrdir._find_or_create_repository(force_new_repo)
277
return bzrdir.create_branch()
279
def _find_or_create_repository(self, force_new_repo):
280
"""Create a new repository if needed, returning the repository."""
282
return self.create_repository()
284
return self.find_repository()
285
except errors.NoRepositoryPresent:
286
return self.create_repository()
289
def create_branch_convenience(base, force_new_repo=False,
290
force_new_tree=None, format=None,
291
possible_transports=None):
292
"""Create a new BzrDir, Branch and Repository at the url 'base'.
294
This is a convenience function - it will use an existing repository
295
if possible, can be told explicitly whether to create a working tree or
298
This will use the current default BzrDirFormat unless one is
299
specified, and use whatever
300
repository format that that uses via bzrdir.create_branch and
301
create_repository. If a shared repository is available that is used
302
preferentially. Whatever repository is used, its tree creation policy
305
The created Branch object is returned.
306
If a working tree cannot be made due to base not being a file:// url,
307
no error is raised unless force_new_tree is True, in which case no
308
data is created on disk and NotLocalUrl is raised.
310
:param base: The URL to create the branch at.
311
:param force_new_repo: If True a new repository is always created.
312
:param force_new_tree: If True or False force creation of a tree or
313
prevent such creation respectively.
314
:param format: Override for the bzrdir format to create.
315
:param possible_transports: An optional reusable transports list.
318
# check for non local urls
319
t = get_transport(base, possible_transports)
320
if not isinstance(t, LocalTransport):
321
raise errors.NotLocalUrl(base)
322
bzrdir = BzrDir.create(base, format, possible_transports)
323
repo = bzrdir._find_or_create_repository(force_new_repo)
324
result = bzrdir.create_branch()
325
if force_new_tree or (repo.make_working_trees() and
326
force_new_tree is None):
328
bzrdir.create_workingtree()
329
except errors.NotLocalUrl:
334
@deprecated_function(zero_ninetyone)
335
def create_repository(base, shared=False, format=None):
336
"""Create a new BzrDir and Repository at the url 'base'.
338
If no format is supplied, this will default to the current default
339
BzrDirFormat by default, and use whatever repository format that that
340
uses for bzrdirformat.create_repository.
342
:param shared: Create a shared repository rather than a standalone
344
The Repository object is returned.
346
This must be overridden as an instance method in child classes, where
347
it should take no parameters and construct whatever repository format
348
that child class desires.
350
This method is deprecated, please call create_repository on a bzrdir
353
bzrdir = BzrDir.create(base, format)
354
return bzrdir.create_repository(shared)
357
def create_standalone_workingtree(base, format=None):
358
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
360
'base' must be a local path or a file:// url.
362
This will use the current default BzrDirFormat unless one is
363
specified, and use whatever
364
repository format that that uses for bzrdirformat.create_workingtree,
365
create_branch and create_repository.
367
:param format: Override for the bzrdir format to create.
368
:return: The WorkingTree object.
370
t = get_transport(base)
371
if not isinstance(t, LocalTransport):
372
raise errors.NotLocalUrl(base)
373
bzrdir = BzrDir.create_branch_and_repo(base,
375
format=format).bzrdir
376
return bzrdir.create_workingtree()
378
def create_workingtree(self, revision_id=None, from_branch=None):
379
"""Create a working tree at this BzrDir.
381
:param revision_id: create it as of this revision id.
382
:param from_branch: override bzrdir branch (for lightweight checkouts)
384
raise NotImplementedError(self.create_workingtree)
386
def retire_bzrdir(self, limit=10000):
387
"""Permanently disable the bzrdir.
389
This is done by renaming it to give the user some ability to recover
390
if there was a problem.
392
This will have horrible consequences if anyone has anything locked or
394
:param limit: number of times to retry
399
to_path = '.bzr.retired.%d' % i
400
self.root_transport.rename('.bzr', to_path)
401
note("renamed %s to %s"
402
% (self.root_transport.abspath('.bzr'), to_path))
404
except (errors.TransportError, IOError, errors.PathError):
411
def destroy_workingtree(self):
412
"""Destroy the working tree at this BzrDir.
414
Formats that do not support this may raise UnsupportedOperation.
416
raise NotImplementedError(self.destroy_workingtree)
418
def destroy_workingtree_metadata(self):
419
"""Destroy the control files for the working tree at this BzrDir.
421
The contents of working tree files are not affected.
422
Formats that do not support this may raise UnsupportedOperation.
424
raise NotImplementedError(self.destroy_workingtree_metadata)
426
def find_repository(self):
427
"""Find the repository that should be used.
429
This does not require a branch as we use it to find the repo for
430
new branches as well as to hook existing branches up to their
434
return self.open_repository()
435
except errors.NoRepositoryPresent:
437
next_transport = self.root_transport.clone('..')
439
# find the next containing bzrdir
441
found_bzrdir = BzrDir.open_containing_from_transport(
443
except errors.NotBranchError:
445
raise errors.NoRepositoryPresent(self)
446
# does it have a repository ?
448
repository = found_bzrdir.open_repository()
449
except errors.NoRepositoryPresent:
450
next_transport = found_bzrdir.root_transport.clone('..')
451
if (found_bzrdir.root_transport.base == next_transport.base):
452
# top of the file system
456
if ((found_bzrdir.root_transport.base ==
457
self.root_transport.base) or repository.is_shared()):
460
raise errors.NoRepositoryPresent(self)
461
raise errors.NoRepositoryPresent(self)
463
def get_branch_reference(self):
464
"""Return the referenced URL for the branch in this bzrdir.
466
:raises NotBranchError: If there is no Branch.
467
:return: The URL the branch in this bzrdir references if it is a
468
reference branch, or None for regular branches.
472
def get_branch_transport(self, branch_format):
473
"""Get the transport for use by branch format in this BzrDir.
475
Note that bzr dirs that do not support format strings will raise
476
IncompatibleFormat if the branch format they are given has
477
a format string, and vice versa.
479
If branch_format is None, the transport is returned with no
480
checking. If it is not None, then the returned transport is
481
guaranteed to point to an existing directory ready for use.
483
raise NotImplementedError(self.get_branch_transport)
485
def get_repository_transport(self, repository_format):
486
"""Get the transport for use by repository format in this BzrDir.
488
Note that bzr dirs that do not support format strings will raise
489
IncompatibleFormat if the repository format they are given has
490
a format string, and vice versa.
492
If repository_format is None, the transport is returned with no
493
checking. If it is not None, then the returned transport is
494
guaranteed to point to an existing directory ready for use.
496
raise NotImplementedError(self.get_repository_transport)
498
def get_workingtree_transport(self, tree_format):
499
"""Get the transport for use by workingtree format in this BzrDir.
501
Note that bzr dirs that do not support format strings will raise
502
IncompatibleFormat if the workingtree format they are given has a
503
format string, and vice versa.
505
If workingtree_format is None, the transport is returned with no
506
checking. If it is not None, then the returned transport is
507
guaranteed to point to an existing directory ready for use.
509
raise NotImplementedError(self.get_workingtree_transport)
511
def __init__(self, _transport, _format):
512
"""Initialize a Bzr control dir object.
514
Only really common logic should reside here, concrete classes should be
515
made with varying behaviours.
517
:param _format: the format that is creating this BzrDir instance.
518
:param _transport: the transport this dir is based at.
520
self._format = _format
521
self.transport = _transport.clone('.bzr')
522
self.root_transport = _transport
524
def is_control_filename(self, filename):
525
"""True if filename is the name of a path which is reserved for bzrdir's.
527
:param filename: A filename within the root transport of this bzrdir.
529
This is true IF and ONLY IF the filename is part of the namespace reserved
530
for bzr control dirs. Currently this is the '.bzr' directory in the root
531
of the root_transport. it is expected that plugins will need to extend
532
this in the future - for instance to make bzr talk with svn working
535
# this might be better on the BzrDirFormat class because it refers to
536
# all the possible bzrdir disk formats.
537
# This method is tested via the workingtree is_control_filename tests-
538
# it was extracted from WorkingTree.is_control_filename. If the method's
539
# contract is extended beyond the current trivial implementation, please
540
# add new tests for it to the appropriate place.
541
return filename == '.bzr' or filename.startswith('.bzr/')
543
def needs_format_conversion(self, format=None):
544
"""Return true if this bzrdir needs convert_format run on it.
546
For instance, if the repository format is out of date but the
547
branch and working tree are not, this should return True.
549
:param format: Optional parameter indicating a specific desired
550
format we plan to arrive at.
552
raise NotImplementedError(self.needs_format_conversion)
555
def open_unsupported(base):
556
"""Open a branch which is not supported."""
557
return BzrDir.open(base, _unsupported=True)
560
def open(base, _unsupported=False, possible_transports=None):
561
"""Open an existing bzrdir, rooted at 'base' (url).
563
:param _unsupported: a private parameter to the BzrDir class.
565
t = get_transport(base, possible_transports=possible_transports)
566
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
569
def open_from_transport(transport, _unsupported=False,
570
_server_formats=True):
571
"""Open a bzrdir within a particular directory.
573
:param transport: Transport containing the bzrdir.
574
:param _unsupported: private.
576
base = transport.base
578
def find_format(transport):
579
return transport, BzrDirFormat.find_format(
580
transport, _server_formats=_server_formats)
582
def redirected(transport, e, redirection_notice):
583
qualified_source = e.get_source_url()
584
relpath = transport.relpath(qualified_source)
585
if not e.target.endswith(relpath):
586
# Not redirected to a branch-format, not a branch
587
raise errors.NotBranchError(path=e.target)
588
target = e.target[:-len(relpath)]
589
note('%s is%s redirected to %s',
590
transport.base, e.permanently, target)
591
# Let's try with a new transport
592
# FIXME: If 'transport' has a qualifier, this should
593
# be applied again to the new transport *iff* the
594
# schemes used are the same. Uncomment this code
595
# once the function (and tests) exist.
597
#target = urlutils.copy_url_qualifiers(original, target)
598
return get_transport(target)
601
transport, format = do_catching_redirections(find_format,
604
except errors.TooManyRedirections:
605
raise errors.NotBranchError(base)
607
BzrDir._check_supported(format, _unsupported)
608
return format.open(transport, _found=True)
610
def open_branch(self, unsupported=False):
611
"""Open the branch object at this BzrDir if one is present.
613
If unsupported is True, then no longer supported branch formats can
616
TODO: static convenience version of this?
618
raise NotImplementedError(self.open_branch)
621
def open_containing(url, possible_transports=None):
622
"""Open an existing branch which contains url.
624
:param url: url to search from.
625
See open_containing_from_transport for more detail.
627
transport = get_transport(url, possible_transports)
628
return BzrDir.open_containing_from_transport(transport)
631
def open_containing_from_transport(a_transport):
632
"""Open an existing branch which contains a_transport.base.
634
This probes for a branch at a_transport, and searches upwards from there.
636
Basically we keep looking up until we find the control directory or
637
run into the root. If there isn't one, raises NotBranchError.
638
If there is one and it is either an unrecognised format or an unsupported
639
format, UnknownFormatError or UnsupportedFormatError are raised.
640
If there is one, it is returned, along with the unused portion of url.
642
:return: The BzrDir that contains the path, and a Unicode path
643
for the rest of the URL.
645
# this gets the normalised url back. I.e. '.' -> the full path.
646
url = a_transport.base
649
result = BzrDir.open_from_transport(a_transport)
650
return result, urlutils.unescape(a_transport.relpath(url))
651
except errors.NotBranchError, e:
654
new_t = a_transport.clone('..')
655
except errors.InvalidURLJoin:
656
# reached the root, whatever that may be
657
raise errors.NotBranchError(path=url)
658
if new_t.base == a_transport.base:
659
# reached the root, whatever that may be
660
raise errors.NotBranchError(path=url)
664
def open_containing_tree_or_branch(klass, location):
665
"""Return the branch and working tree contained by a location.
667
Returns (tree, branch, relpath).
668
If there is no tree at containing the location, tree will be None.
669
If there is no branch containing the location, an exception will be
671
relpath is the portion of the path that is contained by the branch.
673
bzrdir, relpath = klass.open_containing(location)
675
tree = bzrdir.open_workingtree()
676
except (errors.NoWorkingTree, errors.NotLocalUrl):
678
branch = bzrdir.open_branch()
681
return tree, branch, relpath
683
def open_repository(self, _unsupported=False):
684
"""Open the repository object at this BzrDir if one is present.
686
This will not follow the Branch object pointer - it's strictly a direct
687
open facility. Most client code should use open_branch().repository to
690
:param _unsupported: a private parameter, not part of the api.
691
TODO: static convenience version of this?
693
raise NotImplementedError(self.open_repository)
695
def open_workingtree(self, _unsupported=False,
696
recommend_upgrade=True, from_branch=None):
697
"""Open the workingtree object at this BzrDir if one is present.
699
:param recommend_upgrade: Optional keyword parameter, when True (the
700
default), emit through the ui module a recommendation that the user
701
upgrade the working tree when the workingtree being opened is old
702
(but still fully supported).
703
:param from_branch: override bzrdir branch (for lightweight checkouts)
705
raise NotImplementedError(self.open_workingtree)
707
def has_branch(self):
708
"""Tell if this bzrdir contains a branch.
710
Note: if you're going to open the branch, you should just go ahead
711
and try, and not ask permission first. (This method just opens the
712
branch and discards it, and that's somewhat expensive.)
717
except errors.NotBranchError:
720
def has_workingtree(self):
721
"""Tell if this bzrdir contains a working tree.
723
This will still raise an exception if the bzrdir has a workingtree that
724
is remote & inaccessible.
726
Note: if you're going to open the working tree, you should just go ahead
727
and try, and not ask permission first. (This method just opens the
728
workingtree and discards it, and that's somewhat expensive.)
731
self.open_workingtree(recommend_upgrade=False)
733
except errors.NoWorkingTree:
736
def _cloning_metadir(self):
737
"""Produce a metadir suitable for cloning with."""
738
result_format = self._format.__class__()
741
branch = self.open_branch()
742
source_repository = branch.repository
743
except errors.NotBranchError:
745
source_repository = self.open_repository()
746
except errors.NoRepositoryPresent:
747
source_repository = None
749
# XXX TODO: This isinstance is here because we have not implemented
750
# the fix recommended in bug # 103195 - to delegate this choice the
752
repo_format = source_repository._format
753
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
754
result_format.repository_format = repo_format
756
# TODO: Couldn't we just probe for the format in these cases,
757
# rather than opening the whole tree? It would be a little
758
# faster. mbp 20070401
759
tree = self.open_workingtree(recommend_upgrade=False)
760
except (errors.NoWorkingTree, errors.NotLocalUrl):
761
result_format.workingtree_format = None
763
result_format.workingtree_format = tree._format.__class__()
764
return result_format, source_repository
766
def cloning_metadir(self):
767
"""Produce a metadir suitable for cloning or sprouting with.
769
These operations may produce workingtrees (yes, even though they're
770
"cloning" something that doesn't have a tree), so a viable workingtree
771
format must be selected.
773
format, repository = self._cloning_metadir()
774
if format._workingtree_format is None:
775
if repository is None:
777
tree_format = repository._format._matchingbzrdir.workingtree_format
778
format.workingtree_format = tree_format.__class__()
781
def checkout_metadir(self):
782
return self.cloning_metadir()
784
def sprout(self, url, revision_id=None, force_new_repo=False,
785
recurse='down', possible_transports=None):
786
"""Create a copy of this bzrdir prepared for use as a new line of
789
If url's last component does not exist, it will be created.
791
Attributes related to the identity of the source branch like
792
branch nickname will be cleaned, a working tree is created
793
whether one existed before or not; and a local branch is always
796
if revision_id is not None, then the clone operation may tune
797
itself to download less data.
799
target_transport = get_transport(url, possible_transports)
800
target_transport.ensure_base()
801
cloning_format = self.cloning_metadir()
802
result = cloning_format.initialize_on_transport(target_transport)
804
source_branch = self.open_branch()
805
source_repository = source_branch.repository
806
except errors.NotBranchError:
809
source_repository = self.open_repository()
810
except errors.NoRepositoryPresent:
811
source_repository = None
816
result_repo = result.find_repository()
817
except errors.NoRepositoryPresent:
819
if source_repository is None and result_repo is not None:
821
elif source_repository is None and result_repo is None:
822
# no repo available, make a new one
823
result.create_repository()
824
elif source_repository is not None and result_repo is None:
825
# have source, and want to make a new target repo
826
result_repo = source_repository.sprout(result,
827
revision_id=revision_id)
829
# fetch needed content into target.
830
if source_repository is not None:
832
# source_repository.copy_content_into(result_repo,
833
# revision_id=revision_id)
834
# so we can override the copy method
835
result_repo.fetch(source_repository, revision_id=revision_id)
836
if source_branch is not None:
837
source_branch.sprout(result, revision_id=revision_id)
839
result.create_branch()
840
if isinstance(target_transport, LocalTransport) and (
841
result_repo is None or result_repo.make_working_trees()):
842
wt = result.create_workingtree()
845
if wt.path2id('') is None:
847
wt.set_root_id(self.open_workingtree.get_root_id())
848
except errors.NoWorkingTree:
854
if recurse == 'down':
856
basis = wt.basis_tree()
858
subtrees = basis.iter_references()
859
recurse_branch = wt.branch
860
elif source_branch is not None:
861
basis = source_branch.basis_tree()
863
subtrees = basis.iter_references()
864
recurse_branch = source_branch
869
for path, file_id in subtrees:
870
target = urlutils.join(url, urlutils.escape(path))
871
sublocation = source_branch.reference_parent(file_id, path)
872
sublocation.bzrdir.sprout(target,
873
basis.get_reference_revision(file_id, path),
874
force_new_repo=force_new_repo, recurse=recurse)
876
if basis is not None:
881
class BzrDirPreSplitOut(BzrDir):
882
"""A common class for the all-in-one formats."""
884
def __init__(self, _transport, _format):
885
"""See BzrDir.__init__."""
886
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
887
assert self._format._lock_class == lockable_files.TransportLock
888
assert self._format._lock_file_name == 'branch-lock'
889
self._control_files = lockable_files.LockableFiles(
890
self.get_branch_transport(None),
891
self._format._lock_file_name,
892
self._format._lock_class)
894
def break_lock(self):
895
"""Pre-splitout bzrdirs do not suffer from stale locks."""
896
raise NotImplementedError(self.break_lock)
898
def clone(self, url, revision_id=None, force_new_repo=False):
899
"""See BzrDir.clone()."""
900
from bzrlib.workingtree import WorkingTreeFormat2
902
result = self._format._initialize_for_clone(url)
903
self.open_repository().clone(result, revision_id=revision_id)
904
from_branch = self.open_branch()
905
from_branch.clone(result, revision_id=revision_id)
907
self.open_workingtree().clone(result)
908
except errors.NotLocalUrl:
909
# make a new one, this format always has to have one.
911
WorkingTreeFormat2().initialize(result)
912
except errors.NotLocalUrl:
913
# but we cannot do it for remote trees.
914
to_branch = result.open_branch()
915
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
918
def create_branch(self):
919
"""See BzrDir.create_branch."""
920
return self.open_branch()
922
def destroy_branch(self):
923
"""See BzrDir.destroy_branch."""
924
raise errors.UnsupportedOperation(self.destroy_branch, self)
926
def create_repository(self, shared=False):
927
"""See BzrDir.create_repository."""
929
raise errors.IncompatibleFormat('shared repository', self._format)
930
return self.open_repository()
932
def create_workingtree(self, revision_id=None, from_branch=None):
933
"""See BzrDir.create_workingtree."""
934
# this looks buggy but is not -really-
935
# because this format creates the workingtree when the bzrdir is
937
# clone and sprout will have set the revision_id
938
# and that will have set it for us, its only
939
# specific uses of create_workingtree in isolation
940
# that can do wonky stuff here, and that only
941
# happens for creating checkouts, which cannot be
942
# done on this format anyway. So - acceptable wart.
943
result = self.open_workingtree(recommend_upgrade=False)
944
if revision_id is not None:
945
if revision_id == _mod_revision.NULL_REVISION:
946
result.set_parent_ids([])
948
result.set_parent_ids([revision_id])
951
def destroy_workingtree(self):
952
"""See BzrDir.destroy_workingtree."""
953
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
955
def destroy_workingtree_metadata(self):
956
"""See BzrDir.destroy_workingtree_metadata."""
957
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
960
def get_branch_transport(self, branch_format):
961
"""See BzrDir.get_branch_transport()."""
962
if branch_format is None:
963
return self.transport
965
branch_format.get_format_string()
966
except NotImplementedError:
967
return self.transport
968
raise errors.IncompatibleFormat(branch_format, self._format)
970
def get_repository_transport(self, repository_format):
971
"""See BzrDir.get_repository_transport()."""
972
if repository_format is None:
973
return self.transport
975
repository_format.get_format_string()
976
except NotImplementedError:
977
return self.transport
978
raise errors.IncompatibleFormat(repository_format, self._format)
980
def get_workingtree_transport(self, workingtree_format):
981
"""See BzrDir.get_workingtree_transport()."""
982
if workingtree_format is None:
983
return self.transport
985
workingtree_format.get_format_string()
986
except NotImplementedError:
987
return self.transport
988
raise errors.IncompatibleFormat(workingtree_format, self._format)
990
def needs_format_conversion(self, format=None):
991
"""See BzrDir.needs_format_conversion()."""
992
# if the format is not the same as the system default,
993
# an upgrade is needed.
995
format = BzrDirFormat.get_default_format()
996
return not isinstance(self._format, format.__class__)
998
def open_branch(self, unsupported=False):
999
"""See BzrDir.open_branch."""
1000
from bzrlib.branch import BzrBranchFormat4
1001
format = BzrBranchFormat4()
1002
self._check_supported(format, unsupported)
1003
return format.open(self, _found=True)
1005
def sprout(self, url, revision_id=None, force_new_repo=False,
1006
possible_transports=None):
1007
"""See BzrDir.sprout()."""
1008
from bzrlib.workingtree import WorkingTreeFormat2
1009
self._make_tail(url)
1010
result = self._format._initialize_for_clone(url)
1012
self.open_repository().clone(result, revision_id=revision_id)
1013
except errors.NoRepositoryPresent:
1016
self.open_branch().sprout(result, revision_id=revision_id)
1017
except errors.NotBranchError:
1019
# we always want a working tree
1020
WorkingTreeFormat2().initialize(result)
1024
class BzrDir4(BzrDirPreSplitOut):
1025
"""A .bzr version 4 control object.
1027
This is a deprecated format and may be removed after sept 2006.
1030
def create_repository(self, shared=False):
1031
"""See BzrDir.create_repository."""
1032
return self._format.repository_format.initialize(self, shared)
1034
def needs_format_conversion(self, format=None):
1035
"""Format 4 dirs are always in need of conversion."""
1038
def open_repository(self):
1039
"""See BzrDir.open_repository."""
1040
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1041
return RepositoryFormat4().open(self, _found=True)
1044
class BzrDir5(BzrDirPreSplitOut):
1045
"""A .bzr version 5 control object.
1047
This is a deprecated format and may be removed after sept 2006.
1050
def open_repository(self):
1051
"""See BzrDir.open_repository."""
1052
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1053
return RepositoryFormat5().open(self, _found=True)
1055
def open_workingtree(self, _unsupported=False,
1056
recommend_upgrade=True):
1057
"""See BzrDir.create_workingtree."""
1058
from bzrlib.workingtree import WorkingTreeFormat2
1059
wt_format = WorkingTreeFormat2()
1060
# we don't warn here about upgrades; that ought to be handled for the
1062
return wt_format.open(self, _found=True)
1065
class BzrDir6(BzrDirPreSplitOut):
1066
"""A .bzr version 6 control object.
1068
This is a deprecated format and may be removed after sept 2006.
1071
def open_repository(self):
1072
"""See BzrDir.open_repository."""
1073
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1074
return RepositoryFormat6().open(self, _found=True)
1076
def open_workingtree(self, _unsupported=False,
1077
recommend_upgrade=True):
1078
"""See BzrDir.create_workingtree."""
1079
# we don't warn here about upgrades; that ought to be handled for the
1081
from bzrlib.workingtree import WorkingTreeFormat2
1082
return WorkingTreeFormat2().open(self, _found=True)
1085
class BzrDirMeta1(BzrDir):
1086
"""A .bzr meta version 1 control object.
1088
This is the first control object where the
1089
individual aspects are really split out: there are separate repository,
1090
workingtree and branch subdirectories and any subset of the three can be
1091
present within a BzrDir.
1094
def can_convert_format(self):
1095
"""See BzrDir.can_convert_format()."""
1098
def create_branch(self):
1099
"""See BzrDir.create_branch."""
1100
return self._format.get_branch_format().initialize(self)
1102
def destroy_branch(self):
1103
"""See BzrDir.create_branch."""
1104
self.transport.delete_tree('branch')
1106
def create_repository(self, shared=False):
1107
"""See BzrDir.create_repository."""
1108
return self._format.repository_format.initialize(self, shared)
1110
def create_workingtree(self, revision_id=None, from_branch=None):
1111
"""See BzrDir.create_workingtree."""
1112
return self._format.workingtree_format.initialize(
1113
self, revision_id, from_branch=from_branch)
1115
def destroy_workingtree(self):
1116
"""See BzrDir.destroy_workingtree."""
1117
wt = self.open_workingtree(recommend_upgrade=False)
1118
repository = wt.branch.repository
1119
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1120
wt.revert(old_tree=empty)
1121
self.destroy_workingtree_metadata()
1123
def destroy_workingtree_metadata(self):
1124
self.transport.delete_tree('checkout')
1126
def find_branch_format(self):
1127
"""Find the branch 'format' for this bzrdir.
1129
This might be a synthetic object for e.g. RemoteBranch and SVN.
1131
from bzrlib.branch import BranchFormat
1132
return BranchFormat.find_format(self)
1134
def _get_mkdir_mode(self):
1135
"""Figure out the mode to use when creating a bzrdir subdir."""
1136
temp_control = lockable_files.LockableFiles(self.transport, '',
1137
lockable_files.TransportLock)
1138
return temp_control._dir_mode
1140
def get_branch_reference(self):
1141
"""See BzrDir.get_branch_reference()."""
1142
from bzrlib.branch import BranchFormat
1143
format = BranchFormat.find_format(self)
1144
return format.get_reference(self)
1146
def get_branch_transport(self, branch_format):
1147
"""See BzrDir.get_branch_transport()."""
1148
if branch_format is None:
1149
return self.transport.clone('branch')
1151
branch_format.get_format_string()
1152
except NotImplementedError:
1153
raise errors.IncompatibleFormat(branch_format, self._format)
1155
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1156
except errors.FileExists:
1158
return self.transport.clone('branch')
1160
def get_repository_transport(self, repository_format):
1161
"""See BzrDir.get_repository_transport()."""
1162
if repository_format is None:
1163
return self.transport.clone('repository')
1165
repository_format.get_format_string()
1166
except NotImplementedError:
1167
raise errors.IncompatibleFormat(repository_format, self._format)
1169
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1170
except errors.FileExists:
1172
return self.transport.clone('repository')
1174
def get_workingtree_transport(self, workingtree_format):
1175
"""See BzrDir.get_workingtree_transport()."""
1176
if workingtree_format is None:
1177
return self.transport.clone('checkout')
1179
workingtree_format.get_format_string()
1180
except NotImplementedError:
1181
raise errors.IncompatibleFormat(workingtree_format, self._format)
1183
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1184
except errors.FileExists:
1186
return self.transport.clone('checkout')
1188
def needs_format_conversion(self, format=None):
1189
"""See BzrDir.needs_format_conversion()."""
1191
format = BzrDirFormat.get_default_format()
1192
if not isinstance(self._format, format.__class__):
1193
# it is not a meta dir format, conversion is needed.
1195
# we might want to push this down to the repository?
1197
if not isinstance(self.open_repository()._format,
1198
format.repository_format.__class__):
1199
# the repository needs an upgrade.
1201
except errors.NoRepositoryPresent:
1204
if not isinstance(self.open_branch()._format,
1205
format.get_branch_format().__class__):
1206
# the branch needs an upgrade.
1208
except errors.NotBranchError:
1211
my_wt = self.open_workingtree(recommend_upgrade=False)
1212
if not isinstance(my_wt._format,
1213
format.workingtree_format.__class__):
1214
# the workingtree needs an upgrade.
1216
except (errors.NoWorkingTree, errors.NotLocalUrl):
1220
def open_branch(self, unsupported=False):
1221
"""See BzrDir.open_branch."""
1222
format = self.find_branch_format()
1223
self._check_supported(format, unsupported)
1224
return format.open(self, _found=True)
1226
def open_repository(self, unsupported=False):
1227
"""See BzrDir.open_repository."""
1228
from bzrlib.repository import RepositoryFormat
1229
format = RepositoryFormat.find_format(self)
1230
self._check_supported(format, unsupported)
1231
return format.open(self, _found=True)
1233
def open_workingtree(self, unsupported=False,
1234
recommend_upgrade=True):
1235
"""See BzrDir.open_workingtree."""
1236
from bzrlib.workingtree import WorkingTreeFormat
1237
format = WorkingTreeFormat.find_format(self)
1238
self._check_supported(format, unsupported,
1240
basedir=self.root_transport.base)
1241
return format.open(self, _found=True)
1244
class BzrDirFormat(object):
1245
"""An encapsulation of the initialization and open routines for a format.
1247
Formats provide three things:
1248
* An initialization routine,
1252
Formats are placed in a dict by their format string for reference
1253
during bzrdir opening. These should be subclasses of BzrDirFormat
1256
Once a format is deprecated, just deprecate the initialize and open
1257
methods on the format class. Do not deprecate the object, as the
1258
object will be created every system load.
1261
_default_format = None
1262
"""The default format used for new .bzr dirs."""
1265
"""The known formats."""
1267
_control_formats = []
1268
"""The registered control formats - .bzr, ....
1270
This is a list of BzrDirFormat objects.
1273
_control_server_formats = []
1274
"""The registered control server formats, e.g. RemoteBzrDirs.
1276
This is a list of BzrDirFormat objects.
1279
_lock_file_name = 'branch-lock'
1281
# _lock_class must be set in subclasses to the lock type, typ.
1282
# TransportLock or LockDir
1285
def find_format(klass, transport, _server_formats=True):
1286
"""Return the format present at transport."""
1288
formats = klass._control_server_formats + klass._control_formats
1290
formats = klass._control_formats
1291
for format in formats:
1293
return format.probe_transport(transport)
1294
except errors.NotBranchError:
1295
# this format does not find a control dir here.
1297
raise errors.NotBranchError(path=transport.base)
1300
def probe_transport(klass, transport):
1301
"""Return the .bzrdir style format present in a directory."""
1303
format_string = transport.get(".bzr/branch-format").read()
1304
except errors.NoSuchFile:
1305
raise errors.NotBranchError(path=transport.base)
1308
return klass._formats[format_string]
1310
raise errors.UnknownFormatError(format=format_string)
1313
def get_default_format(klass):
1314
"""Return the current default format."""
1315
return klass._default_format
1317
def get_format_string(self):
1318
"""Return the ASCII format string that identifies this format."""
1319
raise NotImplementedError(self.get_format_string)
1321
def get_format_description(self):
1322
"""Return the short description for this format."""
1323
raise NotImplementedError(self.get_format_description)
1325
def get_converter(self, format=None):
1326
"""Return the converter to use to convert bzrdirs needing converts.
1328
This returns a bzrlib.bzrdir.Converter object.
1330
This should return the best upgrader to step this format towards the
1331
current default format. In the case of plugins we can/should provide
1332
some means for them to extend the range of returnable converters.
1334
:param format: Optional format to override the default format of the
1337
raise NotImplementedError(self.get_converter)
1339
def initialize(self, url, possible_transports=None):
1340
"""Create a bzr control dir at this url and return an opened copy.
1342
Subclasses should typically override initialize_on_transport
1343
instead of this method.
1345
return self.initialize_on_transport(get_transport(url,
1346
possible_transports))
1348
def initialize_on_transport(self, transport):
1349
"""Initialize a new bzrdir in the base directory of a Transport."""
1350
# Since we don't have a .bzr directory, inherit the
1351
# mode from the root directory
1352
temp_control = lockable_files.LockableFiles(transport,
1353
'', lockable_files.TransportLock)
1354
temp_control._transport.mkdir('.bzr',
1355
# FIXME: RBC 20060121 don't peek under
1357
mode=temp_control._dir_mode)
1358
file_mode = temp_control._file_mode
1360
mutter('created control directory in ' + transport.base)
1361
control = transport.clone('.bzr')
1362
utf8_files = [('README',
1363
"This is a Bazaar-NG control directory.\n"
1364
"Do not change any files in this directory.\n"),
1365
('branch-format', self.get_format_string()),
1367
# NB: no need to escape relative paths that are url safe.
1368
control_files = lockable_files.LockableFiles(control,
1369
self._lock_file_name, self._lock_class)
1370
control_files.create_lock()
1371
control_files.lock_write()
1373
for file, content in utf8_files:
1374
control_files.put_utf8(file, content)
1376
control_files.unlock()
1377
return self.open(transport, _found=True)
1379
def is_supported(self):
1380
"""Is this format supported?
1382
Supported formats must be initializable and openable.
1383
Unsupported formats may not support initialization or committing or
1384
some other features depending on the reason for not being supported.
1388
def same_model(self, target_format):
1389
return (self.repository_format.rich_root_data ==
1390
target_format.rich_root_data)
1393
def known_formats(klass):
1394
"""Return all the known formats.
1396
Concrete formats should override _known_formats.
1398
# There is double indirection here to make sure that control
1399
# formats used by more than one dir format will only be probed
1400
# once. This can otherwise be quite expensive for remote connections.
1402
for format in klass._control_formats:
1403
result.update(format._known_formats())
1407
def _known_formats(klass):
1408
"""Return the known format instances for this control format."""
1409
return set(klass._formats.values())
1411
def open(self, transport, _found=False):
1412
"""Return an instance of this format for the dir transport points at.
1414
_found is a private parameter, do not use it.
1417
found_format = BzrDirFormat.find_format(transport)
1418
if not isinstance(found_format, self.__class__):
1419
raise AssertionError("%s was asked to open %s, but it seems to need "
1421
% (self, transport, found_format))
1422
return self._open(transport)
1424
def _open(self, transport):
1425
"""Template method helper for opening BzrDirectories.
1427
This performs the actual open and any additional logic or parameter
1430
raise NotImplementedError(self._open)
1433
def register_format(klass, format):
1434
klass._formats[format.get_format_string()] = format
1437
def register_control_format(klass, format):
1438
"""Register a format that does not use '.bzr' for its control dir.
1440
TODO: This should be pulled up into a 'ControlDirFormat' base class
1441
which BzrDirFormat can inherit from, and renamed to register_format
1442
there. It has been done without that for now for simplicity of
1445
klass._control_formats.append(format)
1448
def register_control_server_format(klass, format):
1449
"""Register a control format for client-server environments.
1451
These formats will be tried before ones registered with
1452
register_control_format. This gives implementations that decide to the
1453
chance to grab it before anything looks at the contents of the format
1456
klass._control_server_formats.append(format)
1459
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1460
def set_default_format(klass, format):
1461
klass._set_default_format(format)
1464
def _set_default_format(klass, format):
1465
"""Set default format (for testing behavior of defaults only)"""
1466
klass._default_format = format
1470
return self.get_format_string().rstrip()
1473
def unregister_format(klass, format):
1474
assert klass._formats[format.get_format_string()] is format
1475
del klass._formats[format.get_format_string()]
1478
def unregister_control_format(klass, format):
1479
klass._control_formats.remove(format)
1482
class BzrDirFormat4(BzrDirFormat):
1483
"""Bzr dir format 4.
1485
This format is a combined format for working tree, branch and repository.
1487
- Format 1 working trees [always]
1488
- Format 4 branches [always]
1489
- Format 4 repositories [always]
1491
This format is deprecated: it indexes texts using a text it which is
1492
removed in format 5; write support for this format has been removed.
1495
_lock_class = lockable_files.TransportLock
1497
def get_format_string(self):
1498
"""See BzrDirFormat.get_format_string()."""
1499
return "Bazaar-NG branch, format 0.0.4\n"
1501
def get_format_description(self):
1502
"""See BzrDirFormat.get_format_description()."""
1503
return "All-in-one format 4"
1505
def get_converter(self, format=None):
1506
"""See BzrDirFormat.get_converter()."""
1507
# there is one and only one upgrade path here.
1508
return ConvertBzrDir4To5()
1510
def initialize_on_transport(self, transport):
1511
"""Format 4 branches cannot be created."""
1512
raise errors.UninitializableFormat(self)
1514
def is_supported(self):
1515
"""Format 4 is not supported.
1517
It is not supported because the model changed from 4 to 5 and the
1518
conversion logic is expensive - so doing it on the fly was not
1523
def _open(self, transport):
1524
"""See BzrDirFormat._open."""
1525
return BzrDir4(transport, self)
1527
def __return_repository_format(self):
1528
"""Circular import protection."""
1529
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1530
return RepositoryFormat4()
1531
repository_format = property(__return_repository_format)
1534
class BzrDirFormat5(BzrDirFormat):
1535
"""Bzr control format 5.
1537
This format is a combined format for working tree, branch and repository.
1539
- Format 2 working trees [always]
1540
- Format 4 branches [always]
1541
- Format 5 repositories [always]
1542
Unhashed stores in the repository.
1545
_lock_class = lockable_files.TransportLock
1547
def get_format_string(self):
1548
"""See BzrDirFormat.get_format_string()."""
1549
return "Bazaar-NG branch, format 5\n"
1551
def get_format_description(self):
1552
"""See BzrDirFormat.get_format_description()."""
1553
return "All-in-one format 5"
1555
def get_converter(self, format=None):
1556
"""See BzrDirFormat.get_converter()."""
1557
# there is one and only one upgrade path here.
1558
return ConvertBzrDir5To6()
1560
def _initialize_for_clone(self, url):
1561
return self.initialize_on_transport(get_transport(url), _cloning=True)
1563
def initialize_on_transport(self, transport, _cloning=False):
1564
"""Format 5 dirs always have working tree, branch and repository.
1566
Except when they are being cloned.
1568
from bzrlib.branch import BzrBranchFormat4
1569
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1570
from bzrlib.workingtree import WorkingTreeFormat2
1571
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1572
RepositoryFormat5().initialize(result, _internal=True)
1574
branch = BzrBranchFormat4().initialize(result)
1576
WorkingTreeFormat2().initialize(result)
1577
except errors.NotLocalUrl:
1578
# Even though we can't access the working tree, we need to
1579
# create its control files.
1580
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1583
def _open(self, transport):
1584
"""See BzrDirFormat._open."""
1585
return BzrDir5(transport, self)
1587
def __return_repository_format(self):
1588
"""Circular import protection."""
1589
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1590
return RepositoryFormat5()
1591
repository_format = property(__return_repository_format)
1594
class BzrDirFormat6(BzrDirFormat):
1595
"""Bzr control format 6.
1597
This format is a combined format for working tree, branch and repository.
1599
- Format 2 working trees [always]
1600
- Format 4 branches [always]
1601
- Format 6 repositories [always]
1604
_lock_class = lockable_files.TransportLock
1606
def get_format_string(self):
1607
"""See BzrDirFormat.get_format_string()."""
1608
return "Bazaar-NG branch, format 6\n"
1610
def get_format_description(self):
1611
"""See BzrDirFormat.get_format_description()."""
1612
return "All-in-one format 6"
1614
def get_converter(self, format=None):
1615
"""See BzrDirFormat.get_converter()."""
1616
# there is one and only one upgrade path here.
1617
return ConvertBzrDir6ToMeta()
1619
def _initialize_for_clone(self, url):
1620
return self.initialize_on_transport(get_transport(url), _cloning=True)
1622
def initialize_on_transport(self, transport, _cloning=False):
1623
"""Format 6 dirs always have working tree, branch and repository.
1625
Except when they are being cloned.
1627
from bzrlib.branch import BzrBranchFormat4
1628
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1629
from bzrlib.workingtree import WorkingTreeFormat2
1630
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1631
RepositoryFormat6().initialize(result, _internal=True)
1633
branch = BzrBranchFormat4().initialize(result)
1635
WorkingTreeFormat2().initialize(result)
1636
except errors.NotLocalUrl:
1637
# Even though we can't access the working tree, we need to
1638
# create its control files.
1639
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1642
def _open(self, transport):
1643
"""See BzrDirFormat._open."""
1644
return BzrDir6(transport, self)
1646
def __return_repository_format(self):
1647
"""Circular import protection."""
1648
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1649
return RepositoryFormat6()
1650
repository_format = property(__return_repository_format)
1653
class BzrDirMetaFormat1(BzrDirFormat):
1654
"""Bzr meta control format 1
1656
This is the first format with split out working tree, branch and repository
1659
- Format 3 working trees [optional]
1660
- Format 5 branches [optional]
1661
- Format 7 repositories [optional]
1664
_lock_class = lockdir.LockDir
1667
self._workingtree_format = None
1668
self._branch_format = None
1670
def __eq__(self, other):
1671
if other.__class__ is not self.__class__:
1673
if other.repository_format != self.repository_format:
1675
if other.workingtree_format != self.workingtree_format:
1679
def __ne__(self, other):
1680
return not self == other
1682
def get_branch_format(self):
1683
if self._branch_format is None:
1684
from bzrlib.branch import BranchFormat
1685
self._branch_format = BranchFormat.get_default_format()
1686
return self._branch_format
1688
def set_branch_format(self, format):
1689
self._branch_format = format
1691
def get_converter(self, format=None):
1692
"""See BzrDirFormat.get_converter()."""
1694
format = BzrDirFormat.get_default_format()
1695
if not isinstance(self, format.__class__):
1696
# converting away from metadir is not implemented
1697
raise NotImplementedError(self.get_converter)
1698
return ConvertMetaToMeta(format)
1700
def get_format_string(self):
1701
"""See BzrDirFormat.get_format_string()."""
1702
return "Bazaar-NG meta directory, format 1\n"
1704
def get_format_description(self):
1705
"""See BzrDirFormat.get_format_description()."""
1706
return "Meta directory format 1"
1708
def _open(self, transport):
1709
"""See BzrDirFormat._open."""
1710
return BzrDirMeta1(transport, self)
1712
def __return_repository_format(self):
1713
"""Circular import protection."""
1714
if getattr(self, '_repository_format', None):
1715
return self._repository_format
1716
from bzrlib.repository import RepositoryFormat
1717
return RepositoryFormat.get_default_format()
1719
def __set_repository_format(self, value):
1720
"""Allow changint the repository format for metadir formats."""
1721
self._repository_format = value
1723
repository_format = property(__return_repository_format, __set_repository_format)
1725
def __get_workingtree_format(self):
1726
if self._workingtree_format is None:
1727
from bzrlib.workingtree import WorkingTreeFormat
1728
self._workingtree_format = WorkingTreeFormat.get_default_format()
1729
return self._workingtree_format
1731
def __set_workingtree_format(self, wt_format):
1732
self._workingtree_format = wt_format
1734
workingtree_format = property(__get_workingtree_format,
1735
__set_workingtree_format)
1738
# Register bzr control format
1739
BzrDirFormat.register_control_format(BzrDirFormat)
1741
# Register bzr formats
1742
BzrDirFormat.register_format(BzrDirFormat4())
1743
BzrDirFormat.register_format(BzrDirFormat5())
1744
BzrDirFormat.register_format(BzrDirFormat6())
1745
__default_format = BzrDirMetaFormat1()
1746
BzrDirFormat.register_format(__default_format)
1747
BzrDirFormat._default_format = __default_format
1750
class Converter(object):
1751
"""Converts a disk format object from one format to another."""
1753
def convert(self, to_convert, pb):
1754
"""Perform the conversion of to_convert, giving feedback via pb.
1756
:param to_convert: The disk object to convert.
1757
:param pb: a progress bar to use for progress information.
1760
def step(self, message):
1761
"""Update the pb by a step."""
1763
self.pb.update(message, self.count, self.total)
1766
class ConvertBzrDir4To5(Converter):
1767
"""Converts format 4 bzr dirs to format 5."""
1770
super(ConvertBzrDir4To5, self).__init__()
1771
self.converted_revs = set()
1772
self.absent_revisions = set()
1776
def convert(self, to_convert, pb):
1777
"""See Converter.convert()."""
1778
self.bzrdir = to_convert
1780
self.pb.note('starting upgrade from format 4 to 5')
1781
if isinstance(self.bzrdir.transport, LocalTransport):
1782
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1783
self._convert_to_weaves()
1784
return BzrDir.open(self.bzrdir.root_transport.base)
1786
def _convert_to_weaves(self):
1787
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1790
stat = self.bzrdir.transport.stat('weaves')
1791
if not S_ISDIR(stat.st_mode):
1792
self.bzrdir.transport.delete('weaves')
1793
self.bzrdir.transport.mkdir('weaves')
1794
except errors.NoSuchFile:
1795
self.bzrdir.transport.mkdir('weaves')
1796
# deliberately not a WeaveFile as we want to build it up slowly.
1797
self.inv_weave = Weave('inventory')
1798
# holds in-memory weaves for all files
1799
self.text_weaves = {}
1800
self.bzrdir.transport.delete('branch-format')
1801
self.branch = self.bzrdir.open_branch()
1802
self._convert_working_inv()
1803
rev_history = self.branch.revision_history()
1804
# to_read is a stack holding the revisions we still need to process;
1805
# appending to it adds new highest-priority revisions
1806
self.known_revisions = set(rev_history)
1807
self.to_read = rev_history[-1:]
1809
rev_id = self.to_read.pop()
1810
if (rev_id not in self.revisions
1811
and rev_id not in self.absent_revisions):
1812
self._load_one_rev(rev_id)
1814
to_import = self._make_order()
1815
for i, rev_id in enumerate(to_import):
1816
self.pb.update('converting revision', i, len(to_import))
1817
self._convert_one_rev(rev_id)
1819
self._write_all_weaves()
1820
self._write_all_revs()
1821
self.pb.note('upgraded to weaves:')
1822
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1823
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1824
self.pb.note(' %6d texts', self.text_count)
1825
self._cleanup_spare_files_after_format4()
1826
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1828
def _cleanup_spare_files_after_format4(self):
1829
# FIXME working tree upgrade foo.
1830
for n in 'merged-patches', 'pending-merged-patches':
1832
## assert os.path.getsize(p) == 0
1833
self.bzrdir.transport.delete(n)
1834
except errors.NoSuchFile:
1836
self.bzrdir.transport.delete_tree('inventory-store')
1837
self.bzrdir.transport.delete_tree('text-store')
1839
def _convert_working_inv(self):
1840
inv = xml4.serializer_v4.read_inventory(
1841
self.branch.control_files.get('inventory'))
1842
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1843
# FIXME inventory is a working tree change.
1844
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1846
def _write_all_weaves(self):
1847
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1848
weave_transport = self.bzrdir.transport.clone('weaves')
1849
weaves = WeaveStore(weave_transport, prefixed=False)
1850
transaction = WriteTransaction()
1854
for file_id, file_weave in self.text_weaves.items():
1855
self.pb.update('writing weave', i, len(self.text_weaves))
1856
weaves._put_weave(file_id, file_weave, transaction)
1858
self.pb.update('inventory', 0, 1)
1859
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1860
self.pb.update('inventory', 1, 1)
1864
def _write_all_revs(self):
1865
"""Write all revisions out in new form."""
1866
self.bzrdir.transport.delete_tree('revision-store')
1867
self.bzrdir.transport.mkdir('revision-store')
1868
revision_transport = self.bzrdir.transport.clone('revision-store')
1870
_revision_store = TextRevisionStore(TextStore(revision_transport,
1874
transaction = WriteTransaction()
1875
for i, rev_id in enumerate(self.converted_revs):
1876
self.pb.update('write revision', i, len(self.converted_revs))
1877
_revision_store.add_revision(self.revisions[rev_id], transaction)
1881
def _load_one_rev(self, rev_id):
1882
"""Load a revision object into memory.
1884
Any parents not either loaded or abandoned get queued to be
1886
self.pb.update('loading revision',
1887
len(self.revisions),
1888
len(self.known_revisions))
1889
if not self.branch.repository.has_revision(rev_id):
1891
self.pb.note('revision {%s} not present in branch; '
1892
'will be converted as a ghost',
1894
self.absent_revisions.add(rev_id)
1896
rev = self.branch.repository._revision_store.get_revision(rev_id,
1897
self.branch.repository.get_transaction())
1898
for parent_id in rev.parent_ids:
1899
self.known_revisions.add(parent_id)
1900
self.to_read.append(parent_id)
1901
self.revisions[rev_id] = rev
1903
def _load_old_inventory(self, rev_id):
1904
assert rev_id not in self.converted_revs
1905
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1906
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1907
inv.revision_id = rev_id
1908
rev = self.revisions[rev_id]
1909
if rev.inventory_sha1:
1910
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1911
'inventory sha mismatch for {%s}' % rev_id
1914
def _load_updated_inventory(self, rev_id):
1915
assert rev_id in self.converted_revs
1916
inv_xml = self.inv_weave.get_text(rev_id)
1917
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1920
def _convert_one_rev(self, rev_id):
1921
"""Convert revision and all referenced objects to new format."""
1922
rev = self.revisions[rev_id]
1923
inv = self._load_old_inventory(rev_id)
1924
present_parents = [p for p in rev.parent_ids
1925
if p not in self.absent_revisions]
1926
self._convert_revision_contents(rev, inv, present_parents)
1927
self._store_new_inv(rev, inv, present_parents)
1928
self.converted_revs.add(rev_id)
1930
def _store_new_inv(self, rev, inv, present_parents):
1931
# the XML is now updated with text versions
1933
entries = inv.iter_entries()
1935
for path, ie in entries:
1936
assert getattr(ie, 'revision', None) is not None, \
1937
'no revision on {%s} in {%s}' % \
1938
(file_id, rev.revision_id)
1939
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1940
new_inv_sha1 = sha_string(new_inv_xml)
1941
self.inv_weave.add_lines(rev.revision_id,
1943
new_inv_xml.splitlines(True))
1944
rev.inventory_sha1 = new_inv_sha1
1946
def _convert_revision_contents(self, rev, inv, present_parents):
1947
"""Convert all the files within a revision.
1949
Also upgrade the inventory to refer to the text revision ids."""
1950
rev_id = rev.revision_id
1951
mutter('converting texts of revision {%s}',
1953
parent_invs = map(self._load_updated_inventory, present_parents)
1954
entries = inv.iter_entries()
1956
for path, ie in entries:
1957
self._convert_file_version(rev, ie, parent_invs)
1959
def _convert_file_version(self, rev, ie, parent_invs):
1960
"""Convert one version of one file.
1962
The file needs to be added into the weave if it is a merge
1963
of >=2 parents or if it's changed from its parent.
1965
file_id = ie.file_id
1966
rev_id = rev.revision_id
1967
w = self.text_weaves.get(file_id)
1970
self.text_weaves[file_id] = w
1971
text_changed = False
1972
parent_candiate_entries = ie.parent_candidates(parent_invs)
1973
for old_revision in parent_candiate_entries.keys():
1974
# if this fails, its a ghost ?
1975
assert old_revision in self.converted_revs, \
1976
"Revision {%s} not in converted_revs" % old_revision
1977
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1978
# XXX: Note that this is unordered - and this is tolerable because
1979
# the previous code was also unordered.
1980
previous_entries = dict((head, parent_candiate_entries[head]) for head
1982
self.snapshot_ie(previous_entries, ie, w, rev_id)
1984
assert getattr(ie, 'revision', None) is not None
1986
def get_parents(self, revision_ids):
1987
for revision_id in revision_ids:
1988
yield self.revisions[revision_id].parent_ids
1990
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1991
# TODO: convert this logic, which is ~= snapshot to
1992
# a call to:. This needs the path figured out. rather than a work_tree
1993
# a v4 revision_tree can be given, or something that looks enough like
1994
# one to give the file content to the entry if it needs it.
1995
# and we need something that looks like a weave store for snapshot to
1997
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1998
if len(previous_revisions) == 1:
1999
previous_ie = previous_revisions.values()[0]
2000
if ie._unchanged(previous_ie):
2001
ie.revision = previous_ie.revision
2004
text = self.branch.repository.weave_store.get(ie.text_id)
2005
file_lines = text.readlines()
2006
assert sha_strings(file_lines) == ie.text_sha1
2007
assert sum(map(len, file_lines)) == ie.text_size
2008
w.add_lines(rev_id, previous_revisions, file_lines)
2009
self.text_count += 1
2011
w.add_lines(rev_id, previous_revisions, [])
2012
ie.revision = rev_id
2014
def _make_order(self):
2015
"""Return a suitable order for importing revisions.
2017
The order must be such that an revision is imported after all
2018
its (present) parents.
2020
todo = set(self.revisions.keys())
2021
done = self.absent_revisions.copy()
2024
# scan through looking for a revision whose parents
2026
for rev_id in sorted(list(todo)):
2027
rev = self.revisions[rev_id]
2028
parent_ids = set(rev.parent_ids)
2029
if parent_ids.issubset(done):
2030
# can take this one now
2031
order.append(rev_id)
2037
class ConvertBzrDir5To6(Converter):
2038
"""Converts format 5 bzr dirs to format 6."""
2040
def convert(self, to_convert, pb):
2041
"""See Converter.convert()."""
2042
self.bzrdir = to_convert
2044
self.pb.note('starting upgrade from format 5 to 6')
2045
self._convert_to_prefixed()
2046
return BzrDir.open(self.bzrdir.root_transport.base)
2048
def _convert_to_prefixed(self):
2049
from bzrlib.store import TransportStore
2050
self.bzrdir.transport.delete('branch-format')
2051
for store_name in ["weaves", "revision-store"]:
2052
self.pb.note("adding prefixes to %s" % store_name)
2053
store_transport = self.bzrdir.transport.clone(store_name)
2054
store = TransportStore(store_transport, prefixed=True)
2055
for urlfilename in store_transport.list_dir('.'):
2056
filename = urlutils.unescape(urlfilename)
2057
if (filename.endswith(".weave") or
2058
filename.endswith(".gz") or
2059
filename.endswith(".sig")):
2060
file_id = os.path.splitext(filename)[0]
2063
prefix_dir = store.hash_prefix(file_id)
2064
# FIXME keep track of the dirs made RBC 20060121
2066
store_transport.move(filename, prefix_dir + '/' + filename)
2067
except errors.NoSuchFile: # catches missing dirs strangely enough
2068
store_transport.mkdir(prefix_dir)
2069
store_transport.move(filename, prefix_dir + '/' + filename)
2070
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2073
class ConvertBzrDir6ToMeta(Converter):
2074
"""Converts format 6 bzr dirs to metadirs."""
2076
def convert(self, to_convert, pb):
2077
"""See Converter.convert()."""
2078
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2079
from bzrlib.branch import BzrBranchFormat5
2080
self.bzrdir = to_convert
2083
self.total = 20 # the steps we know about
2084
self.garbage_inventories = []
2086
self.pb.note('starting upgrade from format 6 to metadir')
2087
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2088
# its faster to move specific files around than to open and use the apis...
2089
# first off, nuke ancestry.weave, it was never used.
2091
self.step('Removing ancestry.weave')
2092
self.bzrdir.transport.delete('ancestry.weave')
2093
except errors.NoSuchFile:
2095
# find out whats there
2096
self.step('Finding branch files')
2097
last_revision = self.bzrdir.open_branch().last_revision()
2098
bzrcontents = self.bzrdir.transport.list_dir('.')
2099
for name in bzrcontents:
2100
if name.startswith('basis-inventory.'):
2101
self.garbage_inventories.append(name)
2102
# create new directories for repository, working tree and branch
2103
self.dir_mode = self.bzrdir._control_files._dir_mode
2104
self.file_mode = self.bzrdir._control_files._file_mode
2105
repository_names = [('inventory.weave', True),
2106
('revision-store', True),
2108
self.step('Upgrading repository ')
2109
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2110
self.make_lock('repository')
2111
# we hard code the formats here because we are converting into
2112
# the meta format. The meta format upgrader can take this to a
2113
# future format within each component.
2114
self.put_format('repository', RepositoryFormat7())
2115
for entry in repository_names:
2116
self.move_entry('repository', entry)
2118
self.step('Upgrading branch ')
2119
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2120
self.make_lock('branch')
2121
self.put_format('branch', BzrBranchFormat5())
2122
branch_files = [('revision-history', True),
2123
('branch-name', True),
2125
for entry in branch_files:
2126
self.move_entry('branch', entry)
2128
checkout_files = [('pending-merges', True),
2129
('inventory', True),
2130
('stat-cache', False)]
2131
# If a mandatory checkout file is not present, the branch does not have
2132
# a functional checkout. Do not create a checkout in the converted
2134
for name, mandatory in checkout_files:
2135
if mandatory and name not in bzrcontents:
2136
has_checkout = False
2140
if not has_checkout:
2141
self.pb.note('No working tree.')
2142
# If some checkout files are there, we may as well get rid of them.
2143
for name, mandatory in checkout_files:
2144
if name in bzrcontents:
2145
self.bzrdir.transport.delete(name)
2147
from bzrlib.workingtree import WorkingTreeFormat3
2148
self.step('Upgrading working tree')
2149
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2150
self.make_lock('checkout')
2152
'checkout', WorkingTreeFormat3())
2153
self.bzrdir.transport.delete_multi(
2154
self.garbage_inventories, self.pb)
2155
for entry in checkout_files:
2156
self.move_entry('checkout', entry)
2157
if last_revision is not None:
2158
self.bzrdir._control_files.put_utf8(
2159
'checkout/last-revision', last_revision)
2160
self.bzrdir._control_files.put_utf8(
2161
'branch-format', BzrDirMetaFormat1().get_format_string())
2162
return BzrDir.open(self.bzrdir.root_transport.base)
2164
def make_lock(self, name):
2165
"""Make a lock for the new control dir name."""
2166
self.step('Make %s lock' % name)
2167
ld = lockdir.LockDir(self.bzrdir.transport,
2169
file_modebits=self.file_mode,
2170
dir_modebits=self.dir_mode)
2173
def move_entry(self, new_dir, entry):
2174
"""Move then entry name into new_dir."""
2176
mandatory = entry[1]
2177
self.step('Moving %s' % name)
2179
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2180
except errors.NoSuchFile:
2184
def put_format(self, dirname, format):
2185
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2188
class ConvertMetaToMeta(Converter):
2189
"""Converts the components of metadirs."""
2191
def __init__(self, target_format):
2192
"""Create a metadir to metadir converter.
2194
:param target_format: The final metadir format that is desired.
2196
self.target_format = target_format
2198
def convert(self, to_convert, pb):
2199
"""See Converter.convert()."""
2200
self.bzrdir = to_convert
2204
self.step('checking repository format')
2206
repo = self.bzrdir.open_repository()
2207
except errors.NoRepositoryPresent:
2210
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2211
from bzrlib.repository import CopyConverter
2212
self.pb.note('starting repository conversion')
2213
converter = CopyConverter(self.target_format.repository_format)
2214
converter.convert(repo, pb)
2216
branch = self.bzrdir.open_branch()
2217
except errors.NotBranchError:
2220
# TODO: conversions of Branch and Tree should be done by
2221
# InterXFormat lookups
2222
# Avoid circular imports
2223
from bzrlib import branch as _mod_branch
2224
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2225
self.target_format.get_branch_format().__class__ is
2226
_mod_branch.BzrBranchFormat6):
2227
branch_converter = _mod_branch.Converter5to6()
2228
branch_converter.convert(branch)
2230
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2231
except (errors.NoWorkingTree, errors.NotLocalUrl):
2234
# TODO: conversions of Branch and Tree should be done by
2235
# InterXFormat lookups
2236
if (isinstance(tree, workingtree.WorkingTree3) and
2237
not isinstance(tree, workingtree_4.WorkingTree4) and
2238
isinstance(self.target_format.workingtree_format,
2239
workingtree_4.WorkingTreeFormat4)):
2240
workingtree_4.Converter3to4().convert(tree)
2244
# This is not in remote.py because it's small, and needs to be registered.
2245
# Putting it in remote.py creates a circular import problem.
2246
# we can make it a lazy object if the control formats is turned into something
2248
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2249
"""Format representing bzrdirs accessed via a smart server"""
2251
def get_format_description(self):
2252
return 'bzr remote bzrdir'
2255
def probe_transport(klass, transport):
2256
"""Return a RemoteBzrDirFormat object if it looks possible."""
2258
client = transport.get_smart_client()
2259
except (NotImplementedError, AttributeError,
2260
errors.TransportNotPossible):
2261
# no smart server, so not a branch for this format type.
2262
raise errors.NotBranchError(path=transport.base)
2264
# Send a 'hello' request in protocol version one, and decline to
2265
# open it if the server doesn't support our required version (2) so
2266
# that the VFS-based transport will do it.
2267
request = client.get_request()
2268
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2269
server_version = smart_protocol.query_version()
2270
if server_version != 2:
2271
raise errors.NotBranchError(path=transport.base)
2274
def initialize_on_transport(self, transport):
2276
# hand off the request to the smart server
2277
shared_medium = transport.get_shared_medium()
2278
except errors.NoSmartMedium:
2279
# TODO: lookup the local format from a server hint.
2280
local_dir_format = BzrDirMetaFormat1()
2281
return local_dir_format.initialize_on_transport(transport)
2282
client = _SmartClient(shared_medium)
2283
path = client.remote_path_from_transport(transport)
2284
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2286
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2287
return remote.RemoteBzrDir(transport)
2289
def _open(self, transport):
2290
return remote.RemoteBzrDir(transport)
2292
def __eq__(self, other):
2293
if not isinstance(other, RemoteBzrDirFormat):
2295
return self.get_format_description() == other.get_format_description()
2298
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2301
class BzrDirFormatInfo(object):
2303
def __init__(self, native, deprecated, hidden, experimental):
2304
self.deprecated = deprecated
2305
self.native = native
2306
self.hidden = hidden
2307
self.experimental = experimental
2310
class BzrDirFormatRegistry(registry.Registry):
2311
"""Registry of user-selectable BzrDir subformats.
2313
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2314
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2317
def register_metadir(self, key,
2318
repository_format, help, native=True, deprecated=False,
2322
experimental=False):
2323
"""Register a metadir subformat.
2325
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2326
by the Repository format.
2328
:param repository_format: The fully-qualified repository format class
2330
:param branch_format: Fully-qualified branch format class name as
2332
:param tree_format: Fully-qualified tree format class name as
2335
# This should be expanded to support setting WorkingTree and Branch
2336
# formats, once BzrDirMetaFormat1 supports that.
2337
def _load(full_name):
2338
mod_name, factory_name = full_name.rsplit('.', 1)
2340
mod = __import__(mod_name, globals(), locals(),
2342
except ImportError, e:
2343
raise ImportError('failed to load %s: %s' % (full_name, e))
2345
factory = getattr(mod, factory_name)
2346
except AttributeError:
2347
raise AttributeError('no factory %s in module %r'
2352
bd = BzrDirMetaFormat1()
2353
if branch_format is not None:
2354
bd.set_branch_format(_load(branch_format))
2355
if tree_format is not None:
2356
bd.workingtree_format = _load(tree_format)
2357
if repository_format is not None:
2358
bd.repository_format = _load(repository_format)
2360
self.register(key, helper, help, native, deprecated, hidden,
2363
def register(self, key, factory, help, native=True, deprecated=False,
2364
hidden=False, experimental=False):
2365
"""Register a BzrDirFormat factory.
2367
The factory must be a callable that takes one parameter: the key.
2368
It must produce an instance of the BzrDirFormat when called.
2370
This function mainly exists to prevent the info object from being
2373
registry.Registry.register(self, key, factory, help,
2374
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2376
def register_lazy(self, key, module_name, member_name, help, native=True,
2377
deprecated=False, hidden=False, experimental=False):
2378
registry.Registry.register_lazy(self, key, module_name, member_name,
2379
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2381
def set_default(self, key):
2382
"""Set the 'default' key to be a clone of the supplied key.
2384
This method must be called once and only once.
2386
registry.Registry.register(self, 'default', self.get(key),
2387
self.get_help(key), info=self.get_info(key))
2389
def set_default_repository(self, key):
2390
"""Set the FormatRegistry default and Repository default.
2392
This is a transitional method while Repository.set_default_format
2395
if 'default' in self:
2396
self.remove('default')
2397
self.set_default(key)
2398
format = self.get('default')()
2399
assert isinstance(format, BzrDirMetaFormat1)
2401
def make_bzrdir(self, key):
2402
return self.get(key)()
2404
def help_topic(self, topic):
2405
output = textwrap.dedent("""\
2406
These formats can be used for creating branches, working trees, and
2410
default_realkey = None
2411
default_help = self.get_help('default')
2413
for key in self.keys():
2414
if key == 'default':
2416
help = self.get_help(key)
2417
if help == default_help:
2418
default_realkey = key
2420
help_pairs.append((key, help))
2422
def wrapped(key, help, info):
2424
help = '(native) ' + help
2425
return ':%s:\n%s\n\n' % (key,
2426
textwrap.fill(help, initial_indent=' ',
2427
subsequent_indent=' '))
2428
if default_realkey is not None:
2429
output += wrapped(default_realkey, '(default) %s' % default_help,
2430
self.get_info('default'))
2431
deprecated_pairs = []
2432
experimental_pairs = []
2433
for key, help in help_pairs:
2434
info = self.get_info(key)
2437
elif info.deprecated:
2438
deprecated_pairs.append((key, help))
2439
elif info.experimental:
2440
experimental_pairs.append((key, help))
2442
output += wrapped(key, help, info)
2443
if len(experimental_pairs) > 0:
2444
output += "Experimental formats are shown below.\n\n"
2445
for key, help in experimental_pairs:
2446
info = self.get_info(key)
2447
output += wrapped(key, help, info)
2448
if len(deprecated_pairs) > 0:
2449
output += "Deprecated formats are shown below.\n\n"
2450
for key, help in deprecated_pairs:
2451
info = self.get_info(key)
2452
output += wrapped(key, help, info)
2457
format_registry = BzrDirFormatRegistry()
2458
format_registry.register('weave', BzrDirFormat6,
2459
'Pre-0.8 format. Slower than knit and does not'
2460
' support checkouts or shared repositories.',
2462
format_registry.register_metadir('knit',
2463
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2464
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2465
branch_format='bzrlib.branch.BzrBranchFormat5',
2466
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2467
format_registry.register_metadir('metaweave',
2468
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2469
'Transitional format in 0.8. Slower than knit.',
2470
branch_format='bzrlib.branch.BzrBranchFormat5',
2471
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2473
format_registry.register_metadir('dirstate',
2474
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2475
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2476
'above when accessed over the network.',
2477
branch_format='bzrlib.branch.BzrBranchFormat5',
2478
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2479
# directly from workingtree_4 triggers a circular import.
2480
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2482
format_registry.register_metadir('dirstate-tags',
2483
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2484
help='New in 0.15: Fast local operations and improved scaling for '
2485
'network operations. Additionally adds support for tags.'
2486
' Incompatible with bzr < 0.15.',
2487
branch_format='bzrlib.branch.BzrBranchFormat6',
2488
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2490
format_registry.register_metadir('dirstate-with-subtree',
2491
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2492
help='New in 0.15: Fast local operations and improved scaling for '
2493
'network operations. Additionally adds support for versioning nested '
2494
'bzr branches. Incompatible with bzr < 0.15.',
2495
branch_format='bzrlib.branch.BzrBranchFormat6',
2496
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2499
format_registry.register_metadir('knitpack-experimental',
2500
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2501
help='New in 0.92: Pack-based format with data compatible with '
2502
'dirstate-tags format repositories. Interoperates with '
2503
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2504
'NOTE: This format is experimental. Before using it, please read '
2505
'http://doc.bazaar-vcs.org/latest/developers/knitpack.html.',
2506
branch_format='bzrlib.branch.BzrBranchFormat6',
2507
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2510
format_registry.register_metadir('knitpack-subtree-experimental',
2511
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2512
help='New in 0.92: Pack-based format with data compatible with '
2513
'dirstate-with-subtree format repositories. Interoperates with '
2514
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2515
'NOTE: This format is experimental. Before using it, please read '
2516
'http://doc.bazaar-vcs.org/latest/developers/knitpack.html.',
2517
branch_format='bzrlib.branch.BzrBranchFormat6',
2518
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2522
format_registry.set_default('dirstate-tags')