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):
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
389
for i in xrange(10000):
391
to_path = '.bzr.retired.%d' % i
392
self.root_transport.rename('.bzr', to_path)
393
note("renamed %s to %s"
394
% (self.root_transport.abspath('.bzr'), to_path))
396
except (errors.TransportError, IOError, errors.PathError):
398
raise errors.RetireFailed(self.root_transport.abspath('.bzr'))
400
def destroy_workingtree(self):
401
"""Destroy the working tree at this BzrDir.
403
Formats that do not support this may raise UnsupportedOperation.
405
raise NotImplementedError(self.destroy_workingtree)
407
def destroy_workingtree_metadata(self):
408
"""Destroy the control files for the working tree at this BzrDir.
410
The contents of working tree files are not affected.
411
Formats that do not support this may raise UnsupportedOperation.
413
raise NotImplementedError(self.destroy_workingtree_metadata)
415
def find_repository(self):
416
"""Find the repository that should be used.
418
This does not require a branch as we use it to find the repo for
419
new branches as well as to hook existing branches up to their
423
return self.open_repository()
424
except errors.NoRepositoryPresent:
426
next_transport = self.root_transport.clone('..')
428
# find the next containing bzrdir
430
found_bzrdir = BzrDir.open_containing_from_transport(
432
except errors.NotBranchError:
434
raise errors.NoRepositoryPresent(self)
435
# does it have a repository ?
437
repository = found_bzrdir.open_repository()
438
except errors.NoRepositoryPresent:
439
next_transport = found_bzrdir.root_transport.clone('..')
440
if (found_bzrdir.root_transport.base == next_transport.base):
441
# top of the file system
445
if ((found_bzrdir.root_transport.base ==
446
self.root_transport.base) or repository.is_shared()):
449
raise errors.NoRepositoryPresent(self)
450
raise errors.NoRepositoryPresent(self)
452
def get_branch_reference(self):
453
"""Return the referenced URL for the branch in this bzrdir.
455
:raises NotBranchError: If there is no Branch.
456
:return: The URL the branch in this bzrdir references if it is a
457
reference branch, or None for regular branches.
461
def get_branch_transport(self, branch_format):
462
"""Get the transport for use by branch format in this BzrDir.
464
Note that bzr dirs that do not support format strings will raise
465
IncompatibleFormat if the branch format they are given has
466
a format string, and vice versa.
468
If branch_format is None, the transport is returned with no
469
checking. If it is not None, then the returned transport is
470
guaranteed to point to an existing directory ready for use.
472
raise NotImplementedError(self.get_branch_transport)
474
def get_repository_transport(self, repository_format):
475
"""Get the transport for use by repository format in this BzrDir.
477
Note that bzr dirs that do not support format strings will raise
478
IncompatibleFormat if the repository format they are given has
479
a format string, and vice versa.
481
If repository_format is None, the transport is returned with no
482
checking. If it is not None, then the returned transport is
483
guaranteed to point to an existing directory ready for use.
485
raise NotImplementedError(self.get_repository_transport)
487
def get_workingtree_transport(self, tree_format):
488
"""Get the transport for use by workingtree format in this BzrDir.
490
Note that bzr dirs that do not support format strings will raise
491
IncompatibleFormat if the workingtree format they are given has a
492
format string, and vice versa.
494
If workingtree_format is None, the transport is returned with no
495
checking. If it is not None, then the returned transport is
496
guaranteed to point to an existing directory ready for use.
498
raise NotImplementedError(self.get_workingtree_transport)
500
def __init__(self, _transport, _format):
501
"""Initialize a Bzr control dir object.
503
Only really common logic should reside here, concrete classes should be
504
made with varying behaviours.
506
:param _format: the format that is creating this BzrDir instance.
507
:param _transport: the transport this dir is based at.
509
self._format = _format
510
self.transport = _transport.clone('.bzr')
511
self.root_transport = _transport
513
def is_control_filename(self, filename):
514
"""True if filename is the name of a path which is reserved for bzrdir's.
516
:param filename: A filename within the root transport of this bzrdir.
518
This is true IF and ONLY IF the filename is part of the namespace reserved
519
for bzr control dirs. Currently this is the '.bzr' directory in the root
520
of the root_transport. it is expected that plugins will need to extend
521
this in the future - for instance to make bzr talk with svn working
524
# this might be better on the BzrDirFormat class because it refers to
525
# all the possible bzrdir disk formats.
526
# This method is tested via the workingtree is_control_filename tests-
527
# it was extracted from WorkingTree.is_control_filename. If the method's
528
# contract is extended beyond the current trivial implementation, please
529
# add new tests for it to the appropriate place.
530
return filename == '.bzr' or filename.startswith('.bzr/')
532
def needs_format_conversion(self, format=None):
533
"""Return true if this bzrdir needs convert_format run on it.
535
For instance, if the repository format is out of date but the
536
branch and working tree are not, this should return True.
538
:param format: Optional parameter indicating a specific desired
539
format we plan to arrive at.
541
raise NotImplementedError(self.needs_format_conversion)
544
def open_unsupported(base):
545
"""Open a branch which is not supported."""
546
return BzrDir.open(base, _unsupported=True)
549
def open(base, _unsupported=False, possible_transports=None):
550
"""Open an existing bzrdir, rooted at 'base' (url).
552
:param _unsupported: a private parameter to the BzrDir class.
554
t = get_transport(base, possible_transports=possible_transports)
555
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
558
def open_from_transport(transport, _unsupported=False,
559
_server_formats=True):
560
"""Open a bzrdir within a particular directory.
562
:param transport: Transport containing the bzrdir.
563
:param _unsupported: private.
565
base = transport.base
567
def find_format(transport):
568
return transport, BzrDirFormat.find_format(
569
transport, _server_formats=_server_formats)
571
def redirected(transport, e, redirection_notice):
572
qualified_source = e.get_source_url()
573
relpath = transport.relpath(qualified_source)
574
if not e.target.endswith(relpath):
575
# Not redirected to a branch-format, not a branch
576
raise errors.NotBranchError(path=e.target)
577
target = e.target[:-len(relpath)]
578
note('%s is%s redirected to %s',
579
transport.base, e.permanently, target)
580
# Let's try with a new transport
581
# FIXME: If 'transport' has a qualifier, this should
582
# be applied again to the new transport *iff* the
583
# schemes used are the same. It's a bit tricky to
584
# verify, so I'll punt for now
586
#qualified_target = e.get_target_url()[:-len(relpath)]
587
#return get_transport(qualified_target)
588
return get_transport(target)
591
transport, format = do_catching_redirections(find_format,
594
except errors.TooManyRedirections:
595
raise errors.NotBranchError(base)
597
BzrDir._check_supported(format, _unsupported)
598
return format.open(transport, _found=True)
600
def open_branch(self, unsupported=False):
601
"""Open the branch object at this BzrDir if one is present.
603
If unsupported is True, then no longer supported branch formats can
606
TODO: static convenience version of this?
608
raise NotImplementedError(self.open_branch)
611
def open_containing(url, possible_transports=None):
612
"""Open an existing branch which contains url.
614
:param url: url to search from.
615
See open_containing_from_transport for more detail.
617
transport = get_transport(url, possible_transports)
618
return BzrDir.open_containing_from_transport(transport)
621
def open_containing_from_transport(a_transport):
622
"""Open an existing branch which contains a_transport.base.
624
This probes for a branch at a_transport, and searches upwards from there.
626
Basically we keep looking up until we find the control directory or
627
run into the root. If there isn't one, raises NotBranchError.
628
If there is one and it is either an unrecognised format or an unsupported
629
format, UnknownFormatError or UnsupportedFormatError are raised.
630
If there is one, it is returned, along with the unused portion of url.
632
:return: The BzrDir that contains the path, and a Unicode path
633
for the rest of the URL.
635
# this gets the normalised url back. I.e. '.' -> the full path.
636
url = a_transport.base
639
result = BzrDir.open_from_transport(a_transport)
640
return result, urlutils.unescape(a_transport.relpath(url))
641
except errors.NotBranchError, e:
644
new_t = a_transport.clone('..')
645
except errors.InvalidURLJoin:
646
# reached the root, whatever that may be
647
raise errors.NotBranchError(path=url)
648
if new_t.base == a_transport.base:
649
# reached the root, whatever that may be
650
raise errors.NotBranchError(path=url)
654
def open_containing_tree_or_branch(klass, location):
655
"""Return the branch and working tree contained by a location.
657
Returns (tree, branch, relpath).
658
If there is no tree at containing the location, tree will be None.
659
If there is no branch containing the location, an exception will be
661
relpath is the portion of the path that is contained by the branch.
663
bzrdir, relpath = klass.open_containing(location)
665
tree = bzrdir.open_workingtree()
666
except (errors.NoWorkingTree, errors.NotLocalUrl):
668
branch = bzrdir.open_branch()
671
return tree, branch, relpath
673
def open_repository(self, _unsupported=False):
674
"""Open the repository object at this BzrDir if one is present.
676
This will not follow the Branch object pointer - it's strictly a direct
677
open facility. Most client code should use open_branch().repository to
680
:param _unsupported: a private parameter, not part of the api.
681
TODO: static convenience version of this?
683
raise NotImplementedError(self.open_repository)
685
def open_workingtree(self, _unsupported=False,
686
recommend_upgrade=True):
687
"""Open the workingtree object at this BzrDir if one is present.
689
:param recommend_upgrade: Optional keyword parameter, when True (the
690
default), emit through the ui module a recommendation that the user
691
upgrade the working tree when the workingtree being opened is old
692
(but still fully supported).
694
raise NotImplementedError(self.open_workingtree)
696
def has_branch(self):
697
"""Tell if this bzrdir contains a branch.
699
Note: if you're going to open the branch, you should just go ahead
700
and try, and not ask permission first. (This method just opens the
701
branch and discards it, and that's somewhat expensive.)
706
except errors.NotBranchError:
709
def has_workingtree(self):
710
"""Tell if this bzrdir contains a working tree.
712
This will still raise an exception if the bzrdir has a workingtree that
713
is remote & inaccessible.
715
Note: if you're going to open the working tree, you should just go ahead
716
and try, and not ask permission first. (This method just opens the
717
workingtree and discards it, and that's somewhat expensive.)
720
self.open_workingtree(recommend_upgrade=False)
722
except errors.NoWorkingTree:
725
def _cloning_metadir(self):
726
"""Produce a metadir suitable for cloning with."""
727
result_format = self._format.__class__()
730
branch = self.open_branch()
731
source_repository = branch.repository
732
except errors.NotBranchError:
734
source_repository = self.open_repository()
735
except errors.NoRepositoryPresent:
736
source_repository = None
738
# XXX TODO: This isinstance is here because we have not implemented
739
# the fix recommended in bug # 103195 - to delegate this choice the
741
repo_format = source_repository._format
742
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
743
result_format.repository_format = repo_format
745
# TODO: Couldn't we just probe for the format in these cases,
746
# rather than opening the whole tree? It would be a little
747
# faster. mbp 20070401
748
tree = self.open_workingtree(recommend_upgrade=False)
749
except (errors.NoWorkingTree, errors.NotLocalUrl):
750
result_format.workingtree_format = None
752
result_format.workingtree_format = tree._format.__class__()
753
return result_format, source_repository
755
def cloning_metadir(self):
756
"""Produce a metadir suitable for cloning or sprouting with.
758
These operations may produce workingtrees (yes, even though they're
759
"cloning" something that doesn't have a tree), so a viable workingtree
760
format must be selected.
762
format, repository = self._cloning_metadir()
763
if format._workingtree_format is None:
764
if repository is None:
766
tree_format = repository._format._matchingbzrdir.workingtree_format
767
format.workingtree_format = tree_format.__class__()
770
def checkout_metadir(self):
771
return self.cloning_metadir()
773
def sprout(self, url, revision_id=None, force_new_repo=False,
774
recurse='down', possible_transports=None):
775
"""Create a copy of this bzrdir prepared for use as a new line of
778
If url's last component does not exist, it will be created.
780
Attributes related to the identity of the source branch like
781
branch nickname will be cleaned, a working tree is created
782
whether one existed before or not; and a local branch is always
785
if revision_id is not None, then the clone operation may tune
786
itself to download less data.
788
target_transport = get_transport(url, possible_transports)
789
target_transport.ensure_base()
790
cloning_format = self.cloning_metadir()
791
result = cloning_format.initialize_on_transport(target_transport)
793
source_branch = self.open_branch()
794
source_repository = source_branch.repository
795
except errors.NotBranchError:
798
source_repository = self.open_repository()
799
except errors.NoRepositoryPresent:
800
source_repository = None
805
result_repo = result.find_repository()
806
except errors.NoRepositoryPresent:
808
if source_repository is None and result_repo is not None:
810
elif source_repository is None and result_repo is None:
811
# no repo available, make a new one
812
result.create_repository()
813
elif source_repository is not None and result_repo is None:
814
# have source, and want to make a new target repo
815
result_repo = source_repository.sprout(result,
816
revision_id=revision_id)
818
# fetch needed content into target.
819
if source_repository is not None:
821
# source_repository.copy_content_into(result_repo,
822
# revision_id=revision_id)
823
# so we can override the copy method
824
result_repo.fetch(source_repository, revision_id=revision_id)
825
if source_branch is not None:
826
source_branch.sprout(result, revision_id=revision_id)
828
result.create_branch()
829
if isinstance(target_transport, LocalTransport) and (
830
result_repo is None or result_repo.make_working_trees()):
831
wt = result.create_workingtree()
834
if wt.path2id('') is None:
836
wt.set_root_id(self.open_workingtree.get_root_id())
837
except errors.NoWorkingTree:
843
if recurse == 'down':
845
basis = wt.basis_tree()
847
subtrees = basis.iter_references()
848
recurse_branch = wt.branch
849
elif source_branch is not None:
850
basis = source_branch.basis_tree()
852
subtrees = basis.iter_references()
853
recurse_branch = source_branch
858
for path, file_id in subtrees:
859
target = urlutils.join(url, urlutils.escape(path))
860
sublocation = source_branch.reference_parent(file_id, path)
861
sublocation.bzrdir.sprout(target,
862
basis.get_reference_revision(file_id, path),
863
force_new_repo=force_new_repo, recurse=recurse)
865
if basis is not None:
870
class BzrDirPreSplitOut(BzrDir):
871
"""A common class for the all-in-one formats."""
873
def __init__(self, _transport, _format):
874
"""See BzrDir.__init__."""
875
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
876
assert self._format._lock_class == lockable_files.TransportLock
877
assert self._format._lock_file_name == 'branch-lock'
878
self._control_files = lockable_files.LockableFiles(
879
self.get_branch_transport(None),
880
self._format._lock_file_name,
881
self._format._lock_class)
883
def break_lock(self):
884
"""Pre-splitout bzrdirs do not suffer from stale locks."""
885
raise NotImplementedError(self.break_lock)
887
def clone(self, url, revision_id=None, force_new_repo=False):
888
"""See BzrDir.clone()."""
889
from bzrlib.workingtree import WorkingTreeFormat2
891
result = self._format._initialize_for_clone(url)
892
self.open_repository().clone(result, revision_id=revision_id)
893
from_branch = self.open_branch()
894
from_branch.clone(result, revision_id=revision_id)
896
self.open_workingtree().clone(result)
897
except errors.NotLocalUrl:
898
# make a new one, this format always has to have one.
900
WorkingTreeFormat2().initialize(result)
901
except errors.NotLocalUrl:
902
# but we cannot do it for remote trees.
903
to_branch = result.open_branch()
904
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
907
def create_branch(self):
908
"""See BzrDir.create_branch."""
909
return self.open_branch()
911
def destroy_branch(self):
912
"""See BzrDir.destroy_branch."""
913
raise errors.UnsupportedOperation(self.destroy_branch, self)
915
def create_repository(self, shared=False):
916
"""See BzrDir.create_repository."""
918
raise errors.IncompatibleFormat('shared repository', self._format)
919
return self.open_repository()
921
def create_workingtree(self, revision_id=None):
922
"""See BzrDir.create_workingtree."""
923
# this looks buggy but is not -really-
924
# because this format creates the workingtree when the bzrdir is
926
# clone and sprout will have set the revision_id
927
# and that will have set it for us, its only
928
# specific uses of create_workingtree in isolation
929
# that can do wonky stuff here, and that only
930
# happens for creating checkouts, which cannot be
931
# done on this format anyway. So - acceptable wart.
932
result = self.open_workingtree(recommend_upgrade=False)
933
if revision_id is not None:
934
if revision_id == _mod_revision.NULL_REVISION:
935
result.set_parent_ids([])
937
result.set_parent_ids([revision_id])
940
def destroy_workingtree(self):
941
"""See BzrDir.destroy_workingtree."""
942
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
944
def destroy_workingtree_metadata(self):
945
"""See BzrDir.destroy_workingtree_metadata."""
946
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
949
def get_branch_transport(self, branch_format):
950
"""See BzrDir.get_branch_transport()."""
951
if branch_format is None:
952
return self.transport
954
branch_format.get_format_string()
955
except NotImplementedError:
956
return self.transport
957
raise errors.IncompatibleFormat(branch_format, self._format)
959
def get_repository_transport(self, repository_format):
960
"""See BzrDir.get_repository_transport()."""
961
if repository_format is None:
962
return self.transport
964
repository_format.get_format_string()
965
except NotImplementedError:
966
return self.transport
967
raise errors.IncompatibleFormat(repository_format, self._format)
969
def get_workingtree_transport(self, workingtree_format):
970
"""See BzrDir.get_workingtree_transport()."""
971
if workingtree_format is None:
972
return self.transport
974
workingtree_format.get_format_string()
975
except NotImplementedError:
976
return self.transport
977
raise errors.IncompatibleFormat(workingtree_format, self._format)
979
def needs_format_conversion(self, format=None):
980
"""See BzrDir.needs_format_conversion()."""
981
# if the format is not the same as the system default,
982
# an upgrade is needed.
984
format = BzrDirFormat.get_default_format()
985
return not isinstance(self._format, format.__class__)
987
def open_branch(self, unsupported=False):
988
"""See BzrDir.open_branch."""
989
from bzrlib.branch import BzrBranchFormat4
990
format = BzrBranchFormat4()
991
self._check_supported(format, unsupported)
992
return format.open(self, _found=True)
994
def sprout(self, url, revision_id=None, force_new_repo=False,
995
possible_transports=None):
996
"""See BzrDir.sprout()."""
997
from bzrlib.workingtree import WorkingTreeFormat2
999
result = self._format._initialize_for_clone(url)
1001
self.open_repository().clone(result, revision_id=revision_id)
1002
except errors.NoRepositoryPresent:
1005
self.open_branch().sprout(result, revision_id=revision_id)
1006
except errors.NotBranchError:
1008
# we always want a working tree
1009
WorkingTreeFormat2().initialize(result)
1013
class BzrDir4(BzrDirPreSplitOut):
1014
"""A .bzr version 4 control object.
1016
This is a deprecated format and may be removed after sept 2006.
1019
def create_repository(self, shared=False):
1020
"""See BzrDir.create_repository."""
1021
return self._format.repository_format.initialize(self, shared)
1023
def needs_format_conversion(self, format=None):
1024
"""Format 4 dirs are always in need of conversion."""
1027
def open_repository(self):
1028
"""See BzrDir.open_repository."""
1029
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1030
return RepositoryFormat4().open(self, _found=True)
1033
class BzrDir5(BzrDirPreSplitOut):
1034
"""A .bzr version 5 control object.
1036
This is a deprecated format and may be removed after sept 2006.
1039
def open_repository(self):
1040
"""See BzrDir.open_repository."""
1041
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1042
return RepositoryFormat5().open(self, _found=True)
1044
def open_workingtree(self, _unsupported=False,
1045
recommend_upgrade=True):
1046
"""See BzrDir.create_workingtree."""
1047
from bzrlib.workingtree import WorkingTreeFormat2
1048
wt_format = WorkingTreeFormat2()
1049
# we don't warn here about upgrades; that ought to be handled for the
1051
return wt_format.open(self, _found=True)
1054
class BzrDir6(BzrDirPreSplitOut):
1055
"""A .bzr version 6 control object.
1057
This is a deprecated format and may be removed after sept 2006.
1060
def open_repository(self):
1061
"""See BzrDir.open_repository."""
1062
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1063
return RepositoryFormat6().open(self, _found=True)
1065
def open_workingtree(self, _unsupported=False,
1066
recommend_upgrade=True):
1067
"""See BzrDir.create_workingtree."""
1068
# we don't warn here about upgrades; that ought to be handled for the
1070
from bzrlib.workingtree import WorkingTreeFormat2
1071
return WorkingTreeFormat2().open(self, _found=True)
1074
class BzrDirMeta1(BzrDir):
1075
"""A .bzr meta version 1 control object.
1077
This is the first control object where the
1078
individual aspects are really split out: there are separate repository,
1079
workingtree and branch subdirectories and any subset of the three can be
1080
present within a BzrDir.
1083
def can_convert_format(self):
1084
"""See BzrDir.can_convert_format()."""
1087
def create_branch(self):
1088
"""See BzrDir.create_branch."""
1089
return self._format.get_branch_format().initialize(self)
1091
def destroy_branch(self):
1092
"""See BzrDir.create_branch."""
1093
self.transport.delete_tree('branch')
1095
def create_repository(self, shared=False):
1096
"""See BzrDir.create_repository."""
1097
return self._format.repository_format.initialize(self, shared)
1099
def create_workingtree(self, revision_id=None):
1100
"""See BzrDir.create_workingtree."""
1101
from bzrlib.workingtree import WorkingTreeFormat
1102
return self._format.workingtree_format.initialize(self, revision_id)
1104
def destroy_workingtree(self):
1105
"""See BzrDir.destroy_workingtree."""
1106
wt = self.open_workingtree(recommend_upgrade=False)
1107
repository = wt.branch.repository
1108
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1109
wt.revert(old_tree=empty)
1110
self.destroy_workingtree_metadata()
1112
def destroy_workingtree_metadata(self):
1113
self.transport.delete_tree('checkout')
1115
def find_branch_format(self):
1116
"""Find the branch 'format' for this bzrdir.
1118
This might be a synthetic object for e.g. RemoteBranch and SVN.
1120
from bzrlib.branch import BranchFormat
1121
return BranchFormat.find_format(self)
1123
def _get_mkdir_mode(self):
1124
"""Figure out the mode to use when creating a bzrdir subdir."""
1125
temp_control = lockable_files.LockableFiles(self.transport, '',
1126
lockable_files.TransportLock)
1127
return temp_control._dir_mode
1129
def get_branch_reference(self):
1130
"""See BzrDir.get_branch_reference()."""
1131
from bzrlib.branch import BranchFormat
1132
format = BranchFormat.find_format(self)
1133
return format.get_reference(self)
1135
def get_branch_transport(self, branch_format):
1136
"""See BzrDir.get_branch_transport()."""
1137
if branch_format is None:
1138
return self.transport.clone('branch')
1140
branch_format.get_format_string()
1141
except NotImplementedError:
1142
raise errors.IncompatibleFormat(branch_format, self._format)
1144
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1145
except errors.FileExists:
1147
return self.transport.clone('branch')
1149
def get_repository_transport(self, repository_format):
1150
"""See BzrDir.get_repository_transport()."""
1151
if repository_format is None:
1152
return self.transport.clone('repository')
1154
repository_format.get_format_string()
1155
except NotImplementedError:
1156
raise errors.IncompatibleFormat(repository_format, self._format)
1158
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1159
except errors.FileExists:
1161
return self.transport.clone('repository')
1163
def get_workingtree_transport(self, workingtree_format):
1164
"""See BzrDir.get_workingtree_transport()."""
1165
if workingtree_format is None:
1166
return self.transport.clone('checkout')
1168
workingtree_format.get_format_string()
1169
except NotImplementedError:
1170
raise errors.IncompatibleFormat(workingtree_format, self._format)
1172
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1173
except errors.FileExists:
1175
return self.transport.clone('checkout')
1177
def needs_format_conversion(self, format=None):
1178
"""See BzrDir.needs_format_conversion()."""
1180
format = BzrDirFormat.get_default_format()
1181
if not isinstance(self._format, format.__class__):
1182
# it is not a meta dir format, conversion is needed.
1184
# we might want to push this down to the repository?
1186
if not isinstance(self.open_repository()._format,
1187
format.repository_format.__class__):
1188
# the repository needs an upgrade.
1190
except errors.NoRepositoryPresent:
1193
if not isinstance(self.open_branch()._format,
1194
format.get_branch_format().__class__):
1195
# the branch needs an upgrade.
1197
except errors.NotBranchError:
1200
my_wt = self.open_workingtree(recommend_upgrade=False)
1201
if not isinstance(my_wt._format,
1202
format.workingtree_format.__class__):
1203
# the workingtree needs an upgrade.
1205
except (errors.NoWorkingTree, errors.NotLocalUrl):
1209
def open_branch(self, unsupported=False):
1210
"""See BzrDir.open_branch."""
1211
format = self.find_branch_format()
1212
self._check_supported(format, unsupported)
1213
return format.open(self, _found=True)
1215
def open_repository(self, unsupported=False):
1216
"""See BzrDir.open_repository."""
1217
from bzrlib.repository import RepositoryFormat
1218
format = RepositoryFormat.find_format(self)
1219
self._check_supported(format, unsupported)
1220
return format.open(self, _found=True)
1222
def open_workingtree(self, unsupported=False,
1223
recommend_upgrade=True):
1224
"""See BzrDir.open_workingtree."""
1225
from bzrlib.workingtree import WorkingTreeFormat
1226
format = WorkingTreeFormat.find_format(self)
1227
self._check_supported(format, unsupported,
1229
basedir=self.root_transport.base)
1230
return format.open(self, _found=True)
1233
class BzrDirFormat(object):
1234
"""An encapsulation of the initialization and open routines for a format.
1236
Formats provide three things:
1237
* An initialization routine,
1241
Formats are placed in a dict by their format string for reference
1242
during bzrdir opening. These should be subclasses of BzrDirFormat
1245
Once a format is deprecated, just deprecate the initialize and open
1246
methods on the format class. Do not deprecate the object, as the
1247
object will be created every system load.
1250
_default_format = None
1251
"""The default format used for new .bzr dirs."""
1254
"""The known formats."""
1256
_control_formats = []
1257
"""The registered control formats - .bzr, ....
1259
This is a list of BzrDirFormat objects.
1262
_control_server_formats = []
1263
"""The registered control server formats, e.g. RemoteBzrDirs.
1265
This is a list of BzrDirFormat objects.
1268
_lock_file_name = 'branch-lock'
1270
# _lock_class must be set in subclasses to the lock type, typ.
1271
# TransportLock or LockDir
1274
def find_format(klass, transport, _server_formats=True):
1275
"""Return the format present at transport."""
1277
formats = klass._control_server_formats + klass._control_formats
1279
formats = klass._control_formats
1280
for format in formats:
1282
return format.probe_transport(transport)
1283
except errors.NotBranchError:
1284
# this format does not find a control dir here.
1286
raise errors.NotBranchError(path=transport.base)
1289
def probe_transport(klass, transport):
1290
"""Return the .bzrdir style format present in a directory."""
1292
format_string = transport.get(".bzr/branch-format").read()
1293
except errors.NoSuchFile:
1294
raise errors.NotBranchError(path=transport.base)
1297
return klass._formats[format_string]
1299
raise errors.UnknownFormatError(format=format_string)
1302
def get_default_format(klass):
1303
"""Return the current default format."""
1304
return klass._default_format
1306
def get_format_string(self):
1307
"""Return the ASCII format string that identifies this format."""
1308
raise NotImplementedError(self.get_format_string)
1310
def get_format_description(self):
1311
"""Return the short description for this format."""
1312
raise NotImplementedError(self.get_format_description)
1314
def get_converter(self, format=None):
1315
"""Return the converter to use to convert bzrdirs needing converts.
1317
This returns a bzrlib.bzrdir.Converter object.
1319
This should return the best upgrader to step this format towards the
1320
current default format. In the case of plugins we can/should provide
1321
some means for them to extend the range of returnable converters.
1323
:param format: Optional format to override the default format of the
1326
raise NotImplementedError(self.get_converter)
1328
def initialize(self, url, possible_transports=None):
1329
"""Create a bzr control dir at this url and return an opened copy.
1331
Subclasses should typically override initialize_on_transport
1332
instead of this method.
1334
return self.initialize_on_transport(get_transport(url,
1335
possible_transports))
1337
def initialize_on_transport(self, transport):
1338
"""Initialize a new bzrdir in the base directory of a Transport."""
1339
# Since we don't have a .bzr directory, inherit the
1340
# mode from the root directory
1341
temp_control = lockable_files.LockableFiles(transport,
1342
'', lockable_files.TransportLock)
1343
temp_control._transport.mkdir('.bzr',
1344
# FIXME: RBC 20060121 don't peek under
1346
mode=temp_control._dir_mode)
1347
file_mode = temp_control._file_mode
1349
mutter('created control directory in ' + transport.base)
1350
control = transport.clone('.bzr')
1351
utf8_files = [('README',
1352
"This is a Bazaar-NG control directory.\n"
1353
"Do not change any files in this directory.\n"),
1354
('branch-format', self.get_format_string()),
1356
# NB: no need to escape relative paths that are url safe.
1357
control_files = lockable_files.LockableFiles(control,
1358
self._lock_file_name, self._lock_class)
1359
control_files.create_lock()
1360
control_files.lock_write()
1362
for file, content in utf8_files:
1363
control_files.put_utf8(file, content)
1365
control_files.unlock()
1366
return self.open(transport, _found=True)
1368
def is_supported(self):
1369
"""Is this format supported?
1371
Supported formats must be initializable and openable.
1372
Unsupported formats may not support initialization or committing or
1373
some other features depending on the reason for not being supported.
1377
def same_model(self, target_format):
1378
return (self.repository_format.rich_root_data ==
1379
target_format.rich_root_data)
1382
def known_formats(klass):
1383
"""Return all the known formats.
1385
Concrete formats should override _known_formats.
1387
# There is double indirection here to make sure that control
1388
# formats used by more than one dir format will only be probed
1389
# once. This can otherwise be quite expensive for remote connections.
1391
for format in klass._control_formats:
1392
result.update(format._known_formats())
1396
def _known_formats(klass):
1397
"""Return the known format instances for this control format."""
1398
return set(klass._formats.values())
1400
def open(self, transport, _found=False):
1401
"""Return an instance of this format for the dir transport points at.
1403
_found is a private parameter, do not use it.
1406
found_format = BzrDirFormat.find_format(transport)
1407
if not isinstance(found_format, self.__class__):
1408
raise AssertionError("%s was asked to open %s, but it seems to need "
1410
% (self, transport, found_format))
1411
return self._open(transport)
1413
def _open(self, transport):
1414
"""Template method helper for opening BzrDirectories.
1416
This performs the actual open and any additional logic or parameter
1419
raise NotImplementedError(self._open)
1422
def register_format(klass, format):
1423
klass._formats[format.get_format_string()] = format
1426
def register_control_format(klass, format):
1427
"""Register a format that does not use '.bzr' for its control dir.
1429
TODO: This should be pulled up into a 'ControlDirFormat' base class
1430
which BzrDirFormat can inherit from, and renamed to register_format
1431
there. It has been done without that for now for simplicity of
1434
klass._control_formats.append(format)
1437
def register_control_server_format(klass, format):
1438
"""Register a control format for client-server environments.
1440
These formats will be tried before ones registered with
1441
register_control_format. This gives implementations that decide to the
1442
chance to grab it before anything looks at the contents of the format
1445
klass._control_server_formats.append(format)
1448
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1449
def set_default_format(klass, format):
1450
klass._set_default_format(format)
1453
def _set_default_format(klass, format):
1454
"""Set default format (for testing behavior of defaults only)"""
1455
klass._default_format = format
1459
return self.get_format_string().rstrip()
1462
def unregister_format(klass, format):
1463
assert klass._formats[format.get_format_string()] is format
1464
del klass._formats[format.get_format_string()]
1467
def unregister_control_format(klass, format):
1468
klass._control_formats.remove(format)
1471
class BzrDirFormat4(BzrDirFormat):
1472
"""Bzr dir format 4.
1474
This format is a combined format for working tree, branch and repository.
1476
- Format 1 working trees [always]
1477
- Format 4 branches [always]
1478
- Format 4 repositories [always]
1480
This format is deprecated: it indexes texts using a text it which is
1481
removed in format 5; write support for this format has been removed.
1484
_lock_class = lockable_files.TransportLock
1486
def get_format_string(self):
1487
"""See BzrDirFormat.get_format_string()."""
1488
return "Bazaar-NG branch, format 0.0.4\n"
1490
def get_format_description(self):
1491
"""See BzrDirFormat.get_format_description()."""
1492
return "All-in-one format 4"
1494
def get_converter(self, format=None):
1495
"""See BzrDirFormat.get_converter()."""
1496
# there is one and only one upgrade path here.
1497
return ConvertBzrDir4To5()
1499
def initialize_on_transport(self, transport):
1500
"""Format 4 branches cannot be created."""
1501
raise errors.UninitializableFormat(self)
1503
def is_supported(self):
1504
"""Format 4 is not supported.
1506
It is not supported because the model changed from 4 to 5 and the
1507
conversion logic is expensive - so doing it on the fly was not
1512
def _open(self, transport):
1513
"""See BzrDirFormat._open."""
1514
return BzrDir4(transport, self)
1516
def __return_repository_format(self):
1517
"""Circular import protection."""
1518
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1519
return RepositoryFormat4()
1520
repository_format = property(__return_repository_format)
1523
class BzrDirFormat5(BzrDirFormat):
1524
"""Bzr control format 5.
1526
This format is a combined format for working tree, branch and repository.
1528
- Format 2 working trees [always]
1529
- Format 4 branches [always]
1530
- Format 5 repositories [always]
1531
Unhashed stores in the repository.
1534
_lock_class = lockable_files.TransportLock
1536
def get_format_string(self):
1537
"""See BzrDirFormat.get_format_string()."""
1538
return "Bazaar-NG branch, format 5\n"
1540
def get_format_description(self):
1541
"""See BzrDirFormat.get_format_description()."""
1542
return "All-in-one format 5"
1544
def get_converter(self, format=None):
1545
"""See BzrDirFormat.get_converter()."""
1546
# there is one and only one upgrade path here.
1547
return ConvertBzrDir5To6()
1549
def _initialize_for_clone(self, url):
1550
return self.initialize_on_transport(get_transport(url), _cloning=True)
1552
def initialize_on_transport(self, transport, _cloning=False):
1553
"""Format 5 dirs always have working tree, branch and repository.
1555
Except when they are being cloned.
1557
from bzrlib.branch import BzrBranchFormat4
1558
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1559
from bzrlib.workingtree import WorkingTreeFormat2
1560
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1561
RepositoryFormat5().initialize(result, _internal=True)
1563
branch = BzrBranchFormat4().initialize(result)
1565
WorkingTreeFormat2().initialize(result)
1566
except errors.NotLocalUrl:
1567
# Even though we can't access the working tree, we need to
1568
# create its control files.
1569
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1572
def _open(self, transport):
1573
"""See BzrDirFormat._open."""
1574
return BzrDir5(transport, self)
1576
def __return_repository_format(self):
1577
"""Circular import protection."""
1578
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1579
return RepositoryFormat5()
1580
repository_format = property(__return_repository_format)
1583
class BzrDirFormat6(BzrDirFormat):
1584
"""Bzr control format 6.
1586
This format is a combined format for working tree, branch and repository.
1588
- Format 2 working trees [always]
1589
- Format 4 branches [always]
1590
- Format 6 repositories [always]
1593
_lock_class = lockable_files.TransportLock
1595
def get_format_string(self):
1596
"""See BzrDirFormat.get_format_string()."""
1597
return "Bazaar-NG branch, format 6\n"
1599
def get_format_description(self):
1600
"""See BzrDirFormat.get_format_description()."""
1601
return "All-in-one format 6"
1603
def get_converter(self, format=None):
1604
"""See BzrDirFormat.get_converter()."""
1605
# there is one and only one upgrade path here.
1606
return ConvertBzrDir6ToMeta()
1608
def _initialize_for_clone(self, url):
1609
return self.initialize_on_transport(get_transport(url), _cloning=True)
1611
def initialize_on_transport(self, transport, _cloning=False):
1612
"""Format 6 dirs always have working tree, branch and repository.
1614
Except when they are being cloned.
1616
from bzrlib.branch import BzrBranchFormat4
1617
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1618
from bzrlib.workingtree import WorkingTreeFormat2
1619
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1620
RepositoryFormat6().initialize(result, _internal=True)
1622
branch = BzrBranchFormat4().initialize(result)
1624
WorkingTreeFormat2().initialize(result)
1625
except errors.NotLocalUrl:
1626
# Even though we can't access the working tree, we need to
1627
# create its control files.
1628
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1631
def _open(self, transport):
1632
"""See BzrDirFormat._open."""
1633
return BzrDir6(transport, self)
1635
def __return_repository_format(self):
1636
"""Circular import protection."""
1637
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1638
return RepositoryFormat6()
1639
repository_format = property(__return_repository_format)
1642
class BzrDirMetaFormat1(BzrDirFormat):
1643
"""Bzr meta control format 1
1645
This is the first format with split out working tree, branch and repository
1648
- Format 3 working trees [optional]
1649
- Format 5 branches [optional]
1650
- Format 7 repositories [optional]
1653
_lock_class = lockdir.LockDir
1656
self._workingtree_format = None
1657
self._branch_format = None
1659
def __eq__(self, other):
1660
if other.__class__ is not self.__class__:
1662
if other.repository_format != self.repository_format:
1664
if other.workingtree_format != self.workingtree_format:
1668
def __ne__(self, other):
1669
return not self == other
1671
def get_branch_format(self):
1672
if self._branch_format is None:
1673
from bzrlib.branch import BranchFormat
1674
self._branch_format = BranchFormat.get_default_format()
1675
return self._branch_format
1677
def set_branch_format(self, format):
1678
self._branch_format = format
1680
def get_converter(self, format=None):
1681
"""See BzrDirFormat.get_converter()."""
1683
format = BzrDirFormat.get_default_format()
1684
if not isinstance(self, format.__class__):
1685
# converting away from metadir is not implemented
1686
raise NotImplementedError(self.get_converter)
1687
return ConvertMetaToMeta(format)
1689
def get_format_string(self):
1690
"""See BzrDirFormat.get_format_string()."""
1691
return "Bazaar-NG meta directory, format 1\n"
1693
def get_format_description(self):
1694
"""See BzrDirFormat.get_format_description()."""
1695
return "Meta directory format 1"
1697
def _open(self, transport):
1698
"""See BzrDirFormat._open."""
1699
return BzrDirMeta1(transport, self)
1701
def __return_repository_format(self):
1702
"""Circular import protection."""
1703
if getattr(self, '_repository_format', None):
1704
return self._repository_format
1705
from bzrlib.repository import RepositoryFormat
1706
return RepositoryFormat.get_default_format()
1708
def __set_repository_format(self, value):
1709
"""Allow changint the repository format for metadir formats."""
1710
self._repository_format = value
1712
repository_format = property(__return_repository_format, __set_repository_format)
1714
def __get_workingtree_format(self):
1715
if self._workingtree_format is None:
1716
from bzrlib.workingtree import WorkingTreeFormat
1717
self._workingtree_format = WorkingTreeFormat.get_default_format()
1718
return self._workingtree_format
1720
def __set_workingtree_format(self, wt_format):
1721
self._workingtree_format = wt_format
1723
workingtree_format = property(__get_workingtree_format,
1724
__set_workingtree_format)
1727
# Register bzr control format
1728
BzrDirFormat.register_control_format(BzrDirFormat)
1730
# Register bzr formats
1731
BzrDirFormat.register_format(BzrDirFormat4())
1732
BzrDirFormat.register_format(BzrDirFormat5())
1733
BzrDirFormat.register_format(BzrDirFormat6())
1734
__default_format = BzrDirMetaFormat1()
1735
BzrDirFormat.register_format(__default_format)
1736
BzrDirFormat._default_format = __default_format
1739
class Converter(object):
1740
"""Converts a disk format object from one format to another."""
1742
def convert(self, to_convert, pb):
1743
"""Perform the conversion of to_convert, giving feedback via pb.
1745
:param to_convert: The disk object to convert.
1746
:param pb: a progress bar to use for progress information.
1749
def step(self, message):
1750
"""Update the pb by a step."""
1752
self.pb.update(message, self.count, self.total)
1755
class ConvertBzrDir4To5(Converter):
1756
"""Converts format 4 bzr dirs to format 5."""
1759
super(ConvertBzrDir4To5, self).__init__()
1760
self.converted_revs = set()
1761
self.absent_revisions = set()
1765
def convert(self, to_convert, pb):
1766
"""See Converter.convert()."""
1767
self.bzrdir = to_convert
1769
self.pb.note('starting upgrade from format 4 to 5')
1770
if isinstance(self.bzrdir.transport, LocalTransport):
1771
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1772
self._convert_to_weaves()
1773
return BzrDir.open(self.bzrdir.root_transport.base)
1775
def _convert_to_weaves(self):
1776
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1779
stat = self.bzrdir.transport.stat('weaves')
1780
if not S_ISDIR(stat.st_mode):
1781
self.bzrdir.transport.delete('weaves')
1782
self.bzrdir.transport.mkdir('weaves')
1783
except errors.NoSuchFile:
1784
self.bzrdir.transport.mkdir('weaves')
1785
# deliberately not a WeaveFile as we want to build it up slowly.
1786
self.inv_weave = Weave('inventory')
1787
# holds in-memory weaves for all files
1788
self.text_weaves = {}
1789
self.bzrdir.transport.delete('branch-format')
1790
self.branch = self.bzrdir.open_branch()
1791
self._convert_working_inv()
1792
rev_history = self.branch.revision_history()
1793
# to_read is a stack holding the revisions we still need to process;
1794
# appending to it adds new highest-priority revisions
1795
self.known_revisions = set(rev_history)
1796
self.to_read = rev_history[-1:]
1798
rev_id = self.to_read.pop()
1799
if (rev_id not in self.revisions
1800
and rev_id not in self.absent_revisions):
1801
self._load_one_rev(rev_id)
1803
to_import = self._make_order()
1804
for i, rev_id in enumerate(to_import):
1805
self.pb.update('converting revision', i, len(to_import))
1806
self._convert_one_rev(rev_id)
1808
self._write_all_weaves()
1809
self._write_all_revs()
1810
self.pb.note('upgraded to weaves:')
1811
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1812
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1813
self.pb.note(' %6d texts', self.text_count)
1814
self._cleanup_spare_files_after_format4()
1815
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1817
def _cleanup_spare_files_after_format4(self):
1818
# FIXME working tree upgrade foo.
1819
for n in 'merged-patches', 'pending-merged-patches':
1821
## assert os.path.getsize(p) == 0
1822
self.bzrdir.transport.delete(n)
1823
except errors.NoSuchFile:
1825
self.bzrdir.transport.delete_tree('inventory-store')
1826
self.bzrdir.transport.delete_tree('text-store')
1828
def _convert_working_inv(self):
1829
inv = xml4.serializer_v4.read_inventory(
1830
self.branch.control_files.get('inventory'))
1831
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1832
# FIXME inventory is a working tree change.
1833
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1835
def _write_all_weaves(self):
1836
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1837
weave_transport = self.bzrdir.transport.clone('weaves')
1838
weaves = WeaveStore(weave_transport, prefixed=False)
1839
transaction = WriteTransaction()
1843
for file_id, file_weave in self.text_weaves.items():
1844
self.pb.update('writing weave', i, len(self.text_weaves))
1845
weaves._put_weave(file_id, file_weave, transaction)
1847
self.pb.update('inventory', 0, 1)
1848
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1849
self.pb.update('inventory', 1, 1)
1853
def _write_all_revs(self):
1854
"""Write all revisions out in new form."""
1855
self.bzrdir.transport.delete_tree('revision-store')
1856
self.bzrdir.transport.mkdir('revision-store')
1857
revision_transport = self.bzrdir.transport.clone('revision-store')
1859
_revision_store = TextRevisionStore(TextStore(revision_transport,
1863
transaction = WriteTransaction()
1864
for i, rev_id in enumerate(self.converted_revs):
1865
self.pb.update('write revision', i, len(self.converted_revs))
1866
_revision_store.add_revision(self.revisions[rev_id], transaction)
1870
def _load_one_rev(self, rev_id):
1871
"""Load a revision object into memory.
1873
Any parents not either loaded or abandoned get queued to be
1875
self.pb.update('loading revision',
1876
len(self.revisions),
1877
len(self.known_revisions))
1878
if not self.branch.repository.has_revision(rev_id):
1880
self.pb.note('revision {%s} not present in branch; '
1881
'will be converted as a ghost',
1883
self.absent_revisions.add(rev_id)
1885
rev = self.branch.repository._revision_store.get_revision(rev_id,
1886
self.branch.repository.get_transaction())
1887
for parent_id in rev.parent_ids:
1888
self.known_revisions.add(parent_id)
1889
self.to_read.append(parent_id)
1890
self.revisions[rev_id] = rev
1892
def _load_old_inventory(self, rev_id):
1893
assert rev_id not in self.converted_revs
1894
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1895
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1896
inv.revision_id = rev_id
1897
rev = self.revisions[rev_id]
1898
if rev.inventory_sha1:
1899
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1900
'inventory sha mismatch for {%s}' % rev_id
1903
def _load_updated_inventory(self, rev_id):
1904
assert rev_id in self.converted_revs
1905
inv_xml = self.inv_weave.get_text(rev_id)
1906
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1909
def _convert_one_rev(self, rev_id):
1910
"""Convert revision and all referenced objects to new format."""
1911
rev = self.revisions[rev_id]
1912
inv = self._load_old_inventory(rev_id)
1913
present_parents = [p for p in rev.parent_ids
1914
if p not in self.absent_revisions]
1915
self._convert_revision_contents(rev, inv, present_parents)
1916
self._store_new_inv(rev, inv, present_parents)
1917
self.converted_revs.add(rev_id)
1919
def _store_new_inv(self, rev, inv, present_parents):
1920
# the XML is now updated with text versions
1922
entries = inv.iter_entries()
1924
for path, ie in entries:
1925
assert getattr(ie, 'revision', None) is not None, \
1926
'no revision on {%s} in {%s}' % \
1927
(file_id, rev.revision_id)
1928
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1929
new_inv_sha1 = sha_string(new_inv_xml)
1930
self.inv_weave.add_lines(rev.revision_id,
1932
new_inv_xml.splitlines(True))
1933
rev.inventory_sha1 = new_inv_sha1
1935
def _convert_revision_contents(self, rev, inv, present_parents):
1936
"""Convert all the files within a revision.
1938
Also upgrade the inventory to refer to the text revision ids."""
1939
rev_id = rev.revision_id
1940
mutter('converting texts of revision {%s}',
1942
parent_invs = map(self._load_updated_inventory, present_parents)
1943
entries = inv.iter_entries()
1945
for path, ie in entries:
1946
self._convert_file_version(rev, ie, parent_invs)
1948
def _convert_file_version(self, rev, ie, parent_invs):
1949
"""Convert one version of one file.
1951
The file needs to be added into the weave if it is a merge
1952
of >=2 parents or if it's changed from its parent.
1954
file_id = ie.file_id
1955
rev_id = rev.revision_id
1956
w = self.text_weaves.get(file_id)
1959
self.text_weaves[file_id] = w
1960
text_changed = False
1961
parent_candiate_entries = ie.parent_candidates(parent_invs)
1962
for old_revision in parent_candiate_entries.keys():
1963
# if this fails, its a ghost ?
1964
assert old_revision in self.converted_revs, \
1965
"Revision {%s} not in converted_revs" % old_revision
1966
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1967
# XXX: Note that this is unordered - and this is tolerable because
1968
# the previous code was also unordered.
1969
previous_entries = dict((head, parent_candiate_entries[head]) for head
1971
self.snapshot_ie(previous_entries, ie, w, rev_id)
1973
assert getattr(ie, 'revision', None) is not None
1975
def get_parents(self, revision_ids):
1976
for revision_id in revision_ids:
1977
yield self.revisions[revision_id].parent_ids
1979
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1980
# TODO: convert this logic, which is ~= snapshot to
1981
# a call to:. This needs the path figured out. rather than a work_tree
1982
# a v4 revision_tree can be given, or something that looks enough like
1983
# one to give the file content to the entry if it needs it.
1984
# and we need something that looks like a weave store for snapshot to
1986
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1987
if len(previous_revisions) == 1:
1988
previous_ie = previous_revisions.values()[0]
1989
if ie._unchanged(previous_ie):
1990
ie.revision = previous_ie.revision
1993
text = self.branch.repository.weave_store.get(ie.text_id)
1994
file_lines = text.readlines()
1995
assert sha_strings(file_lines) == ie.text_sha1
1996
assert sum(map(len, file_lines)) == ie.text_size
1997
w.add_lines(rev_id, previous_revisions, file_lines)
1998
self.text_count += 1
2000
w.add_lines(rev_id, previous_revisions, [])
2001
ie.revision = rev_id
2003
def _make_order(self):
2004
"""Return a suitable order for importing revisions.
2006
The order must be such that an revision is imported after all
2007
its (present) parents.
2009
todo = set(self.revisions.keys())
2010
done = self.absent_revisions.copy()
2013
# scan through looking for a revision whose parents
2015
for rev_id in sorted(list(todo)):
2016
rev = self.revisions[rev_id]
2017
parent_ids = set(rev.parent_ids)
2018
if parent_ids.issubset(done):
2019
# can take this one now
2020
order.append(rev_id)
2026
class ConvertBzrDir5To6(Converter):
2027
"""Converts format 5 bzr dirs to format 6."""
2029
def convert(self, to_convert, pb):
2030
"""See Converter.convert()."""
2031
self.bzrdir = to_convert
2033
self.pb.note('starting upgrade from format 5 to 6')
2034
self._convert_to_prefixed()
2035
return BzrDir.open(self.bzrdir.root_transport.base)
2037
def _convert_to_prefixed(self):
2038
from bzrlib.store import TransportStore
2039
self.bzrdir.transport.delete('branch-format')
2040
for store_name in ["weaves", "revision-store"]:
2041
self.pb.note("adding prefixes to %s" % store_name)
2042
store_transport = self.bzrdir.transport.clone(store_name)
2043
store = TransportStore(store_transport, prefixed=True)
2044
for urlfilename in store_transport.list_dir('.'):
2045
filename = urlutils.unescape(urlfilename)
2046
if (filename.endswith(".weave") or
2047
filename.endswith(".gz") or
2048
filename.endswith(".sig")):
2049
file_id = os.path.splitext(filename)[0]
2052
prefix_dir = store.hash_prefix(file_id)
2053
# FIXME keep track of the dirs made RBC 20060121
2055
store_transport.move(filename, prefix_dir + '/' + filename)
2056
except errors.NoSuchFile: # catches missing dirs strangely enough
2057
store_transport.mkdir(prefix_dir)
2058
store_transport.move(filename, prefix_dir + '/' + filename)
2059
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2062
class ConvertBzrDir6ToMeta(Converter):
2063
"""Converts format 6 bzr dirs to metadirs."""
2065
def convert(self, to_convert, pb):
2066
"""See Converter.convert()."""
2067
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2068
from bzrlib.branch import BzrBranchFormat5
2069
self.bzrdir = to_convert
2072
self.total = 20 # the steps we know about
2073
self.garbage_inventories = []
2075
self.pb.note('starting upgrade from format 6 to metadir')
2076
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2077
# its faster to move specific files around than to open and use the apis...
2078
# first off, nuke ancestry.weave, it was never used.
2080
self.step('Removing ancestry.weave')
2081
self.bzrdir.transport.delete('ancestry.weave')
2082
except errors.NoSuchFile:
2084
# find out whats there
2085
self.step('Finding branch files')
2086
last_revision = self.bzrdir.open_branch().last_revision()
2087
bzrcontents = self.bzrdir.transport.list_dir('.')
2088
for name in bzrcontents:
2089
if name.startswith('basis-inventory.'):
2090
self.garbage_inventories.append(name)
2091
# create new directories for repository, working tree and branch
2092
self.dir_mode = self.bzrdir._control_files._dir_mode
2093
self.file_mode = self.bzrdir._control_files._file_mode
2094
repository_names = [('inventory.weave', True),
2095
('revision-store', True),
2097
self.step('Upgrading repository ')
2098
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2099
self.make_lock('repository')
2100
# we hard code the formats here because we are converting into
2101
# the meta format. The meta format upgrader can take this to a
2102
# future format within each component.
2103
self.put_format('repository', RepositoryFormat7())
2104
for entry in repository_names:
2105
self.move_entry('repository', entry)
2107
self.step('Upgrading branch ')
2108
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2109
self.make_lock('branch')
2110
self.put_format('branch', BzrBranchFormat5())
2111
branch_files = [('revision-history', True),
2112
('branch-name', True),
2114
for entry in branch_files:
2115
self.move_entry('branch', entry)
2117
checkout_files = [('pending-merges', True),
2118
('inventory', True),
2119
('stat-cache', False)]
2120
# If a mandatory checkout file is not present, the branch does not have
2121
# a functional checkout. Do not create a checkout in the converted
2123
for name, mandatory in checkout_files:
2124
if mandatory and name not in bzrcontents:
2125
has_checkout = False
2129
if not has_checkout:
2130
self.pb.note('No working tree.')
2131
# If some checkout files are there, we may as well get rid of them.
2132
for name, mandatory in checkout_files:
2133
if name in bzrcontents:
2134
self.bzrdir.transport.delete(name)
2136
from bzrlib.workingtree import WorkingTreeFormat3
2137
self.step('Upgrading working tree')
2138
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2139
self.make_lock('checkout')
2141
'checkout', WorkingTreeFormat3())
2142
self.bzrdir.transport.delete_multi(
2143
self.garbage_inventories, self.pb)
2144
for entry in checkout_files:
2145
self.move_entry('checkout', entry)
2146
if last_revision is not None:
2147
self.bzrdir._control_files.put_utf8(
2148
'checkout/last-revision', last_revision)
2149
self.bzrdir._control_files.put_utf8(
2150
'branch-format', BzrDirMetaFormat1().get_format_string())
2151
return BzrDir.open(self.bzrdir.root_transport.base)
2153
def make_lock(self, name):
2154
"""Make a lock for the new control dir name."""
2155
self.step('Make %s lock' % name)
2156
ld = lockdir.LockDir(self.bzrdir.transport,
2158
file_modebits=self.file_mode,
2159
dir_modebits=self.dir_mode)
2162
def move_entry(self, new_dir, entry):
2163
"""Move then entry name into new_dir."""
2165
mandatory = entry[1]
2166
self.step('Moving %s' % name)
2168
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2169
except errors.NoSuchFile:
2173
def put_format(self, dirname, format):
2174
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2177
class ConvertMetaToMeta(Converter):
2178
"""Converts the components of metadirs."""
2180
def __init__(self, target_format):
2181
"""Create a metadir to metadir converter.
2183
:param target_format: The final metadir format that is desired.
2185
self.target_format = target_format
2187
def convert(self, to_convert, pb):
2188
"""See Converter.convert()."""
2189
self.bzrdir = to_convert
2193
self.step('checking repository format')
2195
repo = self.bzrdir.open_repository()
2196
except errors.NoRepositoryPresent:
2199
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2200
from bzrlib.repository import CopyConverter
2201
self.pb.note('starting repository conversion')
2202
converter = CopyConverter(self.target_format.repository_format)
2203
converter.convert(repo, pb)
2205
branch = self.bzrdir.open_branch()
2206
except errors.NotBranchError:
2209
# TODO: conversions of Branch and Tree should be done by
2210
# InterXFormat lookups
2211
# Avoid circular imports
2212
from bzrlib import branch as _mod_branch
2213
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2214
self.target_format.get_branch_format().__class__ is
2215
_mod_branch.BzrBranchFormat6):
2216
branch_converter = _mod_branch.Converter5to6()
2217
branch_converter.convert(branch)
2219
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2220
except (errors.NoWorkingTree, errors.NotLocalUrl):
2223
# TODO: conversions of Branch and Tree should be done by
2224
# InterXFormat lookups
2225
if (isinstance(tree, workingtree.WorkingTree3) and
2226
not isinstance(tree, workingtree_4.WorkingTree4) and
2227
isinstance(self.target_format.workingtree_format,
2228
workingtree_4.WorkingTreeFormat4)):
2229
workingtree_4.Converter3to4().convert(tree)
2233
# This is not in remote.py because it's small, and needs to be registered.
2234
# Putting it in remote.py creates a circular import problem.
2235
# we can make it a lazy object if the control formats is turned into something
2237
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2238
"""Format representing bzrdirs accessed via a smart server"""
2240
def get_format_description(self):
2241
return 'bzr remote bzrdir'
2244
def probe_transport(klass, transport):
2245
"""Return a RemoteBzrDirFormat object if it looks possible."""
2247
client = transport.get_smart_client()
2248
except (NotImplementedError, AttributeError,
2249
errors.TransportNotPossible):
2250
# no smart server, so not a branch for this format type.
2251
raise errors.NotBranchError(path=transport.base)
2253
# Send a 'hello' request in protocol version one, and decline to
2254
# open it if the server doesn't support our required version (2) so
2255
# that the VFS-based transport will do it.
2256
request = client.get_request()
2257
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2258
server_version = smart_protocol.query_version()
2259
if server_version != 2:
2260
raise errors.NotBranchError(path=transport.base)
2263
def initialize_on_transport(self, transport):
2265
# hand off the request to the smart server
2266
shared_medium = transport.get_shared_medium()
2267
except errors.NoSmartMedium:
2268
# TODO: lookup the local format from a server hint.
2269
local_dir_format = BzrDirMetaFormat1()
2270
return local_dir_format.initialize_on_transport(transport)
2271
client = _SmartClient(shared_medium)
2272
path = client.remote_path_from_transport(transport)
2273
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2275
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2276
return remote.RemoteBzrDir(transport)
2278
def _open(self, transport):
2279
return remote.RemoteBzrDir(transport)
2281
def __eq__(self, other):
2282
if not isinstance(other, RemoteBzrDirFormat):
2284
return self.get_format_description() == other.get_format_description()
2287
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2290
class BzrDirFormatInfo(object):
2292
def __init__(self, native, deprecated, hidden):
2293
self.deprecated = deprecated
2294
self.native = native
2295
self.hidden = hidden
2298
class BzrDirFormatRegistry(registry.Registry):
2299
"""Registry of user-selectable BzrDir subformats.
2301
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2302
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2305
def register_metadir(self, key,
2306
repository_format, help, native=True, deprecated=False,
2310
"""Register a metadir subformat.
2312
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2313
by the Repository format.
2315
:param repository_format: The fully-qualified repository format class
2317
:param branch_format: Fully-qualified branch format class name as
2319
:param tree_format: Fully-qualified tree format class name as
2322
# This should be expanded to support setting WorkingTree and Branch
2323
# formats, once BzrDirMetaFormat1 supports that.
2324
def _load(full_name):
2325
mod_name, factory_name = full_name.rsplit('.', 1)
2327
mod = __import__(mod_name, globals(), locals(),
2329
except ImportError, e:
2330
raise ImportError('failed to load %s: %s' % (full_name, e))
2332
factory = getattr(mod, factory_name)
2333
except AttributeError:
2334
raise AttributeError('no factory %s in module %r'
2339
bd = BzrDirMetaFormat1()
2340
if branch_format is not None:
2341
bd.set_branch_format(_load(branch_format))
2342
if tree_format is not None:
2343
bd.workingtree_format = _load(tree_format)
2344
if repository_format is not None:
2345
bd.repository_format = _load(repository_format)
2347
self.register(key, helper, help, native, deprecated, hidden)
2349
def register(self, key, factory, help, native=True, deprecated=False,
2351
"""Register a BzrDirFormat factory.
2353
The factory must be a callable that takes one parameter: the key.
2354
It must produce an instance of the BzrDirFormat when called.
2356
This function mainly exists to prevent the info object from being
2359
registry.Registry.register(self, key, factory, help,
2360
BzrDirFormatInfo(native, deprecated, hidden))
2362
def register_lazy(self, key, module_name, member_name, help, native=True,
2363
deprecated=False, hidden=False):
2364
registry.Registry.register_lazy(self, key, module_name, member_name,
2365
help, BzrDirFormatInfo(native, deprecated, hidden))
2367
def set_default(self, key):
2368
"""Set the 'default' key to be a clone of the supplied key.
2370
This method must be called once and only once.
2372
registry.Registry.register(self, 'default', self.get(key),
2373
self.get_help(key), info=self.get_info(key))
2375
def set_default_repository(self, key):
2376
"""Set the FormatRegistry default and Repository default.
2378
This is a transitional method while Repository.set_default_format
2381
if 'default' in self:
2382
self.remove('default')
2383
self.set_default(key)
2384
format = self.get('default')()
2385
assert isinstance(format, BzrDirMetaFormat1)
2387
def make_bzrdir(self, key):
2388
return self.get(key)()
2390
def help_topic(self, topic):
2391
output = textwrap.dedent("""\
2392
These formats can be used for creating branches, working trees, and
2396
default_realkey = None
2397
default_help = self.get_help('default')
2399
for key in self.keys():
2400
if key == 'default':
2402
help = self.get_help(key)
2403
if help == default_help:
2404
default_realkey = key
2406
help_pairs.append((key, help))
2408
def wrapped(key, help, info):
2410
help = '(native) ' + help
2411
return ':%s:\n%s\n\n' % (key,
2412
textwrap.fill(help, initial_indent=' ',
2413
subsequent_indent=' '))
2414
if default_realkey is not None:
2415
output += wrapped(default_realkey, '(default) %s' % default_help,
2416
self.get_info('default'))
2417
deprecated_pairs = []
2418
for key, help in help_pairs:
2419
info = self.get_info(key)
2422
elif info.deprecated:
2423
deprecated_pairs.append((key, help))
2425
output += wrapped(key, help, info)
2426
if len(deprecated_pairs) > 0:
2427
output += "Deprecated formats are shown below.\n\n"
2428
for key, help in deprecated_pairs:
2429
info = self.get_info(key)
2430
output += wrapped(key, help, info)
2435
format_registry = BzrDirFormatRegistry()
2436
format_registry.register('weave', BzrDirFormat6,
2437
'Pre-0.8 format. Slower than knit and does not'
2438
' support checkouts or shared repositories.',
2440
format_registry.register_metadir('knit',
2441
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2442
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2443
branch_format='bzrlib.branch.BzrBranchFormat5',
2444
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2445
format_registry.register_metadir('metaweave',
2446
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2447
'Transitional format in 0.8. Slower than knit.',
2448
branch_format='bzrlib.branch.BzrBranchFormat5',
2449
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2451
format_registry.register_metadir('dirstate',
2452
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2453
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2454
'above when accessed over the network.',
2455
branch_format='bzrlib.branch.BzrBranchFormat5',
2456
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2457
# directly from workingtree_4 triggers a circular import.
2458
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2460
format_registry.register_metadir('dirstate-tags',
2461
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2462
help='New in 0.15: Fast local operations and improved scaling for '
2463
'network operations. Additionally adds support for tags.'
2464
' Incompatible with bzr < 0.15.',
2465
branch_format='bzrlib.branch.BzrBranchFormat6',
2466
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2468
format_registry.register_metadir('dirstate-with-subtree',
2469
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2470
help='New in 0.15: Fast local operations and improved scaling for '
2471
'network operations. Additionally adds support for versioning nested '
2472
'bzr branches. Incompatible with bzr < 0.15.',
2473
branch_format='bzrlib.branch.BzrBranchFormat6',
2474
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2477
format_registry.set_default('dirstate-tags')