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
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
36
from stat import S_ISDIR
38
from warnings import warn
48
revision as _mod_revision,
58
from bzrlib.osutils import (
62
from bzrlib.smart.client import _SmartClient
63
from bzrlib.smart import protocol
64
from bzrlib.store.revision.text import TextRevisionStore
65
from bzrlib.store.text import TextStore
66
from bzrlib.store.versioned import WeaveStore
67
from bzrlib.transactions import WriteTransaction
68
from bzrlib.transport import (
69
do_catching_redirections,
72
from bzrlib.weave import Weave
75
from bzrlib.trace import (
79
from bzrlib.transport.local import LocalTransport
80
from bzrlib.symbol_versioning import (
88
"""A .bzr control diretory.
90
BzrDir instances let you create or open any of the things that can be
91
found within .bzr - checkouts, branches and repositories.
94
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
96
a transport connected to the directory this bzr was opened from
97
(i.e. the parent directory holding the .bzr directory).
100
def break_lock(self):
101
"""Invoke break_lock on the first object in the bzrdir.
103
If there is a tree, the tree is opened and break_lock() called.
104
Otherwise, branch is tried, and finally repository.
106
# XXX: This seems more like a UI function than something that really
107
# belongs in this class.
109
thing_to_unlock = self.open_workingtree()
110
except (errors.NotLocalUrl, errors.NoWorkingTree):
112
thing_to_unlock = self.open_branch()
113
except errors.NotBranchError:
115
thing_to_unlock = self.open_repository()
116
except errors.NoRepositoryPresent:
118
thing_to_unlock.break_lock()
120
def can_convert_format(self):
121
"""Return true if this bzrdir is one whose format we can convert from."""
124
def check_conversion_target(self, target_format):
125
target_repo_format = target_format.repository_format
126
source_repo_format = self._format.repository_format
127
source_repo_format.check_conversion_target(target_repo_format)
130
def _check_supported(format, allow_unsupported,
131
recommend_upgrade=True,
133
"""Give an error or warning on old formats.
135
:param format: may be any kind of format - workingtree, branch,
138
:param allow_unsupported: If true, allow opening
139
formats that are strongly deprecated, and which may
140
have limited functionality.
142
:param recommend_upgrade: If true (default), warn
143
the user through the ui object that they may wish
144
to upgrade the object.
146
# TODO: perhaps move this into a base Format class; it's not BzrDir
147
# specific. mbp 20070323
148
if not allow_unsupported and not format.is_supported():
149
# see open_downlevel to open legacy branches.
150
raise errors.UnsupportedFormatError(format=format)
151
if recommend_upgrade \
152
and getattr(format, 'upgrade_recommended', False):
153
ui.ui_factory.recommend_upgrade(
154
format.get_format_description(),
157
def clone(self, url, revision_id=None, force_new_repo=False):
158
"""Clone this bzrdir and its contents to url verbatim.
160
If url's last component does not exist, it will be created.
162
if revision_id is not None, then the clone operation may tune
163
itself to download less data.
164
:param force_new_repo: Do not use a shared repository for the target
165
even if one is available.
167
return self.clone_on_transport(get_transport(url),
168
revision_id=revision_id,
169
force_new_repo=force_new_repo)
171
def clone_on_transport(self, transport, revision_id=None,
172
force_new_repo=False):
173
"""Clone this bzrdir and its contents to transport verbatim.
175
If the target directory does not exist, it will be created.
177
if revision_id is not None, then the clone operation may tune
178
itself to download less data.
179
:param force_new_repo: Do not use a shared repository for the target
180
even if one is available.
182
transport.ensure_base()
183
result = self._format.initialize_on_transport(transport)
185
local_repo = self.find_repository()
186
except errors.NoRepositoryPresent:
189
# may need to copy content in
191
result_repo = local_repo.clone(
193
revision_id=revision_id)
194
result_repo.set_make_working_trees(local_repo.make_working_trees())
197
result_repo = result.find_repository()
198
# fetch content this dir needs.
199
result_repo.fetch(local_repo, revision_id=revision_id)
200
except errors.NoRepositoryPresent:
201
# needed to make one anyway.
202
result_repo = local_repo.clone(
204
revision_id=revision_id)
205
result_repo.set_make_working_trees(local_repo.make_working_trees())
206
# 1 if there is a branch present
207
# make sure its content is available in the target repository
210
self.open_branch().clone(result, revision_id=revision_id)
211
except errors.NotBranchError:
214
result_repo = result.find_repository()
215
except errors.NoRepositoryPresent:
217
if result_repo is None or result_repo.make_working_trees():
219
self.open_workingtree().clone(result)
220
except (errors.NoWorkingTree, errors.NotLocalUrl):
224
# TODO: This should be given a Transport, and should chdir up; otherwise
225
# this will open a new connection.
226
def _make_tail(self, url):
227
t = get_transport(url)
231
def create(cls, base, format=None, possible_transports=None):
232
"""Create a new BzrDir at the url 'base'.
234
:param format: If supplied, the format of branch to create. If not
235
supplied, the default is used.
236
:param possible_transports: If supplied, a list of transports that
237
can be reused to share a remote connection.
239
if cls is not BzrDir:
240
raise AssertionError("BzrDir.create always creates the default"
241
" format, not one of %r" % cls)
242
t = get_transport(base, possible_transports)
245
format = BzrDirFormat.get_default_format()
246
return format.initialize_on_transport(t)
248
def create_branch(self):
249
"""Create a branch in this BzrDir.
251
The bzrdir's format will control what branch format is created.
252
For more control see BranchFormatXX.create(a_bzrdir).
254
raise NotImplementedError(self.create_branch)
256
def destroy_branch(self):
257
"""Destroy the branch in this BzrDir"""
258
raise NotImplementedError(self.destroy_branch)
261
def create_branch_and_repo(base, force_new_repo=False, format=None):
262
"""Create a new BzrDir, Branch and Repository at the url 'base'.
264
This will use the current default BzrDirFormat unless one is
265
specified, and use whatever
266
repository format that that uses via bzrdir.create_branch and
267
create_repository. If a shared repository is available that is used
270
The created Branch object is returned.
272
:param base: The URL to create the branch at.
273
:param force_new_repo: If True a new repository is always created.
274
:param format: If supplied, the format of branch to create. If not
275
supplied, the default is used.
277
bzrdir = BzrDir.create(base, format)
278
bzrdir._find_or_create_repository(force_new_repo)
279
return bzrdir.create_branch()
281
def _find_or_create_repository(self, force_new_repo):
282
"""Create a new repository if needed, returning the repository."""
284
return self.create_repository()
286
return self.find_repository()
287
except errors.NoRepositoryPresent:
288
return self.create_repository()
291
def create_branch_convenience(base, force_new_repo=False,
292
force_new_tree=None, format=None,
293
possible_transports=None):
294
"""Create a new BzrDir, Branch and Repository at the url 'base'.
296
This is a convenience function - it will use an existing repository
297
if possible, can be told explicitly whether to create a working tree or
300
This will use the current default BzrDirFormat unless one is
301
specified, and use whatever
302
repository format that that uses via bzrdir.create_branch and
303
create_repository. If a shared repository is available that is used
304
preferentially. Whatever repository is used, its tree creation policy
307
The created Branch object is returned.
308
If a working tree cannot be made due to base not being a file:// url,
309
no error is raised unless force_new_tree is True, in which case no
310
data is created on disk and NotLocalUrl is raised.
312
:param base: The URL to create the branch at.
313
:param force_new_repo: If True a new repository is always created.
314
:param force_new_tree: If True or False force creation of a tree or
315
prevent such creation respectively.
316
:param format: Override for the bzrdir format to create.
317
:param possible_transports: An optional reusable transports list.
320
# check for non local urls
321
t = get_transport(base, possible_transports)
322
if not isinstance(t, LocalTransport):
323
raise errors.NotLocalUrl(base)
324
bzrdir = BzrDir.create(base, format, possible_transports)
325
repo = bzrdir._find_or_create_repository(force_new_repo)
326
result = bzrdir.create_branch()
327
if force_new_tree or (repo.make_working_trees() and
328
force_new_tree is None):
330
bzrdir.create_workingtree()
331
except errors.NotLocalUrl:
336
@deprecated_function(zero_ninetyone)
337
def create_repository(base, shared=False, format=None):
338
"""Create a new BzrDir and Repository at the url 'base'.
340
If no format is supplied, this will default to the current default
341
BzrDirFormat by default, and use whatever repository format that that
342
uses for bzrdirformat.create_repository.
344
:param shared: Create a shared repository rather than a standalone
346
The Repository object is returned.
348
This must be overridden as an instance method in child classes, where
349
it should take no parameters and construct whatever repository format
350
that child class desires.
352
This method is deprecated, please call create_repository on a bzrdir
355
bzrdir = BzrDir.create(base, format)
356
return bzrdir.create_repository(shared)
359
def create_standalone_workingtree(base, format=None):
360
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
362
'base' must be a local path or a file:// url.
364
This will use the current default BzrDirFormat unless one is
365
specified, and use whatever
366
repository format that that uses for bzrdirformat.create_workingtree,
367
create_branch and create_repository.
369
:param format: Override for the bzrdir format to create.
370
:return: The WorkingTree object.
372
t = get_transport(base)
373
if not isinstance(t, LocalTransport):
374
raise errors.NotLocalUrl(base)
375
bzrdir = BzrDir.create_branch_and_repo(base,
377
format=format).bzrdir
378
return bzrdir.create_workingtree()
380
def create_workingtree(self, revision_id=None, from_branch=None):
381
"""Create a working tree at this BzrDir.
383
:param revision_id: create it as of this revision id.
384
:param from_branch: override bzrdir branch (for lightweight checkouts)
386
raise NotImplementedError(self.create_workingtree)
388
def retire_bzrdir(self, limit=10000):
389
"""Permanently disable the bzrdir.
391
This is done by renaming it to give the user some ability to recover
392
if there was a problem.
394
This will have horrible consequences if anyone has anything locked or
396
:param limit: number of times to retry
401
to_path = '.bzr.retired.%d' % i
402
self.root_transport.rename('.bzr', to_path)
403
note("renamed %s to %s"
404
% (self.root_transport.abspath('.bzr'), to_path))
406
except (errors.TransportError, IOError, errors.PathError):
413
def destroy_workingtree(self):
414
"""Destroy the working tree at this BzrDir.
416
Formats that do not support this may raise UnsupportedOperation.
418
raise NotImplementedError(self.destroy_workingtree)
420
def destroy_workingtree_metadata(self):
421
"""Destroy the control files for the working tree at this BzrDir.
423
The contents of working tree files are not affected.
424
Formats that do not support this may raise UnsupportedOperation.
426
raise NotImplementedError(self.destroy_workingtree_metadata)
428
def find_repository(self):
429
"""Find the repository that should be used.
431
This does not require a branch as we use it to find the repo for
432
new branches as well as to hook existing branches up to their
436
return self.open_repository()
437
except errors.NoRepositoryPresent:
439
next_transport = self.root_transport.clone('..')
441
# find the next containing bzrdir
443
found_bzrdir = BzrDir.open_containing_from_transport(
445
except errors.NotBranchError:
447
raise errors.NoRepositoryPresent(self)
448
# does it have a repository ?
450
repository = found_bzrdir.open_repository()
451
except errors.NoRepositoryPresent:
452
next_transport = found_bzrdir.root_transport.clone('..')
453
if (found_bzrdir.root_transport.base == next_transport.base):
454
# top of the file system
458
if ((found_bzrdir.root_transport.base ==
459
self.root_transport.base) or repository.is_shared()):
462
raise errors.NoRepositoryPresent(self)
463
raise errors.NoRepositoryPresent(self)
465
def get_branch_reference(self):
466
"""Return the referenced URL for the branch in this bzrdir.
468
:raises NotBranchError: If there is no Branch.
469
:return: The URL the branch in this bzrdir references if it is a
470
reference branch, or None for regular branches.
474
def get_branch_transport(self, branch_format):
475
"""Get the transport for use by branch format in this BzrDir.
477
Note that bzr dirs that do not support format strings will raise
478
IncompatibleFormat if the branch format they are given has
479
a format string, and vice versa.
481
If branch_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_branch_transport)
487
def get_repository_transport(self, repository_format):
488
"""Get the transport for use by repository format in this BzrDir.
490
Note that bzr dirs that do not support format strings will raise
491
IncompatibleFormat if the repository format they are given has
492
a format string, and vice versa.
494
If repository_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_repository_transport)
500
def get_workingtree_transport(self, tree_format):
501
"""Get the transport for use by workingtree format in this BzrDir.
503
Note that bzr dirs that do not support format strings will raise
504
IncompatibleFormat if the workingtree format they are given has a
505
format string, and vice versa.
507
If workingtree_format is None, the transport is returned with no
508
checking. If it is not None, then the returned transport is
509
guaranteed to point to an existing directory ready for use.
511
raise NotImplementedError(self.get_workingtree_transport)
513
def __init__(self, _transport, _format):
514
"""Initialize a Bzr control dir object.
516
Only really common logic should reside here, concrete classes should be
517
made with varying behaviours.
519
:param _format: the format that is creating this BzrDir instance.
520
:param _transport: the transport this dir is based at.
522
self._format = _format
523
self.transport = _transport.clone('.bzr')
524
self.root_transport = _transport
526
def is_control_filename(self, filename):
527
"""True if filename is the name of a path which is reserved for bzrdir's.
529
:param filename: A filename within the root transport of this bzrdir.
531
This is true IF and ONLY IF the filename is part of the namespace reserved
532
for bzr control dirs. Currently this is the '.bzr' directory in the root
533
of the root_transport. it is expected that plugins will need to extend
534
this in the future - for instance to make bzr talk with svn working
537
# this might be better on the BzrDirFormat class because it refers to
538
# all the possible bzrdir disk formats.
539
# This method is tested via the workingtree is_control_filename tests-
540
# it was extracted from WorkingTree.is_control_filename. If the method's
541
# contract is extended beyond the current trivial implementation, please
542
# add new tests for it to the appropriate place.
543
return filename == '.bzr' or filename.startswith('.bzr/')
545
def needs_format_conversion(self, format=None):
546
"""Return true if this bzrdir needs convert_format run on it.
548
For instance, if the repository format is out of date but the
549
branch and working tree are not, this should return True.
551
:param format: Optional parameter indicating a specific desired
552
format we plan to arrive at.
554
raise NotImplementedError(self.needs_format_conversion)
557
def open_unsupported(base):
558
"""Open a branch which is not supported."""
559
return BzrDir.open(base, _unsupported=True)
562
def open(base, _unsupported=False, possible_transports=None):
563
"""Open an existing bzrdir, rooted at 'base' (url).
565
:param _unsupported: a private parameter to the BzrDir class.
567
t = get_transport(base, possible_transports=possible_transports)
568
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
571
def open_from_transport(transport, _unsupported=False,
572
_server_formats=True):
573
"""Open a bzrdir within a particular directory.
575
:param transport: Transport containing the bzrdir.
576
:param _unsupported: private.
578
base = transport.base
580
def find_format(transport):
581
return transport, BzrDirFormat.find_format(
582
transport, _server_formats=_server_formats)
584
def redirected(transport, e, redirection_notice):
585
qualified_source = e.get_source_url()
586
relpath = transport.relpath(qualified_source)
587
if not e.target.endswith(relpath):
588
# Not redirected to a branch-format, not a branch
589
raise errors.NotBranchError(path=e.target)
590
target = e.target[:-len(relpath)]
591
note('%s is%s redirected to %s',
592
transport.base, e.permanently, target)
593
# Let's try with a new transport
594
# FIXME: If 'transport' has a qualifier, this should
595
# be applied again to the new transport *iff* the
596
# schemes used are the same. Uncomment this code
597
# once the function (and tests) exist.
599
#target = urlutils.copy_url_qualifiers(original, target)
600
return get_transport(target)
603
transport, format = do_catching_redirections(find_format,
606
except errors.TooManyRedirections:
607
raise errors.NotBranchError(base)
609
BzrDir._check_supported(format, _unsupported)
610
return format.open(transport, _found=True)
612
def open_branch(self, unsupported=False):
613
"""Open the branch object at this BzrDir if one is present.
615
If unsupported is True, then no longer supported branch formats can
618
TODO: static convenience version of this?
620
raise NotImplementedError(self.open_branch)
623
def open_containing(url, possible_transports=None):
624
"""Open an existing branch which contains url.
626
:param url: url to search from.
627
See open_containing_from_transport for more detail.
629
transport = get_transport(url, possible_transports)
630
return BzrDir.open_containing_from_transport(transport)
633
def open_containing_from_transport(a_transport):
634
"""Open an existing branch which contains a_transport.base.
636
This probes for a branch at a_transport, and searches upwards from there.
638
Basically we keep looking up until we find the control directory or
639
run into the root. If there isn't one, raises NotBranchError.
640
If there is one and it is either an unrecognised format or an unsupported
641
format, UnknownFormatError or UnsupportedFormatError are raised.
642
If there is one, it is returned, along with the unused portion of url.
644
:return: The BzrDir that contains the path, and a Unicode path
645
for the rest of the URL.
647
# this gets the normalised url back. I.e. '.' -> the full path.
648
url = a_transport.base
651
result = BzrDir.open_from_transport(a_transport)
652
return result, urlutils.unescape(a_transport.relpath(url))
653
except errors.NotBranchError, e:
656
new_t = a_transport.clone('..')
657
except errors.InvalidURLJoin:
658
# reached the root, whatever that may be
659
raise errors.NotBranchError(path=url)
660
if new_t.base == a_transport.base:
661
# reached the root, whatever that may be
662
raise errors.NotBranchError(path=url)
666
def open_containing_tree_or_branch(klass, location):
667
"""Return the branch and working tree contained by a location.
669
Returns (tree, branch, relpath).
670
If there is no tree at containing the location, tree will be None.
671
If there is no branch containing the location, an exception will be
673
relpath is the portion of the path that is contained by the branch.
675
bzrdir, relpath = klass.open_containing(location)
677
tree = bzrdir.open_workingtree()
678
except (errors.NoWorkingTree, errors.NotLocalUrl):
680
branch = bzrdir.open_branch()
683
return tree, branch, relpath
685
def open_repository(self, _unsupported=False):
686
"""Open the repository object at this BzrDir if one is present.
688
This will not follow the Branch object pointer - it's strictly a direct
689
open facility. Most client code should use open_branch().repository to
692
:param _unsupported: a private parameter, not part of the api.
693
TODO: static convenience version of this?
695
raise NotImplementedError(self.open_repository)
697
def open_workingtree(self, _unsupported=False,
698
recommend_upgrade=True, from_branch=None):
699
"""Open the workingtree object at this BzrDir if one is present.
701
:param recommend_upgrade: Optional keyword parameter, when True (the
702
default), emit through the ui module a recommendation that the user
703
upgrade the working tree when the workingtree being opened is old
704
(but still fully supported).
705
:param from_branch: override bzrdir branch (for lightweight checkouts)
707
raise NotImplementedError(self.open_workingtree)
709
def has_branch(self):
710
"""Tell if this bzrdir contains a branch.
712
Note: if you're going to open the branch, you should just go ahead
713
and try, and not ask permission first. (This method just opens the
714
branch and discards it, and that's somewhat expensive.)
719
except errors.NotBranchError:
722
def has_workingtree(self):
723
"""Tell if this bzrdir contains a working tree.
725
This will still raise an exception if the bzrdir has a workingtree that
726
is remote & inaccessible.
728
Note: if you're going to open the working tree, you should just go ahead
729
and try, and not ask permission first. (This method just opens the
730
workingtree and discards it, and that's somewhat expensive.)
733
self.open_workingtree(recommend_upgrade=False)
735
except errors.NoWorkingTree:
738
def _cloning_metadir(self):
739
"""Produce a metadir suitable for cloning with."""
740
result_format = self._format.__class__()
743
branch = self.open_branch()
744
source_repository = branch.repository
745
except errors.NotBranchError:
747
source_repository = self.open_repository()
748
except errors.NoRepositoryPresent:
749
source_repository = None
751
# XXX TODO: This isinstance is here because we have not implemented
752
# the fix recommended in bug # 103195 - to delegate this choice the
754
repo_format = source_repository._format
755
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
756
result_format.repository_format = repo_format
758
# TODO: Couldn't we just probe for the format in these cases,
759
# rather than opening the whole tree? It would be a little
760
# faster. mbp 20070401
761
tree = self.open_workingtree(recommend_upgrade=False)
762
except (errors.NoWorkingTree, errors.NotLocalUrl):
763
result_format.workingtree_format = None
765
result_format.workingtree_format = tree._format.__class__()
766
return result_format, source_repository
768
def cloning_metadir(self):
769
"""Produce a metadir suitable for cloning or sprouting with.
771
These operations may produce workingtrees (yes, even though they're
772
"cloning" something that doesn't have a tree), so a viable workingtree
773
format must be selected.
775
format, repository = self._cloning_metadir()
776
if format._workingtree_format is None:
777
if repository is None:
779
tree_format = repository._format._matchingbzrdir.workingtree_format
780
format.workingtree_format = tree_format.__class__()
783
def checkout_metadir(self):
784
return self.cloning_metadir()
786
def sprout(self, url, revision_id=None, force_new_repo=False,
787
recurse='down', possible_transports=None):
788
"""Create a copy of this bzrdir prepared for use as a new line of
791
If url's last component does not exist, it will be created.
793
Attributes related to the identity of the source branch like
794
branch nickname will be cleaned, a working tree is created
795
whether one existed before or not; and a local branch is always
798
if revision_id is not None, then the clone operation may tune
799
itself to download less data.
801
target_transport = get_transport(url, possible_transports)
802
target_transport.ensure_base()
803
cloning_format = self.cloning_metadir()
804
result = cloning_format.initialize_on_transport(target_transport)
806
source_branch = self.open_branch()
807
source_repository = source_branch.repository
808
except errors.NotBranchError:
811
source_repository = self.open_repository()
812
except errors.NoRepositoryPresent:
813
source_repository = None
818
result_repo = result.find_repository()
819
except errors.NoRepositoryPresent:
821
if source_repository is None and result_repo is not None:
823
elif source_repository is None and result_repo is None:
824
# no repo available, make a new one
825
result.create_repository()
826
elif source_repository is not None and result_repo is None:
827
# have source, and want to make a new target repo
828
result_repo = source_repository.sprout(result,
829
revision_id=revision_id)
831
# fetch needed content into target.
832
if source_repository is not None:
834
# source_repository.copy_content_into(result_repo,
835
# revision_id=revision_id)
836
# so we can override the copy method
837
result_repo.fetch(source_repository, revision_id=revision_id)
838
if source_branch is not None:
839
source_branch.sprout(result, revision_id=revision_id)
841
result.create_branch()
842
if isinstance(target_transport, LocalTransport) and (
843
result_repo is None or result_repo.make_working_trees()):
844
wt = result.create_workingtree()
847
if wt.path2id('') is None:
849
wt.set_root_id(self.open_workingtree.get_root_id())
850
except errors.NoWorkingTree:
856
if recurse == 'down':
858
basis = wt.basis_tree()
860
subtrees = basis.iter_references()
861
recurse_branch = wt.branch
862
elif source_branch is not None:
863
basis = source_branch.basis_tree()
865
subtrees = basis.iter_references()
866
recurse_branch = source_branch
871
for path, file_id in subtrees:
872
target = urlutils.join(url, urlutils.escape(path))
873
sublocation = source_branch.reference_parent(file_id, path)
874
sublocation.bzrdir.sprout(target,
875
basis.get_reference_revision(file_id, path),
876
force_new_repo=force_new_repo, recurse=recurse)
878
if basis is not None:
883
class BzrDirPreSplitOut(BzrDir):
884
"""A common class for the all-in-one formats."""
886
def __init__(self, _transport, _format):
887
"""See BzrDir.__init__."""
888
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
889
assert self._format._lock_class == lockable_files.TransportLock
890
assert self._format._lock_file_name == 'branch-lock'
891
self._control_files = lockable_files.LockableFiles(
892
self.get_branch_transport(None),
893
self._format._lock_file_name,
894
self._format._lock_class)
896
def break_lock(self):
897
"""Pre-splitout bzrdirs do not suffer from stale locks."""
898
raise NotImplementedError(self.break_lock)
900
def clone(self, url, revision_id=None, force_new_repo=False):
901
"""See BzrDir.clone()."""
902
from bzrlib.workingtree import WorkingTreeFormat2
904
result = self._format._initialize_for_clone(url)
905
self.open_repository().clone(result, revision_id=revision_id)
906
from_branch = self.open_branch()
907
from_branch.clone(result, revision_id=revision_id)
909
self.open_workingtree().clone(result)
910
except errors.NotLocalUrl:
911
# make a new one, this format always has to have one.
913
WorkingTreeFormat2().initialize(result)
914
except errors.NotLocalUrl:
915
# but we cannot do it for remote trees.
916
to_branch = result.open_branch()
917
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
920
def create_branch(self):
921
"""See BzrDir.create_branch."""
922
return self.open_branch()
924
def destroy_branch(self):
925
"""See BzrDir.destroy_branch."""
926
raise errors.UnsupportedOperation(self.destroy_branch, self)
928
def create_repository(self, shared=False):
929
"""See BzrDir.create_repository."""
931
raise errors.IncompatibleFormat('shared repository', self._format)
932
return self.open_repository()
934
def create_workingtree(self, revision_id=None, from_branch=None):
935
"""See BzrDir.create_workingtree."""
936
# this looks buggy but is not -really-
937
# because this format creates the workingtree when the bzrdir is
939
# clone and sprout will have set the revision_id
940
# and that will have set it for us, its only
941
# specific uses of create_workingtree in isolation
942
# that can do wonky stuff here, and that only
943
# happens for creating checkouts, which cannot be
944
# done on this format anyway. So - acceptable wart.
945
result = self.open_workingtree(recommend_upgrade=False)
946
if revision_id is not None:
947
if revision_id == _mod_revision.NULL_REVISION:
948
result.set_parent_ids([])
950
result.set_parent_ids([revision_id])
953
def destroy_workingtree(self):
954
"""See BzrDir.destroy_workingtree."""
955
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
957
def destroy_workingtree_metadata(self):
958
"""See BzrDir.destroy_workingtree_metadata."""
959
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
962
def get_branch_transport(self, branch_format):
963
"""See BzrDir.get_branch_transport()."""
964
if branch_format is None:
965
return self.transport
967
branch_format.get_format_string()
968
except NotImplementedError:
969
return self.transport
970
raise errors.IncompatibleFormat(branch_format, self._format)
972
def get_repository_transport(self, repository_format):
973
"""See BzrDir.get_repository_transport()."""
974
if repository_format is None:
975
return self.transport
977
repository_format.get_format_string()
978
except NotImplementedError:
979
return self.transport
980
raise errors.IncompatibleFormat(repository_format, self._format)
982
def get_workingtree_transport(self, workingtree_format):
983
"""See BzrDir.get_workingtree_transport()."""
984
if workingtree_format is None:
985
return self.transport
987
workingtree_format.get_format_string()
988
except NotImplementedError:
989
return self.transport
990
raise errors.IncompatibleFormat(workingtree_format, self._format)
992
def needs_format_conversion(self, format=None):
993
"""See BzrDir.needs_format_conversion()."""
994
# if the format is not the same as the system default,
995
# an upgrade is needed.
997
format = BzrDirFormat.get_default_format()
998
return not isinstance(self._format, format.__class__)
1000
def open_branch(self, unsupported=False):
1001
"""See BzrDir.open_branch."""
1002
from bzrlib.branch import BzrBranchFormat4
1003
format = BzrBranchFormat4()
1004
self._check_supported(format, unsupported)
1005
return format.open(self, _found=True)
1007
def sprout(self, url, revision_id=None, force_new_repo=False,
1008
possible_transports=None):
1009
"""See BzrDir.sprout()."""
1010
from bzrlib.workingtree import WorkingTreeFormat2
1011
self._make_tail(url)
1012
result = self._format._initialize_for_clone(url)
1014
self.open_repository().clone(result, revision_id=revision_id)
1015
except errors.NoRepositoryPresent:
1018
self.open_branch().sprout(result, revision_id=revision_id)
1019
except errors.NotBranchError:
1021
# we always want a working tree
1022
WorkingTreeFormat2().initialize(result)
1026
class BzrDir4(BzrDirPreSplitOut):
1027
"""A .bzr version 4 control object.
1029
This is a deprecated format and may be removed after sept 2006.
1032
def create_repository(self, shared=False):
1033
"""See BzrDir.create_repository."""
1034
return self._format.repository_format.initialize(self, shared)
1036
def needs_format_conversion(self, format=None):
1037
"""Format 4 dirs are always in need of conversion."""
1040
def open_repository(self):
1041
"""See BzrDir.open_repository."""
1042
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1043
return RepositoryFormat4().open(self, _found=True)
1046
class BzrDir5(BzrDirPreSplitOut):
1047
"""A .bzr version 5 control object.
1049
This is a deprecated format and may be removed after sept 2006.
1052
def open_repository(self):
1053
"""See BzrDir.open_repository."""
1054
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1055
return RepositoryFormat5().open(self, _found=True)
1057
def open_workingtree(self, _unsupported=False,
1058
recommend_upgrade=True):
1059
"""See BzrDir.create_workingtree."""
1060
from bzrlib.workingtree import WorkingTreeFormat2
1061
wt_format = WorkingTreeFormat2()
1062
# we don't warn here about upgrades; that ought to be handled for the
1064
return wt_format.open(self, _found=True)
1067
class BzrDir6(BzrDirPreSplitOut):
1068
"""A .bzr version 6 control object.
1070
This is a deprecated format and may be removed after sept 2006.
1073
def open_repository(self):
1074
"""See BzrDir.open_repository."""
1075
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1076
return RepositoryFormat6().open(self, _found=True)
1078
def open_workingtree(self, _unsupported=False,
1079
recommend_upgrade=True):
1080
"""See BzrDir.create_workingtree."""
1081
# we don't warn here about upgrades; that ought to be handled for the
1083
from bzrlib.workingtree import WorkingTreeFormat2
1084
return WorkingTreeFormat2().open(self, _found=True)
1087
class BzrDirMeta1(BzrDir):
1088
"""A .bzr meta version 1 control object.
1090
This is the first control object where the
1091
individual aspects are really split out: there are separate repository,
1092
workingtree and branch subdirectories and any subset of the three can be
1093
present within a BzrDir.
1096
def can_convert_format(self):
1097
"""See BzrDir.can_convert_format()."""
1100
def create_branch(self):
1101
"""See BzrDir.create_branch."""
1102
return self._format.get_branch_format().initialize(self)
1104
def destroy_branch(self):
1105
"""See BzrDir.create_branch."""
1106
self.transport.delete_tree('branch')
1108
def create_repository(self, shared=False):
1109
"""See BzrDir.create_repository."""
1110
return self._format.repository_format.initialize(self, shared)
1112
def create_workingtree(self, revision_id=None, from_branch=None):
1113
"""See BzrDir.create_workingtree."""
1114
return self._format.workingtree_format.initialize(
1115
self, revision_id, from_branch=from_branch)
1117
def destroy_workingtree(self):
1118
"""See BzrDir.destroy_workingtree."""
1119
wt = self.open_workingtree(recommend_upgrade=False)
1120
repository = wt.branch.repository
1121
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1122
wt.revert(old_tree=empty)
1123
self.destroy_workingtree_metadata()
1125
def destroy_workingtree_metadata(self):
1126
self.transport.delete_tree('checkout')
1128
def find_branch_format(self):
1129
"""Find the branch 'format' for this bzrdir.
1131
This might be a synthetic object for e.g. RemoteBranch and SVN.
1133
from bzrlib.branch import BranchFormat
1134
return BranchFormat.find_format(self)
1136
def _get_mkdir_mode(self):
1137
"""Figure out the mode to use when creating a bzrdir subdir."""
1138
temp_control = lockable_files.LockableFiles(self.transport, '',
1139
lockable_files.TransportLock)
1140
return temp_control._dir_mode
1142
def get_branch_reference(self):
1143
"""See BzrDir.get_branch_reference()."""
1144
from bzrlib.branch import BranchFormat
1145
format = BranchFormat.find_format(self)
1146
return format.get_reference(self)
1148
def get_branch_transport(self, branch_format):
1149
"""See BzrDir.get_branch_transport()."""
1150
if branch_format is None:
1151
return self.transport.clone('branch')
1153
branch_format.get_format_string()
1154
except NotImplementedError:
1155
raise errors.IncompatibleFormat(branch_format, self._format)
1157
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1158
except errors.FileExists:
1160
return self.transport.clone('branch')
1162
def get_repository_transport(self, repository_format):
1163
"""See BzrDir.get_repository_transport()."""
1164
if repository_format is None:
1165
return self.transport.clone('repository')
1167
repository_format.get_format_string()
1168
except NotImplementedError:
1169
raise errors.IncompatibleFormat(repository_format, self._format)
1171
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1172
except errors.FileExists:
1174
return self.transport.clone('repository')
1176
def get_workingtree_transport(self, workingtree_format):
1177
"""See BzrDir.get_workingtree_transport()."""
1178
if workingtree_format is None:
1179
return self.transport.clone('checkout')
1181
workingtree_format.get_format_string()
1182
except NotImplementedError:
1183
raise errors.IncompatibleFormat(workingtree_format, self._format)
1185
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1186
except errors.FileExists:
1188
return self.transport.clone('checkout')
1190
def needs_format_conversion(self, format=None):
1191
"""See BzrDir.needs_format_conversion()."""
1193
format = BzrDirFormat.get_default_format()
1194
if not isinstance(self._format, format.__class__):
1195
# it is not a meta dir format, conversion is needed.
1197
# we might want to push this down to the repository?
1199
if not isinstance(self.open_repository()._format,
1200
format.repository_format.__class__):
1201
# the repository needs an upgrade.
1203
except errors.NoRepositoryPresent:
1206
if not isinstance(self.open_branch()._format,
1207
format.get_branch_format().__class__):
1208
# the branch needs an upgrade.
1210
except errors.NotBranchError:
1213
my_wt = self.open_workingtree(recommend_upgrade=False)
1214
if not isinstance(my_wt._format,
1215
format.workingtree_format.__class__):
1216
# the workingtree needs an upgrade.
1218
except (errors.NoWorkingTree, errors.NotLocalUrl):
1222
def open_branch(self, unsupported=False):
1223
"""See BzrDir.open_branch."""
1224
format = self.find_branch_format()
1225
self._check_supported(format, unsupported)
1226
return format.open(self, _found=True)
1228
def open_repository(self, unsupported=False):
1229
"""See BzrDir.open_repository."""
1230
from bzrlib.repository import RepositoryFormat
1231
format = RepositoryFormat.find_format(self)
1232
self._check_supported(format, unsupported)
1233
return format.open(self, _found=True)
1235
def open_workingtree(self, unsupported=False,
1236
recommend_upgrade=True):
1237
"""See BzrDir.open_workingtree."""
1238
from bzrlib.workingtree import WorkingTreeFormat
1239
format = WorkingTreeFormat.find_format(self)
1240
self._check_supported(format, unsupported,
1242
basedir=self.root_transport.base)
1243
return format.open(self, _found=True)
1246
class BzrDirFormat(object):
1247
"""An encapsulation of the initialization and open routines for a format.
1249
Formats provide three things:
1250
* An initialization routine,
1254
Formats are placed in a dict by their format string for reference
1255
during bzrdir opening. These should be subclasses of BzrDirFormat
1258
Once a format is deprecated, just deprecate the initialize and open
1259
methods on the format class. Do not deprecate the object, as the
1260
object will be created every system load.
1263
_default_format = None
1264
"""The default format used for new .bzr dirs."""
1267
"""The known formats."""
1269
_control_formats = []
1270
"""The registered control formats - .bzr, ....
1272
This is a list of BzrDirFormat objects.
1275
_control_server_formats = []
1276
"""The registered control server formats, e.g. RemoteBzrDirs.
1278
This is a list of BzrDirFormat objects.
1281
_lock_file_name = 'branch-lock'
1283
# _lock_class must be set in subclasses to the lock type, typ.
1284
# TransportLock or LockDir
1287
def find_format(klass, transport, _server_formats=True):
1288
"""Return the format present at transport."""
1290
formats = klass._control_server_formats + klass._control_formats
1292
formats = klass._control_formats
1293
for format in formats:
1295
return format.probe_transport(transport)
1296
except errors.NotBranchError:
1297
# this format does not find a control dir here.
1299
raise errors.NotBranchError(path=transport.base)
1302
def probe_transport(klass, transport):
1303
"""Return the .bzrdir style format present in a directory."""
1305
format_string = transport.get(".bzr/branch-format").read()
1306
except errors.NoSuchFile:
1307
raise errors.NotBranchError(path=transport.base)
1310
return klass._formats[format_string]
1312
raise errors.UnknownFormatError(format=format_string)
1315
def get_default_format(klass):
1316
"""Return the current default format."""
1317
return klass._default_format
1319
def get_format_string(self):
1320
"""Return the ASCII format string that identifies this format."""
1321
raise NotImplementedError(self.get_format_string)
1323
def get_format_description(self):
1324
"""Return the short description for this format."""
1325
raise NotImplementedError(self.get_format_description)
1327
def get_converter(self, format=None):
1328
"""Return the converter to use to convert bzrdirs needing converts.
1330
This returns a bzrlib.bzrdir.Converter object.
1332
This should return the best upgrader to step this format towards the
1333
current default format. In the case of plugins we can/should provide
1334
some means for them to extend the range of returnable converters.
1336
:param format: Optional format to override the default format of the
1339
raise NotImplementedError(self.get_converter)
1341
def initialize(self, url, possible_transports=None):
1342
"""Create a bzr control dir at this url and return an opened copy.
1344
Subclasses should typically override initialize_on_transport
1345
instead of this method.
1347
return self.initialize_on_transport(get_transport(url,
1348
possible_transports))
1350
def initialize_on_transport(self, transport):
1351
"""Initialize a new bzrdir in the base directory of a Transport."""
1352
# Since we don't have a .bzr directory, inherit the
1353
# mode from the root directory
1354
temp_control = lockable_files.LockableFiles(transport,
1355
'', lockable_files.TransportLock)
1356
temp_control._transport.mkdir('.bzr',
1357
# FIXME: RBC 20060121 don't peek under
1359
mode=temp_control._dir_mode)
1360
if sys.platform == 'win32' and isinstance(transport, LocalTransport):
1361
win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1362
file_mode = temp_control._file_mode
1364
mutter('created control directory in ' + transport.base)
1365
control = transport.clone('.bzr')
1366
utf8_files = [('README',
1367
"This is a Bazaar-NG control directory.\n"
1368
"Do not change any files in this directory.\n"),
1369
('branch-format', self.get_format_string()),
1371
# NB: no need to escape relative paths that are url safe.
1372
control_files = lockable_files.LockableFiles(control,
1373
self._lock_file_name, self._lock_class)
1374
control_files.create_lock()
1375
control_files.lock_write()
1377
for file, content in utf8_files:
1378
control_files.put_utf8(file, content)
1380
control_files.unlock()
1381
return self.open(transport, _found=True)
1383
def is_supported(self):
1384
"""Is this format supported?
1386
Supported formats must be initializable and openable.
1387
Unsupported formats may not support initialization or committing or
1388
some other features depending on the reason for not being supported.
1392
def same_model(self, target_format):
1393
return (self.repository_format.rich_root_data ==
1394
target_format.rich_root_data)
1397
def known_formats(klass):
1398
"""Return all the known formats.
1400
Concrete formats should override _known_formats.
1402
# There is double indirection here to make sure that control
1403
# formats used by more than one dir format will only be probed
1404
# once. This can otherwise be quite expensive for remote connections.
1406
for format in klass._control_formats:
1407
result.update(format._known_formats())
1411
def _known_formats(klass):
1412
"""Return the known format instances for this control format."""
1413
return set(klass._formats.values())
1415
def open(self, transport, _found=False):
1416
"""Return an instance of this format for the dir transport points at.
1418
_found is a private parameter, do not use it.
1421
found_format = BzrDirFormat.find_format(transport)
1422
if not isinstance(found_format, self.__class__):
1423
raise AssertionError("%s was asked to open %s, but it seems to need "
1425
% (self, transport, found_format))
1426
return self._open(transport)
1428
def _open(self, transport):
1429
"""Template method helper for opening BzrDirectories.
1431
This performs the actual open and any additional logic or parameter
1434
raise NotImplementedError(self._open)
1437
def register_format(klass, format):
1438
klass._formats[format.get_format_string()] = format
1441
def register_control_format(klass, format):
1442
"""Register a format that does not use '.bzr' for its control dir.
1444
TODO: This should be pulled up into a 'ControlDirFormat' base class
1445
which BzrDirFormat can inherit from, and renamed to register_format
1446
there. It has been done without that for now for simplicity of
1449
klass._control_formats.append(format)
1452
def register_control_server_format(klass, format):
1453
"""Register a control format for client-server environments.
1455
These formats will be tried before ones registered with
1456
register_control_format. This gives implementations that decide to the
1457
chance to grab it before anything looks at the contents of the format
1460
klass._control_server_formats.append(format)
1463
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1464
def set_default_format(klass, format):
1465
klass._set_default_format(format)
1468
def _set_default_format(klass, format):
1469
"""Set default format (for testing behavior of defaults only)"""
1470
klass._default_format = format
1474
return self.get_format_string().rstrip()
1477
def unregister_format(klass, format):
1478
assert klass._formats[format.get_format_string()] is format
1479
del klass._formats[format.get_format_string()]
1482
def unregister_control_format(klass, format):
1483
klass._control_formats.remove(format)
1486
class BzrDirFormat4(BzrDirFormat):
1487
"""Bzr dir format 4.
1489
This format is a combined format for working tree, branch and repository.
1491
- Format 1 working trees [always]
1492
- Format 4 branches [always]
1493
- Format 4 repositories [always]
1495
This format is deprecated: it indexes texts using a text it which is
1496
removed in format 5; write support for this format has been removed.
1499
_lock_class = lockable_files.TransportLock
1501
def get_format_string(self):
1502
"""See BzrDirFormat.get_format_string()."""
1503
return "Bazaar-NG branch, format 0.0.4\n"
1505
def get_format_description(self):
1506
"""See BzrDirFormat.get_format_description()."""
1507
return "All-in-one format 4"
1509
def get_converter(self, format=None):
1510
"""See BzrDirFormat.get_converter()."""
1511
# there is one and only one upgrade path here.
1512
return ConvertBzrDir4To5()
1514
def initialize_on_transport(self, transport):
1515
"""Format 4 branches cannot be created."""
1516
raise errors.UninitializableFormat(self)
1518
def is_supported(self):
1519
"""Format 4 is not supported.
1521
It is not supported because the model changed from 4 to 5 and the
1522
conversion logic is expensive - so doing it on the fly was not
1527
def _open(self, transport):
1528
"""See BzrDirFormat._open."""
1529
return BzrDir4(transport, self)
1531
def __return_repository_format(self):
1532
"""Circular import protection."""
1533
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1534
return RepositoryFormat4()
1535
repository_format = property(__return_repository_format)
1538
class BzrDirFormat5(BzrDirFormat):
1539
"""Bzr control format 5.
1541
This format is a combined format for working tree, branch and repository.
1543
- Format 2 working trees [always]
1544
- Format 4 branches [always]
1545
- Format 5 repositories [always]
1546
Unhashed stores in the repository.
1549
_lock_class = lockable_files.TransportLock
1551
def get_format_string(self):
1552
"""See BzrDirFormat.get_format_string()."""
1553
return "Bazaar-NG branch, format 5\n"
1555
def get_format_description(self):
1556
"""See BzrDirFormat.get_format_description()."""
1557
return "All-in-one format 5"
1559
def get_converter(self, format=None):
1560
"""See BzrDirFormat.get_converter()."""
1561
# there is one and only one upgrade path here.
1562
return ConvertBzrDir5To6()
1564
def _initialize_for_clone(self, url):
1565
return self.initialize_on_transport(get_transport(url), _cloning=True)
1567
def initialize_on_transport(self, transport, _cloning=False):
1568
"""Format 5 dirs always have working tree, branch and repository.
1570
Except when they are being cloned.
1572
from bzrlib.branch import BzrBranchFormat4
1573
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1574
from bzrlib.workingtree import WorkingTreeFormat2
1575
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1576
RepositoryFormat5().initialize(result, _internal=True)
1578
branch = BzrBranchFormat4().initialize(result)
1580
WorkingTreeFormat2().initialize(result)
1581
except errors.NotLocalUrl:
1582
# Even though we can't access the working tree, we need to
1583
# create its control files.
1584
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1587
def _open(self, transport):
1588
"""See BzrDirFormat._open."""
1589
return BzrDir5(transport, self)
1591
def __return_repository_format(self):
1592
"""Circular import protection."""
1593
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1594
return RepositoryFormat5()
1595
repository_format = property(__return_repository_format)
1598
class BzrDirFormat6(BzrDirFormat):
1599
"""Bzr control format 6.
1601
This format is a combined format for working tree, branch and repository.
1603
- Format 2 working trees [always]
1604
- Format 4 branches [always]
1605
- Format 6 repositories [always]
1608
_lock_class = lockable_files.TransportLock
1610
def get_format_string(self):
1611
"""See BzrDirFormat.get_format_string()."""
1612
return "Bazaar-NG branch, format 6\n"
1614
def get_format_description(self):
1615
"""See BzrDirFormat.get_format_description()."""
1616
return "All-in-one format 6"
1618
def get_converter(self, format=None):
1619
"""See BzrDirFormat.get_converter()."""
1620
# there is one and only one upgrade path here.
1621
return ConvertBzrDir6ToMeta()
1623
def _initialize_for_clone(self, url):
1624
return self.initialize_on_transport(get_transport(url), _cloning=True)
1626
def initialize_on_transport(self, transport, _cloning=False):
1627
"""Format 6 dirs always have working tree, branch and repository.
1629
Except when they are being cloned.
1631
from bzrlib.branch import BzrBranchFormat4
1632
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1633
from bzrlib.workingtree import WorkingTreeFormat2
1634
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1635
RepositoryFormat6().initialize(result, _internal=True)
1637
branch = BzrBranchFormat4().initialize(result)
1639
WorkingTreeFormat2().initialize(result)
1640
except errors.NotLocalUrl:
1641
# Even though we can't access the working tree, we need to
1642
# create its control files.
1643
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1646
def _open(self, transport):
1647
"""See BzrDirFormat._open."""
1648
return BzrDir6(transport, self)
1650
def __return_repository_format(self):
1651
"""Circular import protection."""
1652
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1653
return RepositoryFormat6()
1654
repository_format = property(__return_repository_format)
1657
class BzrDirMetaFormat1(BzrDirFormat):
1658
"""Bzr meta control format 1
1660
This is the first format with split out working tree, branch and repository
1663
- Format 3 working trees [optional]
1664
- Format 5 branches [optional]
1665
- Format 7 repositories [optional]
1668
_lock_class = lockdir.LockDir
1671
self._workingtree_format = None
1672
self._branch_format = None
1674
def __eq__(self, other):
1675
if other.__class__ is not self.__class__:
1677
if other.repository_format != self.repository_format:
1679
if other.workingtree_format != self.workingtree_format:
1683
def __ne__(self, other):
1684
return not self == other
1686
def get_branch_format(self):
1687
if self._branch_format is None:
1688
from bzrlib.branch import BranchFormat
1689
self._branch_format = BranchFormat.get_default_format()
1690
return self._branch_format
1692
def set_branch_format(self, format):
1693
self._branch_format = format
1695
def get_converter(self, format=None):
1696
"""See BzrDirFormat.get_converter()."""
1698
format = BzrDirFormat.get_default_format()
1699
if not isinstance(self, format.__class__):
1700
# converting away from metadir is not implemented
1701
raise NotImplementedError(self.get_converter)
1702
return ConvertMetaToMeta(format)
1704
def get_format_string(self):
1705
"""See BzrDirFormat.get_format_string()."""
1706
return "Bazaar-NG meta directory, format 1\n"
1708
def get_format_description(self):
1709
"""See BzrDirFormat.get_format_description()."""
1710
return "Meta directory format 1"
1712
def _open(self, transport):
1713
"""See BzrDirFormat._open."""
1714
return BzrDirMeta1(transport, self)
1716
def __return_repository_format(self):
1717
"""Circular import protection."""
1718
if getattr(self, '_repository_format', None):
1719
return self._repository_format
1720
from bzrlib.repository import RepositoryFormat
1721
return RepositoryFormat.get_default_format()
1723
def __set_repository_format(self, value):
1724
"""Allow changing the repository format for metadir formats."""
1725
self._repository_format = value
1727
repository_format = property(__return_repository_format, __set_repository_format)
1729
def __get_workingtree_format(self):
1730
if self._workingtree_format is None:
1731
from bzrlib.workingtree import WorkingTreeFormat
1732
self._workingtree_format = WorkingTreeFormat.get_default_format()
1733
return self._workingtree_format
1735
def __set_workingtree_format(self, wt_format):
1736
self._workingtree_format = wt_format
1738
workingtree_format = property(__get_workingtree_format,
1739
__set_workingtree_format)
1742
# Register bzr control format
1743
BzrDirFormat.register_control_format(BzrDirFormat)
1745
# Register bzr formats
1746
BzrDirFormat.register_format(BzrDirFormat4())
1747
BzrDirFormat.register_format(BzrDirFormat5())
1748
BzrDirFormat.register_format(BzrDirFormat6())
1749
__default_format = BzrDirMetaFormat1()
1750
BzrDirFormat.register_format(__default_format)
1751
BzrDirFormat._default_format = __default_format
1754
class Converter(object):
1755
"""Converts a disk format object from one format to another."""
1757
def convert(self, to_convert, pb):
1758
"""Perform the conversion of to_convert, giving feedback via pb.
1760
:param to_convert: The disk object to convert.
1761
:param pb: a progress bar to use for progress information.
1764
def step(self, message):
1765
"""Update the pb by a step."""
1767
self.pb.update(message, self.count, self.total)
1770
class ConvertBzrDir4To5(Converter):
1771
"""Converts format 4 bzr dirs to format 5."""
1774
super(ConvertBzrDir4To5, self).__init__()
1775
self.converted_revs = set()
1776
self.absent_revisions = set()
1780
def convert(self, to_convert, pb):
1781
"""See Converter.convert()."""
1782
self.bzrdir = to_convert
1784
self.pb.note('starting upgrade from format 4 to 5')
1785
if isinstance(self.bzrdir.transport, LocalTransport):
1786
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1787
self._convert_to_weaves()
1788
return BzrDir.open(self.bzrdir.root_transport.base)
1790
def _convert_to_weaves(self):
1791
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1794
stat = self.bzrdir.transport.stat('weaves')
1795
if not S_ISDIR(stat.st_mode):
1796
self.bzrdir.transport.delete('weaves')
1797
self.bzrdir.transport.mkdir('weaves')
1798
except errors.NoSuchFile:
1799
self.bzrdir.transport.mkdir('weaves')
1800
# deliberately not a WeaveFile as we want to build it up slowly.
1801
self.inv_weave = Weave('inventory')
1802
# holds in-memory weaves for all files
1803
self.text_weaves = {}
1804
self.bzrdir.transport.delete('branch-format')
1805
self.branch = self.bzrdir.open_branch()
1806
self._convert_working_inv()
1807
rev_history = self.branch.revision_history()
1808
# to_read is a stack holding the revisions we still need to process;
1809
# appending to it adds new highest-priority revisions
1810
self.known_revisions = set(rev_history)
1811
self.to_read = rev_history[-1:]
1813
rev_id = self.to_read.pop()
1814
if (rev_id not in self.revisions
1815
and rev_id not in self.absent_revisions):
1816
self._load_one_rev(rev_id)
1818
to_import = self._make_order()
1819
for i, rev_id in enumerate(to_import):
1820
self.pb.update('converting revision', i, len(to_import))
1821
self._convert_one_rev(rev_id)
1823
self._write_all_weaves()
1824
self._write_all_revs()
1825
self.pb.note('upgraded to weaves:')
1826
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1827
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1828
self.pb.note(' %6d texts', self.text_count)
1829
self._cleanup_spare_files_after_format4()
1830
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1832
def _cleanup_spare_files_after_format4(self):
1833
# FIXME working tree upgrade foo.
1834
for n in 'merged-patches', 'pending-merged-patches':
1836
## assert os.path.getsize(p) == 0
1837
self.bzrdir.transport.delete(n)
1838
except errors.NoSuchFile:
1840
self.bzrdir.transport.delete_tree('inventory-store')
1841
self.bzrdir.transport.delete_tree('text-store')
1843
def _convert_working_inv(self):
1844
inv = xml4.serializer_v4.read_inventory(
1845
self.branch.control_files.get('inventory'))
1846
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1847
# FIXME inventory is a working tree change.
1848
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1850
def _write_all_weaves(self):
1851
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1852
weave_transport = self.bzrdir.transport.clone('weaves')
1853
weaves = WeaveStore(weave_transport, prefixed=False)
1854
transaction = WriteTransaction()
1858
for file_id, file_weave in self.text_weaves.items():
1859
self.pb.update('writing weave', i, len(self.text_weaves))
1860
weaves._put_weave(file_id, file_weave, transaction)
1862
self.pb.update('inventory', 0, 1)
1863
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1864
self.pb.update('inventory', 1, 1)
1868
def _write_all_revs(self):
1869
"""Write all revisions out in new form."""
1870
self.bzrdir.transport.delete_tree('revision-store')
1871
self.bzrdir.transport.mkdir('revision-store')
1872
revision_transport = self.bzrdir.transport.clone('revision-store')
1874
_revision_store = TextRevisionStore(TextStore(revision_transport,
1878
transaction = WriteTransaction()
1879
for i, rev_id in enumerate(self.converted_revs):
1880
self.pb.update('write revision', i, len(self.converted_revs))
1881
_revision_store.add_revision(self.revisions[rev_id], transaction)
1885
def _load_one_rev(self, rev_id):
1886
"""Load a revision object into memory.
1888
Any parents not either loaded or abandoned get queued to be
1890
self.pb.update('loading revision',
1891
len(self.revisions),
1892
len(self.known_revisions))
1893
if not self.branch.repository.has_revision(rev_id):
1895
self.pb.note('revision {%s} not present in branch; '
1896
'will be converted as a ghost',
1898
self.absent_revisions.add(rev_id)
1900
rev = self.branch.repository._revision_store.get_revision(rev_id,
1901
self.branch.repository.get_transaction())
1902
for parent_id in rev.parent_ids:
1903
self.known_revisions.add(parent_id)
1904
self.to_read.append(parent_id)
1905
self.revisions[rev_id] = rev
1907
def _load_old_inventory(self, rev_id):
1908
assert rev_id not in self.converted_revs
1909
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1910
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1911
inv.revision_id = rev_id
1912
rev = self.revisions[rev_id]
1913
if rev.inventory_sha1:
1914
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1915
'inventory sha mismatch for {%s}' % rev_id
1918
def _load_updated_inventory(self, rev_id):
1919
assert rev_id in self.converted_revs
1920
inv_xml = self.inv_weave.get_text(rev_id)
1921
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1924
def _convert_one_rev(self, rev_id):
1925
"""Convert revision and all referenced objects to new format."""
1926
rev = self.revisions[rev_id]
1927
inv = self._load_old_inventory(rev_id)
1928
present_parents = [p for p in rev.parent_ids
1929
if p not in self.absent_revisions]
1930
self._convert_revision_contents(rev, inv, present_parents)
1931
self._store_new_inv(rev, inv, present_parents)
1932
self.converted_revs.add(rev_id)
1934
def _store_new_inv(self, rev, inv, present_parents):
1935
# the XML is now updated with text versions
1937
entries = inv.iter_entries()
1939
for path, ie in entries:
1940
assert getattr(ie, 'revision', None) is not None, \
1941
'no revision on {%s} in {%s}' % \
1942
(file_id, rev.revision_id)
1943
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1944
new_inv_sha1 = sha_string(new_inv_xml)
1945
self.inv_weave.add_lines(rev.revision_id,
1947
new_inv_xml.splitlines(True))
1948
rev.inventory_sha1 = new_inv_sha1
1950
def _convert_revision_contents(self, rev, inv, present_parents):
1951
"""Convert all the files within a revision.
1953
Also upgrade the inventory to refer to the text revision ids."""
1954
rev_id = rev.revision_id
1955
mutter('converting texts of revision {%s}',
1957
parent_invs = map(self._load_updated_inventory, present_parents)
1958
entries = inv.iter_entries()
1960
for path, ie in entries:
1961
self._convert_file_version(rev, ie, parent_invs)
1963
def _convert_file_version(self, rev, ie, parent_invs):
1964
"""Convert one version of one file.
1966
The file needs to be added into the weave if it is a merge
1967
of >=2 parents or if it's changed from its parent.
1969
file_id = ie.file_id
1970
rev_id = rev.revision_id
1971
w = self.text_weaves.get(file_id)
1974
self.text_weaves[file_id] = w
1975
text_changed = False
1976
parent_candiate_entries = ie.parent_candidates(parent_invs)
1977
for old_revision in parent_candiate_entries.keys():
1978
# if this fails, its a ghost ?
1979
assert old_revision in self.converted_revs, \
1980
"Revision {%s} not in converted_revs" % old_revision
1981
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1982
# XXX: Note that this is unordered - and this is tolerable because
1983
# the previous code was also unordered.
1984
previous_entries = dict((head, parent_candiate_entries[head]) for head
1986
self.snapshot_ie(previous_entries, ie, w, rev_id)
1988
assert getattr(ie, 'revision', None) is not None
1990
def get_parents(self, revision_ids):
1991
for revision_id in revision_ids:
1992
yield self.revisions[revision_id].parent_ids
1994
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1995
# TODO: convert this logic, which is ~= snapshot to
1996
# a call to:. This needs the path figured out. rather than a work_tree
1997
# a v4 revision_tree can be given, or something that looks enough like
1998
# one to give the file content to the entry if it needs it.
1999
# and we need something that looks like a weave store for snapshot to
2001
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
2002
if len(previous_revisions) == 1:
2003
previous_ie = previous_revisions.values()[0]
2004
if ie._unchanged(previous_ie):
2005
ie.revision = previous_ie.revision
2008
text = self.branch.repository.weave_store.get(ie.text_id)
2009
file_lines = text.readlines()
2010
assert sha_strings(file_lines) == ie.text_sha1
2011
assert sum(map(len, file_lines)) == ie.text_size
2012
w.add_lines(rev_id, previous_revisions, file_lines)
2013
self.text_count += 1
2015
w.add_lines(rev_id, previous_revisions, [])
2016
ie.revision = rev_id
2018
def _make_order(self):
2019
"""Return a suitable order for importing revisions.
2021
The order must be such that an revision is imported after all
2022
its (present) parents.
2024
todo = set(self.revisions.keys())
2025
done = self.absent_revisions.copy()
2028
# scan through looking for a revision whose parents
2030
for rev_id in sorted(list(todo)):
2031
rev = self.revisions[rev_id]
2032
parent_ids = set(rev.parent_ids)
2033
if parent_ids.issubset(done):
2034
# can take this one now
2035
order.append(rev_id)
2041
class ConvertBzrDir5To6(Converter):
2042
"""Converts format 5 bzr dirs to format 6."""
2044
def convert(self, to_convert, pb):
2045
"""See Converter.convert()."""
2046
self.bzrdir = to_convert
2048
self.pb.note('starting upgrade from format 5 to 6')
2049
self._convert_to_prefixed()
2050
return BzrDir.open(self.bzrdir.root_transport.base)
2052
def _convert_to_prefixed(self):
2053
from bzrlib.store import TransportStore
2054
self.bzrdir.transport.delete('branch-format')
2055
for store_name in ["weaves", "revision-store"]:
2056
self.pb.note("adding prefixes to %s" % store_name)
2057
store_transport = self.bzrdir.transport.clone(store_name)
2058
store = TransportStore(store_transport, prefixed=True)
2059
for urlfilename in store_transport.list_dir('.'):
2060
filename = urlutils.unescape(urlfilename)
2061
if (filename.endswith(".weave") or
2062
filename.endswith(".gz") or
2063
filename.endswith(".sig")):
2064
file_id = os.path.splitext(filename)[0]
2067
prefix_dir = store.hash_prefix(file_id)
2068
# FIXME keep track of the dirs made RBC 20060121
2070
store_transport.move(filename, prefix_dir + '/' + filename)
2071
except errors.NoSuchFile: # catches missing dirs strangely enough
2072
store_transport.mkdir(prefix_dir)
2073
store_transport.move(filename, prefix_dir + '/' + filename)
2074
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2077
class ConvertBzrDir6ToMeta(Converter):
2078
"""Converts format 6 bzr dirs to metadirs."""
2080
def convert(self, to_convert, pb):
2081
"""See Converter.convert()."""
2082
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2083
from bzrlib.branch import BzrBranchFormat5
2084
self.bzrdir = to_convert
2087
self.total = 20 # the steps we know about
2088
self.garbage_inventories = []
2090
self.pb.note('starting upgrade from format 6 to metadir')
2091
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2092
# its faster to move specific files around than to open and use the apis...
2093
# first off, nuke ancestry.weave, it was never used.
2095
self.step('Removing ancestry.weave')
2096
self.bzrdir.transport.delete('ancestry.weave')
2097
except errors.NoSuchFile:
2099
# find out whats there
2100
self.step('Finding branch files')
2101
last_revision = self.bzrdir.open_branch().last_revision()
2102
bzrcontents = self.bzrdir.transport.list_dir('.')
2103
for name in bzrcontents:
2104
if name.startswith('basis-inventory.'):
2105
self.garbage_inventories.append(name)
2106
# create new directories for repository, working tree and branch
2107
self.dir_mode = self.bzrdir._control_files._dir_mode
2108
self.file_mode = self.bzrdir._control_files._file_mode
2109
repository_names = [('inventory.weave', True),
2110
('revision-store', True),
2112
self.step('Upgrading repository ')
2113
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2114
self.make_lock('repository')
2115
# we hard code the formats here because we are converting into
2116
# the meta format. The meta format upgrader can take this to a
2117
# future format within each component.
2118
self.put_format('repository', RepositoryFormat7())
2119
for entry in repository_names:
2120
self.move_entry('repository', entry)
2122
self.step('Upgrading branch ')
2123
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2124
self.make_lock('branch')
2125
self.put_format('branch', BzrBranchFormat5())
2126
branch_files = [('revision-history', True),
2127
('branch-name', True),
2129
for entry in branch_files:
2130
self.move_entry('branch', entry)
2132
checkout_files = [('pending-merges', True),
2133
('inventory', True),
2134
('stat-cache', False)]
2135
# If a mandatory checkout file is not present, the branch does not have
2136
# a functional checkout. Do not create a checkout in the converted
2138
for name, mandatory in checkout_files:
2139
if mandatory and name not in bzrcontents:
2140
has_checkout = False
2144
if not has_checkout:
2145
self.pb.note('No working tree.')
2146
# If some checkout files are there, we may as well get rid of them.
2147
for name, mandatory in checkout_files:
2148
if name in bzrcontents:
2149
self.bzrdir.transport.delete(name)
2151
from bzrlib.workingtree import WorkingTreeFormat3
2152
self.step('Upgrading working tree')
2153
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2154
self.make_lock('checkout')
2156
'checkout', WorkingTreeFormat3())
2157
self.bzrdir.transport.delete_multi(
2158
self.garbage_inventories, self.pb)
2159
for entry in checkout_files:
2160
self.move_entry('checkout', entry)
2161
if last_revision is not None:
2162
self.bzrdir._control_files.put_utf8(
2163
'checkout/last-revision', last_revision)
2164
self.bzrdir._control_files.put_utf8(
2165
'branch-format', BzrDirMetaFormat1().get_format_string())
2166
return BzrDir.open(self.bzrdir.root_transport.base)
2168
def make_lock(self, name):
2169
"""Make a lock for the new control dir name."""
2170
self.step('Make %s lock' % name)
2171
ld = lockdir.LockDir(self.bzrdir.transport,
2173
file_modebits=self.file_mode,
2174
dir_modebits=self.dir_mode)
2177
def move_entry(self, new_dir, entry):
2178
"""Move then entry name into new_dir."""
2180
mandatory = entry[1]
2181
self.step('Moving %s' % name)
2183
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2184
except errors.NoSuchFile:
2188
def put_format(self, dirname, format):
2189
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2192
class ConvertMetaToMeta(Converter):
2193
"""Converts the components of metadirs."""
2195
def __init__(self, target_format):
2196
"""Create a metadir to metadir converter.
2198
:param target_format: The final metadir format that is desired.
2200
self.target_format = target_format
2202
def convert(self, to_convert, pb):
2203
"""See Converter.convert()."""
2204
self.bzrdir = to_convert
2208
self.step('checking repository format')
2210
repo = self.bzrdir.open_repository()
2211
except errors.NoRepositoryPresent:
2214
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2215
from bzrlib.repository import CopyConverter
2216
self.pb.note('starting repository conversion')
2217
converter = CopyConverter(self.target_format.repository_format)
2218
converter.convert(repo, pb)
2220
branch = self.bzrdir.open_branch()
2221
except errors.NotBranchError:
2224
# TODO: conversions of Branch and Tree should be done by
2225
# InterXFormat lookups
2226
# Avoid circular imports
2227
from bzrlib import branch as _mod_branch
2228
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2229
self.target_format.get_branch_format().__class__ is
2230
_mod_branch.BzrBranchFormat6):
2231
branch_converter = _mod_branch.Converter5to6()
2232
branch_converter.convert(branch)
2234
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2235
except (errors.NoWorkingTree, errors.NotLocalUrl):
2238
# TODO: conversions of Branch and Tree should be done by
2239
# InterXFormat lookups
2240
if (isinstance(tree, workingtree.WorkingTree3) and
2241
not isinstance(tree, workingtree_4.WorkingTree4) and
2242
isinstance(self.target_format.workingtree_format,
2243
workingtree_4.WorkingTreeFormat4)):
2244
workingtree_4.Converter3to4().convert(tree)
2248
# This is not in remote.py because it's small, and needs to be registered.
2249
# Putting it in remote.py creates a circular import problem.
2250
# we can make it a lazy object if the control formats is turned into something
2252
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2253
"""Format representing bzrdirs accessed via a smart server"""
2255
def get_format_description(self):
2256
return 'bzr remote bzrdir'
2259
def probe_transport(klass, transport):
2260
"""Return a RemoteBzrDirFormat object if it looks possible."""
2262
client = transport.get_smart_client()
2263
except (NotImplementedError, AttributeError,
2264
errors.TransportNotPossible):
2265
# no smart server, so not a branch for this format type.
2266
raise errors.NotBranchError(path=transport.base)
2268
# Send a 'hello' request in protocol version one, and decline to
2269
# open it if the server doesn't support our required version (2) so
2270
# that the VFS-based transport will do it.
2271
request = client.get_request()
2272
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2273
server_version = smart_protocol.query_version()
2274
if server_version != 2:
2275
raise errors.NotBranchError(path=transport.base)
2278
def initialize_on_transport(self, transport):
2280
# hand off the request to the smart server
2281
shared_medium = transport.get_shared_medium()
2282
except errors.NoSmartMedium:
2283
# TODO: lookup the local format from a server hint.
2284
local_dir_format = BzrDirMetaFormat1()
2285
return local_dir_format.initialize_on_transport(transport)
2286
client = _SmartClient(shared_medium)
2287
path = client.remote_path_from_transport(transport)
2288
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2290
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2291
return remote.RemoteBzrDir(transport)
2293
def _open(self, transport):
2294
return remote.RemoteBzrDir(transport)
2296
def __eq__(self, other):
2297
if not isinstance(other, RemoteBzrDirFormat):
2299
return self.get_format_description() == other.get_format_description()
2302
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2305
class BzrDirFormatInfo(object):
2307
def __init__(self, native, deprecated, hidden, experimental):
2308
self.deprecated = deprecated
2309
self.native = native
2310
self.hidden = hidden
2311
self.experimental = experimental
2314
class BzrDirFormatRegistry(registry.Registry):
2315
"""Registry of user-selectable BzrDir subformats.
2317
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2318
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2321
def register_metadir(self, key,
2322
repository_format, help, native=True, deprecated=False,
2326
experimental=False):
2327
"""Register a metadir subformat.
2329
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2330
by the Repository format.
2332
:param repository_format: The fully-qualified repository format class
2334
:param branch_format: Fully-qualified branch format class name as
2336
:param tree_format: Fully-qualified tree format class name as
2339
# This should be expanded to support setting WorkingTree and Branch
2340
# formats, once BzrDirMetaFormat1 supports that.
2341
def _load(full_name):
2342
mod_name, factory_name = full_name.rsplit('.', 1)
2344
mod = __import__(mod_name, globals(), locals(),
2346
except ImportError, e:
2347
raise ImportError('failed to load %s: %s' % (full_name, e))
2349
factory = getattr(mod, factory_name)
2350
except AttributeError:
2351
raise AttributeError('no factory %s in module %r'
2356
bd = BzrDirMetaFormat1()
2357
if branch_format is not None:
2358
bd.set_branch_format(_load(branch_format))
2359
if tree_format is not None:
2360
bd.workingtree_format = _load(tree_format)
2361
if repository_format is not None:
2362
bd.repository_format = _load(repository_format)
2364
self.register(key, helper, help, native, deprecated, hidden,
2367
def register(self, key, factory, help, native=True, deprecated=False,
2368
hidden=False, experimental=False):
2369
"""Register a BzrDirFormat factory.
2371
The factory must be a callable that takes one parameter: the key.
2372
It must produce an instance of the BzrDirFormat when called.
2374
This function mainly exists to prevent the info object from being
2377
registry.Registry.register(self, key, factory, help,
2378
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2380
def register_lazy(self, key, module_name, member_name, help, native=True,
2381
deprecated=False, hidden=False, experimental=False):
2382
registry.Registry.register_lazy(self, key, module_name, member_name,
2383
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2385
def set_default(self, key):
2386
"""Set the 'default' key to be a clone of the supplied key.
2388
This method must be called once and only once.
2390
registry.Registry.register(self, 'default', self.get(key),
2391
self.get_help(key), info=self.get_info(key))
2393
def set_default_repository(self, key):
2394
"""Set the FormatRegistry default and Repository default.
2396
This is a transitional method while Repository.set_default_format
2399
if 'default' in self:
2400
self.remove('default')
2401
self.set_default(key)
2402
format = self.get('default')()
2403
assert isinstance(format, BzrDirMetaFormat1)
2405
def make_bzrdir(self, key):
2406
return self.get(key)()
2408
def help_topic(self, topic):
2409
output = textwrap.dedent("""\
2410
These formats can be used for creating branches, working trees, and
2414
default_realkey = None
2415
default_help = self.get_help('default')
2417
for key in self.keys():
2418
if key == 'default':
2420
help = self.get_help(key)
2421
if help == default_help:
2422
default_realkey = key
2424
help_pairs.append((key, help))
2426
def wrapped(key, help, info):
2428
help = '(native) ' + help
2429
return ':%s:\n%s\n\n' % (key,
2430
textwrap.fill(help, initial_indent=' ',
2431
subsequent_indent=' '))
2432
if default_realkey is not None:
2433
output += wrapped(default_realkey, '(default) %s' % default_help,
2434
self.get_info('default'))
2435
deprecated_pairs = []
2436
experimental_pairs = []
2437
for key, help in help_pairs:
2438
info = self.get_info(key)
2441
elif info.deprecated:
2442
deprecated_pairs.append((key, help))
2443
elif info.experimental:
2444
experimental_pairs.append((key, help))
2446
output += wrapped(key, help, info)
2447
if len(experimental_pairs) > 0:
2448
output += "Experimental formats are shown below.\n\n"
2449
for key, help in experimental_pairs:
2450
info = self.get_info(key)
2451
output += wrapped(key, help, info)
2452
if len(deprecated_pairs) > 0:
2453
output += "Deprecated formats are shown below.\n\n"
2454
for key, help in deprecated_pairs:
2455
info = self.get_info(key)
2456
output += wrapped(key, help, info)
2461
format_registry = BzrDirFormatRegistry()
2462
format_registry.register('weave', BzrDirFormat6,
2463
'Pre-0.8 format. Slower than knit and does not'
2464
' support checkouts or shared repositories.',
2466
format_registry.register_metadir('knit',
2467
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2468
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2469
branch_format='bzrlib.branch.BzrBranchFormat5',
2470
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2471
format_registry.register_metadir('metaweave',
2472
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2473
'Transitional format in 0.8. Slower than knit.',
2474
branch_format='bzrlib.branch.BzrBranchFormat5',
2475
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2477
format_registry.register_metadir('dirstate',
2478
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2479
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2480
'above when accessed over the network.',
2481
branch_format='bzrlib.branch.BzrBranchFormat5',
2482
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2483
# directly from workingtree_4 triggers a circular import.
2484
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2486
format_registry.register_metadir('dirstate-tags',
2487
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2488
help='New in 0.15: Fast local operations and improved scaling for '
2489
'network operations. Additionally adds support for tags.'
2490
' Incompatible with bzr < 0.15.',
2491
branch_format='bzrlib.branch.BzrBranchFormat6',
2492
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2494
format_registry.register_metadir('rich-root',
2495
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
2496
help='New in 1.0. Better handling of tree roots. Incompatible with'
2498
branch_format='bzrlib.branch.BzrBranchFormat6',
2499
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2502
format_registry.register_metadir('dirstate-with-subtree',
2503
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2504
help='New in 0.15: Fast local operations and improved scaling for '
2505
'network operations. Additionally adds support for versioning nested '
2506
'bzr branches. Incompatible with bzr < 0.15.',
2507
branch_format='bzrlib.branch.BzrBranchFormat6',
2508
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2511
format_registry.register_metadir('pack-0.92',
2512
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2513
help='New in 0.92: Pack-based format with data compatible with '
2514
'dirstate-tags format repositories. Interoperates with '
2515
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2516
'Previously called knitpack-experimental. '
2517
'For more information, see '
2518
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2519
branch_format='bzrlib.branch.BzrBranchFormat6',
2520
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2523
format_registry.register_metadir('pack-0.92-subtree',
2524
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2525
help='New in 0.92: Pack-based format with data compatible with '
2526
'dirstate-with-subtree format repositories. Interoperates with '
2527
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2528
'Previously called knitpack-experimental. '
2529
'For more information, see '
2530
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2531
branch_format='bzrlib.branch.BzrBranchFormat6',
2532
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2536
format_registry.register_metadir('rich-root-pack',
2537
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
2538
help='New in 1.0: Pack-based format with data compatible with '
2539
'rich-root format repositories. Interoperates with '
2540
'bzr repositories before 0.92 but cannot be read by bzr < 1.0. '
2541
'NOTE: This format is experimental. Before using it, please read '
2542
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2543
branch_format='bzrlib.branch.BzrBranchFormat6',
2544
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2548
format_registry.set_default('dirstate-tags')