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 destroy_repository(self):
249
"""Destroy the repository in this BzrDir"""
250
raise NotImplementedError(self.destroy_repository)
252
def create_branch(self):
253
"""Create a branch in this BzrDir.
255
The bzrdir's format will control what branch format is created.
256
For more control see BranchFormatXX.create(a_bzrdir).
258
raise NotImplementedError(self.create_branch)
260
def destroy_branch(self):
261
"""Destroy the branch in this BzrDir"""
262
raise NotImplementedError(self.destroy_branch)
265
def create_branch_and_repo(base, force_new_repo=False, format=None):
266
"""Create a new BzrDir, Branch and Repository at the url 'base'.
268
This will use the current default BzrDirFormat unless one is
269
specified, and use whatever
270
repository format that that uses via bzrdir.create_branch and
271
create_repository. If a shared repository is available that is used
274
The created Branch object is returned.
276
:param base: The URL to create the branch at.
277
:param force_new_repo: If True a new repository is always created.
278
:param format: If supplied, the format of branch to create. If not
279
supplied, the default is used.
281
bzrdir = BzrDir.create(base, format)
282
bzrdir._find_or_create_repository(force_new_repo)
283
return bzrdir.create_branch()
285
def _find_or_create_repository(self, force_new_repo):
286
"""Create a new repository if needed, returning the repository."""
288
return self.create_repository()
290
return self.find_repository()
291
except errors.NoRepositoryPresent:
292
return self.create_repository()
295
def create_branch_convenience(base, force_new_repo=False,
296
force_new_tree=None, format=None,
297
possible_transports=None):
298
"""Create a new BzrDir, Branch and Repository at the url 'base'.
300
This is a convenience function - it will use an existing repository
301
if possible, can be told explicitly whether to create a working tree or
304
This will use the current default BzrDirFormat unless one is
305
specified, and use whatever
306
repository format that that uses via bzrdir.create_branch and
307
create_repository. If a shared repository is available that is used
308
preferentially. Whatever repository is used, its tree creation policy
311
The created Branch object is returned.
312
If a working tree cannot be made due to base not being a file:// url,
313
no error is raised unless force_new_tree is True, in which case no
314
data is created on disk and NotLocalUrl is raised.
316
:param base: The URL to create the branch at.
317
:param force_new_repo: If True a new repository is always created.
318
:param force_new_tree: If True or False force creation of a tree or
319
prevent such creation respectively.
320
:param format: Override for the bzrdir format to create.
321
:param possible_transports: An optional reusable transports list.
324
# check for non local urls
325
t = get_transport(base, possible_transports)
326
if not isinstance(t, LocalTransport):
327
raise errors.NotLocalUrl(base)
328
bzrdir = BzrDir.create(base, format, possible_transports)
329
repo = bzrdir._find_or_create_repository(force_new_repo)
330
result = bzrdir.create_branch()
331
if force_new_tree or (repo.make_working_trees() and
332
force_new_tree is None):
334
bzrdir.create_workingtree()
335
except errors.NotLocalUrl:
340
@deprecated_function(zero_ninetyone)
341
def create_repository(base, shared=False, format=None):
342
"""Create a new BzrDir and Repository at the url 'base'.
344
If no format is supplied, this will default to the current default
345
BzrDirFormat by default, and use whatever repository format that that
346
uses for bzrdirformat.create_repository.
348
:param shared: Create a shared repository rather than a standalone
350
The Repository object is returned.
352
This must be overridden as an instance method in child classes, where
353
it should take no parameters and construct whatever repository format
354
that child class desires.
356
This method is deprecated, please call create_repository on a bzrdir
359
bzrdir = BzrDir.create(base, format)
360
return bzrdir.create_repository(shared)
363
def create_standalone_workingtree(base, format=None):
364
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
366
'base' must be a local path or a file:// url.
368
This will use the current default BzrDirFormat unless one is
369
specified, and use whatever
370
repository format that that uses for bzrdirformat.create_workingtree,
371
create_branch and create_repository.
373
:param format: Override for the bzrdir format to create.
374
:return: The WorkingTree object.
376
t = get_transport(base)
377
if not isinstance(t, LocalTransport):
378
raise errors.NotLocalUrl(base)
379
bzrdir = BzrDir.create_branch_and_repo(base,
381
format=format).bzrdir
382
return bzrdir.create_workingtree()
384
def create_workingtree(self, revision_id=None, from_branch=None):
385
"""Create a working tree at this BzrDir.
387
:param revision_id: create it as of this revision id.
388
:param from_branch: override bzrdir branch (for lightweight checkouts)
390
raise NotImplementedError(self.create_workingtree)
392
def retire_bzrdir(self, limit=10000):
393
"""Permanently disable the bzrdir.
395
This is done by renaming it to give the user some ability to recover
396
if there was a problem.
398
This will have horrible consequences if anyone has anything locked or
400
:param limit: number of times to retry
405
to_path = '.bzr.retired.%d' % i
406
self.root_transport.rename('.bzr', to_path)
407
note("renamed %s to %s"
408
% (self.root_transport.abspath('.bzr'), to_path))
410
except (errors.TransportError, IOError, errors.PathError):
417
def destroy_workingtree(self):
418
"""Destroy the working tree at this BzrDir.
420
Formats that do not support this may raise UnsupportedOperation.
422
raise NotImplementedError(self.destroy_workingtree)
424
def destroy_workingtree_metadata(self):
425
"""Destroy the control files for the working tree at this BzrDir.
427
The contents of working tree files are not affected.
428
Formats that do not support this may raise UnsupportedOperation.
430
raise NotImplementedError(self.destroy_workingtree_metadata)
432
def find_repository(self):
433
"""Find the repository that should be used.
435
This does not require a branch as we use it to find the repo for
436
new branches as well as to hook existing branches up to their
440
return self.open_repository()
441
except errors.NoRepositoryPresent:
443
next_transport = self.root_transport.clone('..')
445
# find the next containing bzrdir
447
found_bzrdir = BzrDir.open_containing_from_transport(
449
except errors.NotBranchError:
451
raise errors.NoRepositoryPresent(self)
452
# does it have a repository ?
454
repository = found_bzrdir.open_repository()
455
except errors.NoRepositoryPresent:
456
next_transport = found_bzrdir.root_transport.clone('..')
457
if (found_bzrdir.root_transport.base == next_transport.base):
458
# top of the file system
462
if ((found_bzrdir.root_transport.base ==
463
self.root_transport.base) or repository.is_shared()):
466
raise errors.NoRepositoryPresent(self)
467
raise errors.NoRepositoryPresent(self)
469
def get_branch_reference(self):
470
"""Return the referenced URL for the branch in this bzrdir.
472
:raises NotBranchError: If there is no Branch.
473
:return: The URL the branch in this bzrdir references if it is a
474
reference branch, or None for regular branches.
478
def get_branch_transport(self, branch_format):
479
"""Get the transport for use by branch format in this BzrDir.
481
Note that bzr dirs that do not support format strings will raise
482
IncompatibleFormat if the branch format they are given has
483
a format string, and vice versa.
485
If branch_format is None, the transport is returned with no
486
checking. If it is not None, then the returned transport is
487
guaranteed to point to an existing directory ready for use.
489
raise NotImplementedError(self.get_branch_transport)
491
def get_repository_transport(self, repository_format):
492
"""Get the transport for use by repository format in this BzrDir.
494
Note that bzr dirs that do not support format strings will raise
495
IncompatibleFormat if the repository format they are given has
496
a format string, and vice versa.
498
If repository_format is None, the transport is returned with no
499
checking. If it is not None, then the returned transport is
500
guaranteed to point to an existing directory ready for use.
502
raise NotImplementedError(self.get_repository_transport)
504
def get_workingtree_transport(self, tree_format):
505
"""Get the transport for use by workingtree format in this BzrDir.
507
Note that bzr dirs that do not support format strings will raise
508
IncompatibleFormat if the workingtree format they are given has a
509
format string, and vice versa.
511
If workingtree_format is None, the transport is returned with no
512
checking. If it is not None, then the returned transport is
513
guaranteed to point to an existing directory ready for use.
515
raise NotImplementedError(self.get_workingtree_transport)
517
def __init__(self, _transport, _format):
518
"""Initialize a Bzr control dir object.
520
Only really common logic should reside here, concrete classes should be
521
made with varying behaviours.
523
:param _format: the format that is creating this BzrDir instance.
524
:param _transport: the transport this dir is based at.
526
self._format = _format
527
self.transport = _transport.clone('.bzr')
528
self.root_transport = _transport
530
def is_control_filename(self, filename):
531
"""True if filename is the name of a path which is reserved for bzrdir's.
533
:param filename: A filename within the root transport of this bzrdir.
535
This is true IF and ONLY IF the filename is part of the namespace reserved
536
for bzr control dirs. Currently this is the '.bzr' directory in the root
537
of the root_transport. it is expected that plugins will need to extend
538
this in the future - for instance to make bzr talk with svn working
541
# this might be better on the BzrDirFormat class because it refers to
542
# all the possible bzrdir disk formats.
543
# This method is tested via the workingtree is_control_filename tests-
544
# it was extracted from WorkingTree.is_control_filename. If the method's
545
# contract is extended beyond the current trivial implementation, please
546
# add new tests for it to the appropriate place.
547
return filename == '.bzr' or filename.startswith('.bzr/')
549
def needs_format_conversion(self, format=None):
550
"""Return true if this bzrdir needs convert_format run on it.
552
For instance, if the repository format is out of date but the
553
branch and working tree are not, this should return True.
555
:param format: Optional parameter indicating a specific desired
556
format we plan to arrive at.
558
raise NotImplementedError(self.needs_format_conversion)
561
def open_unsupported(base):
562
"""Open a branch which is not supported."""
563
return BzrDir.open(base, _unsupported=True)
566
def open(base, _unsupported=False, possible_transports=None):
567
"""Open an existing bzrdir, rooted at 'base' (url).
569
:param _unsupported: a private parameter to the BzrDir class.
571
t = get_transport(base, possible_transports=possible_transports)
572
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
575
def open_from_transport(transport, _unsupported=False,
576
_server_formats=True):
577
"""Open a bzrdir within a particular directory.
579
:param transport: Transport containing the bzrdir.
580
:param _unsupported: private.
582
base = transport.base
584
def find_format(transport):
585
return transport, BzrDirFormat.find_format(
586
transport, _server_formats=_server_formats)
588
def redirected(transport, e, redirection_notice):
589
qualified_source = e.get_source_url()
590
relpath = transport.relpath(qualified_source)
591
if not e.target.endswith(relpath):
592
# Not redirected to a branch-format, not a branch
593
raise errors.NotBranchError(path=e.target)
594
target = e.target[:-len(relpath)]
595
note('%s is%s redirected to %s',
596
transport.base, e.permanently, target)
597
# Let's try with a new transport
598
# FIXME: If 'transport' has a qualifier, this should
599
# be applied again to the new transport *iff* the
600
# schemes used are the same. Uncomment this code
601
# once the function (and tests) exist.
603
#target = urlutils.copy_url_qualifiers(original, target)
604
return get_transport(target)
607
transport, format = do_catching_redirections(find_format,
610
except errors.TooManyRedirections:
611
raise errors.NotBranchError(base)
613
BzrDir._check_supported(format, _unsupported)
614
return format.open(transport, _found=True)
616
def open_branch(self, unsupported=False):
617
"""Open the branch object at this BzrDir if one is present.
619
If unsupported is True, then no longer supported branch formats can
622
TODO: static convenience version of this?
624
raise NotImplementedError(self.open_branch)
627
def open_containing(url, possible_transports=None):
628
"""Open an existing branch which contains url.
630
:param url: url to search from.
631
See open_containing_from_transport for more detail.
633
transport = get_transport(url, possible_transports)
634
return BzrDir.open_containing_from_transport(transport)
637
def open_containing_from_transport(a_transport):
638
"""Open an existing branch which contains a_transport.base.
640
This probes for a branch at a_transport, and searches upwards from there.
642
Basically we keep looking up until we find the control directory or
643
run into the root. If there isn't one, raises NotBranchError.
644
If there is one and it is either an unrecognised format or an unsupported
645
format, UnknownFormatError or UnsupportedFormatError are raised.
646
If there is one, it is returned, along with the unused portion of url.
648
:return: The BzrDir that contains the path, and a Unicode path
649
for the rest of the URL.
651
# this gets the normalised url back. I.e. '.' -> the full path.
652
url = a_transport.base
655
result = BzrDir.open_from_transport(a_transport)
656
return result, urlutils.unescape(a_transport.relpath(url))
657
except errors.NotBranchError, e:
660
new_t = a_transport.clone('..')
661
except errors.InvalidURLJoin:
662
# reached the root, whatever that may be
663
raise errors.NotBranchError(path=url)
664
if new_t.base == a_transport.base:
665
# reached the root, whatever that may be
666
raise errors.NotBranchError(path=url)
670
def open_containing_tree_or_branch(klass, location):
671
"""Return the branch and working tree contained by a location.
673
Returns (tree, branch, relpath).
674
If there is no tree at containing the location, tree will be None.
675
If there is no branch containing the location, an exception will be
677
relpath is the portion of the path that is contained by the branch.
679
bzrdir, relpath = klass.open_containing(location)
681
tree = bzrdir.open_workingtree()
682
except (errors.NoWorkingTree, errors.NotLocalUrl):
684
branch = bzrdir.open_branch()
687
return tree, branch, relpath
689
def open_repository(self, _unsupported=False):
690
"""Open the repository object at this BzrDir if one is present.
692
This will not follow the Branch object pointer - it's strictly a direct
693
open facility. Most client code should use open_branch().repository to
696
:param _unsupported: a private parameter, not part of the api.
697
TODO: static convenience version of this?
699
raise NotImplementedError(self.open_repository)
701
def open_workingtree(self, _unsupported=False,
702
recommend_upgrade=True, from_branch=None):
703
"""Open the workingtree object at this BzrDir if one is present.
705
:param recommend_upgrade: Optional keyword parameter, when True (the
706
default), emit through the ui module a recommendation that the user
707
upgrade the working tree when the workingtree being opened is old
708
(but still fully supported).
709
:param from_branch: override bzrdir branch (for lightweight checkouts)
711
raise NotImplementedError(self.open_workingtree)
713
def has_branch(self):
714
"""Tell if this bzrdir contains a branch.
716
Note: if you're going to open the branch, you should just go ahead
717
and try, and not ask permission first. (This method just opens the
718
branch and discards it, and that's somewhat expensive.)
723
except errors.NotBranchError:
726
def has_workingtree(self):
727
"""Tell if this bzrdir contains a working tree.
729
This will still raise an exception if the bzrdir has a workingtree that
730
is remote & inaccessible.
732
Note: if you're going to open the working tree, you should just go ahead
733
and try, and not ask permission first. (This method just opens the
734
workingtree and discards it, and that's somewhat expensive.)
737
self.open_workingtree(recommend_upgrade=False)
739
except errors.NoWorkingTree:
742
def _cloning_metadir(self):
743
"""Produce a metadir suitable for cloning with."""
744
result_format = self._format.__class__()
747
branch = self.open_branch()
748
source_repository = branch.repository
749
except errors.NotBranchError:
751
source_repository = self.open_repository()
752
except errors.NoRepositoryPresent:
753
source_repository = None
755
# XXX TODO: This isinstance is here because we have not implemented
756
# the fix recommended in bug # 103195 - to delegate this choice the
758
repo_format = source_repository._format
759
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
760
result_format.repository_format = repo_format
762
# TODO: Couldn't we just probe for the format in these cases,
763
# rather than opening the whole tree? It would be a little
764
# faster. mbp 20070401
765
tree = self.open_workingtree(recommend_upgrade=False)
766
except (errors.NoWorkingTree, errors.NotLocalUrl):
767
result_format.workingtree_format = None
769
result_format.workingtree_format = tree._format.__class__()
770
return result_format, source_repository
772
def cloning_metadir(self):
773
"""Produce a metadir suitable for cloning or sprouting with.
775
These operations may produce workingtrees (yes, even though they're
776
"cloning" something that doesn't have a tree), so a viable workingtree
777
format must be selected.
779
format, repository = self._cloning_metadir()
780
if format._workingtree_format is None:
781
if repository is None:
783
tree_format = repository._format._matchingbzrdir.workingtree_format
784
format.workingtree_format = tree_format.__class__()
787
def checkout_metadir(self):
788
return self.cloning_metadir()
790
def sprout(self, url, revision_id=None, force_new_repo=False,
791
recurse='down', possible_transports=None,
792
accelerator_tree=None):
793
"""Create a copy of this bzrdir prepared for use as a new line of
796
If url's last component does not exist, it will be created.
798
Attributes related to the identity of the source branch like
799
branch nickname will be cleaned, a working tree is created
800
whether one existed before or not; and a local branch is always
803
if revision_id is not None, then the clone operation may tune
804
itself to download less data.
806
target_transport = get_transport(url, possible_transports)
807
target_transport.ensure_base()
808
cloning_format = self.cloning_metadir()
809
result = cloning_format.initialize_on_transport(target_transport)
811
source_branch = self.open_branch()
812
source_repository = source_branch.repository
813
except errors.NotBranchError:
816
source_repository = self.open_repository()
817
except errors.NoRepositoryPresent:
818
source_repository = None
823
result_repo = result.find_repository()
824
except errors.NoRepositoryPresent:
826
if source_repository is None and result_repo is not None:
828
elif source_repository is None and result_repo is None:
829
# no repo available, make a new one
830
result.create_repository()
831
elif source_repository is not None and result_repo is None:
832
# have source, and want to make a new target repo
833
result_repo = source_repository.sprout(result,
834
revision_id=revision_id)
836
# fetch needed content into target.
837
if source_repository is not None:
839
# source_repository.copy_content_into(result_repo,
840
# revision_id=revision_id)
841
# so we can override the copy method
842
result_repo.fetch(source_repository, revision_id=revision_id)
843
if source_branch is not None:
844
source_branch.sprout(result, revision_id=revision_id)
846
result.create_branch()
847
if isinstance(target_transport, LocalTransport) and (
848
result_repo is None or result_repo.make_working_trees()):
849
wt = result.create_workingtree(accelerator_tree=accelerator_tree)
852
if wt.path2id('') is None:
853
if self_wt is not None:
854
wt.set_root_id(self_wt.get_root_id())
859
if recurse == 'down':
861
basis = wt.basis_tree()
863
subtrees = basis.iter_references()
864
recurse_branch = wt.branch
865
elif source_branch is not None:
866
basis = source_branch.basis_tree()
868
subtrees = basis.iter_references()
869
recurse_branch = source_branch
874
for path, file_id in subtrees:
875
target = urlutils.join(url, urlutils.escape(path))
876
sublocation = source_branch.reference_parent(file_id, path)
877
sublocation.bzrdir.sprout(target,
878
basis.get_reference_revision(file_id, path),
879
force_new_repo=force_new_repo, recurse=recurse)
881
if basis is not None:
886
class BzrDirPreSplitOut(BzrDir):
887
"""A common class for the all-in-one formats."""
889
def __init__(self, _transport, _format):
890
"""See BzrDir.__init__."""
891
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
892
assert self._format._lock_class == lockable_files.TransportLock
893
assert self._format._lock_file_name == 'branch-lock'
894
self._control_files = lockable_files.LockableFiles(
895
self.get_branch_transport(None),
896
self._format._lock_file_name,
897
self._format._lock_class)
899
def break_lock(self):
900
"""Pre-splitout bzrdirs do not suffer from stale locks."""
901
raise NotImplementedError(self.break_lock)
903
def clone(self, url, revision_id=None, force_new_repo=False):
904
"""See BzrDir.clone()."""
905
from bzrlib.workingtree import WorkingTreeFormat2
907
result = self._format._initialize_for_clone(url)
908
self.open_repository().clone(result, revision_id=revision_id)
909
from_branch = self.open_branch()
910
from_branch.clone(result, revision_id=revision_id)
912
self.open_workingtree().clone(result)
913
except errors.NotLocalUrl:
914
# make a new one, this format always has to have one.
916
WorkingTreeFormat2().initialize(result)
917
except errors.NotLocalUrl:
918
# but we cannot do it for remote trees.
919
to_branch = result.open_branch()
920
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
923
def create_branch(self):
924
"""See BzrDir.create_branch."""
925
return self.open_branch()
927
def destroy_branch(self):
928
"""See BzrDir.destroy_branch."""
929
raise errors.UnsupportedOperation(self.destroy_branch, self)
931
def create_repository(self, shared=False):
932
"""See BzrDir.create_repository."""
934
raise errors.IncompatibleFormat('shared repository', self._format)
935
return self.open_repository()
937
def destroy_repository(self):
938
"""See BzrDir.destroy_repository."""
939
raise errors.UnsupportedOperation(self.destroy_repository, self)
941
def create_workingtree(self, revision_id=None, from_branch=None,
942
accelerator_tree=None):
943
"""See BzrDir.create_workingtree."""
944
# this looks buggy but is not -really-
945
# because this format creates the workingtree when the bzrdir is
947
# clone and sprout will have set the revision_id
948
# and that will have set it for us, its only
949
# specific uses of create_workingtree in isolation
950
# that can do wonky stuff here, and that only
951
# happens for creating checkouts, which cannot be
952
# done on this format anyway. So - acceptable wart.
953
result = self.open_workingtree(recommend_upgrade=False)
954
if revision_id is not None:
955
if revision_id == _mod_revision.NULL_REVISION:
956
result.set_parent_ids([])
958
result.set_parent_ids([revision_id])
961
def destroy_workingtree(self):
962
"""See BzrDir.destroy_workingtree."""
963
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
965
def destroy_workingtree_metadata(self):
966
"""See BzrDir.destroy_workingtree_metadata."""
967
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
970
def get_branch_transport(self, branch_format):
971
"""See BzrDir.get_branch_transport()."""
972
if branch_format is None:
973
return self.transport
975
branch_format.get_format_string()
976
except NotImplementedError:
977
return self.transport
978
raise errors.IncompatibleFormat(branch_format, self._format)
980
def get_repository_transport(self, repository_format):
981
"""See BzrDir.get_repository_transport()."""
982
if repository_format is None:
983
return self.transport
985
repository_format.get_format_string()
986
except NotImplementedError:
987
return self.transport
988
raise errors.IncompatibleFormat(repository_format, self._format)
990
def get_workingtree_transport(self, workingtree_format):
991
"""See BzrDir.get_workingtree_transport()."""
992
if workingtree_format is None:
993
return self.transport
995
workingtree_format.get_format_string()
996
except NotImplementedError:
997
return self.transport
998
raise errors.IncompatibleFormat(workingtree_format, self._format)
1000
def needs_format_conversion(self, format=None):
1001
"""See BzrDir.needs_format_conversion()."""
1002
# if the format is not the same as the system default,
1003
# an upgrade is needed.
1005
format = BzrDirFormat.get_default_format()
1006
return not isinstance(self._format, format.__class__)
1008
def open_branch(self, unsupported=False):
1009
"""See BzrDir.open_branch."""
1010
from bzrlib.branch import BzrBranchFormat4
1011
format = BzrBranchFormat4()
1012
self._check_supported(format, unsupported)
1013
return format.open(self, _found=True)
1015
def sprout(self, url, revision_id=None, force_new_repo=False,
1016
possible_transports=None, accelerator_tree=None):
1017
"""See BzrDir.sprout()."""
1018
from bzrlib.workingtree import WorkingTreeFormat2
1019
self._make_tail(url)
1020
result = self._format._initialize_for_clone(url)
1022
self.open_repository().clone(result, revision_id=revision_id)
1023
except errors.NoRepositoryPresent:
1026
self.open_branch().sprout(result, revision_id=revision_id)
1027
except errors.NotBranchError:
1029
# we always want a working tree
1030
WorkingTreeFormat2().initialize(result,
1031
accelerator_tree=accelerator_tree)
1035
class BzrDir4(BzrDirPreSplitOut):
1036
"""A .bzr version 4 control object.
1038
This is a deprecated format and may be removed after sept 2006.
1041
def create_repository(self, shared=False):
1042
"""See BzrDir.create_repository."""
1043
return self._format.repository_format.initialize(self, shared)
1045
def needs_format_conversion(self, format=None):
1046
"""Format 4 dirs are always in need of conversion."""
1049
def open_repository(self):
1050
"""See BzrDir.open_repository."""
1051
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1052
return RepositoryFormat4().open(self, _found=True)
1055
class BzrDir5(BzrDirPreSplitOut):
1056
"""A .bzr version 5 control object.
1058
This is a deprecated format and may be removed after sept 2006.
1061
def open_repository(self):
1062
"""See BzrDir.open_repository."""
1063
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1064
return RepositoryFormat5().open(self, _found=True)
1066
def open_workingtree(self, _unsupported=False,
1067
recommend_upgrade=True):
1068
"""See BzrDir.create_workingtree."""
1069
from bzrlib.workingtree import WorkingTreeFormat2
1070
wt_format = WorkingTreeFormat2()
1071
# we don't warn here about upgrades; that ought to be handled for the
1073
return wt_format.open(self, _found=True)
1076
class BzrDir6(BzrDirPreSplitOut):
1077
"""A .bzr version 6 control object.
1079
This is a deprecated format and may be removed after sept 2006.
1082
def open_repository(self):
1083
"""See BzrDir.open_repository."""
1084
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1085
return RepositoryFormat6().open(self, _found=True)
1087
def open_workingtree(self, _unsupported=False,
1088
recommend_upgrade=True):
1089
"""See BzrDir.create_workingtree."""
1090
# we don't warn here about upgrades; that ought to be handled for the
1092
from bzrlib.workingtree import WorkingTreeFormat2
1093
return WorkingTreeFormat2().open(self, _found=True)
1096
class BzrDirMeta1(BzrDir):
1097
"""A .bzr meta version 1 control object.
1099
This is the first control object where the
1100
individual aspects are really split out: there are separate repository,
1101
workingtree and branch subdirectories and any subset of the three can be
1102
present within a BzrDir.
1105
def can_convert_format(self):
1106
"""See BzrDir.can_convert_format()."""
1109
def create_branch(self):
1110
"""See BzrDir.create_branch."""
1111
return self._format.get_branch_format().initialize(self)
1113
def destroy_branch(self):
1114
"""See BzrDir.create_branch."""
1115
self.transport.delete_tree('branch')
1117
def create_repository(self, shared=False):
1118
"""See BzrDir.create_repository."""
1119
return self._format.repository_format.initialize(self, shared)
1121
def destroy_repository(self):
1122
"""See BzrDir.destroy_repository."""
1123
self.transport.delete_tree('repository')
1125
def create_workingtree(self, revision_id=None, from_branch=None,
1126
accelerator_tree=None):
1127
"""See BzrDir.create_workingtree."""
1128
return self._format.workingtree_format.initialize(
1129
self, revision_id, from_branch=from_branch,
1130
accelerator_tree=accelerator_tree)
1132
def destroy_workingtree(self):
1133
"""See BzrDir.destroy_workingtree."""
1134
wt = self.open_workingtree(recommend_upgrade=False)
1135
repository = wt.branch.repository
1136
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1137
wt.revert(old_tree=empty)
1138
self.destroy_workingtree_metadata()
1140
def destroy_workingtree_metadata(self):
1141
self.transport.delete_tree('checkout')
1143
def find_branch_format(self):
1144
"""Find the branch 'format' for this bzrdir.
1146
This might be a synthetic object for e.g. RemoteBranch and SVN.
1148
from bzrlib.branch import BranchFormat
1149
return BranchFormat.find_format(self)
1151
def _get_mkdir_mode(self):
1152
"""Figure out the mode to use when creating a bzrdir subdir."""
1153
temp_control = lockable_files.LockableFiles(self.transport, '',
1154
lockable_files.TransportLock)
1155
return temp_control._dir_mode
1157
def get_branch_reference(self):
1158
"""See BzrDir.get_branch_reference()."""
1159
from bzrlib.branch import BranchFormat
1160
format = BranchFormat.find_format(self)
1161
return format.get_reference(self)
1163
def get_branch_transport(self, branch_format):
1164
"""See BzrDir.get_branch_transport()."""
1165
if branch_format is None:
1166
return self.transport.clone('branch')
1168
branch_format.get_format_string()
1169
except NotImplementedError:
1170
raise errors.IncompatibleFormat(branch_format, self._format)
1172
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1173
except errors.FileExists:
1175
return self.transport.clone('branch')
1177
def get_repository_transport(self, repository_format):
1178
"""See BzrDir.get_repository_transport()."""
1179
if repository_format is None:
1180
return self.transport.clone('repository')
1182
repository_format.get_format_string()
1183
except NotImplementedError:
1184
raise errors.IncompatibleFormat(repository_format, self._format)
1186
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1187
except errors.FileExists:
1189
return self.transport.clone('repository')
1191
def get_workingtree_transport(self, workingtree_format):
1192
"""See BzrDir.get_workingtree_transport()."""
1193
if workingtree_format is None:
1194
return self.transport.clone('checkout')
1196
workingtree_format.get_format_string()
1197
except NotImplementedError:
1198
raise errors.IncompatibleFormat(workingtree_format, self._format)
1200
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1201
except errors.FileExists:
1203
return self.transport.clone('checkout')
1205
def needs_format_conversion(self, format=None):
1206
"""See BzrDir.needs_format_conversion()."""
1208
format = BzrDirFormat.get_default_format()
1209
if not isinstance(self._format, format.__class__):
1210
# it is not a meta dir format, conversion is needed.
1212
# we might want to push this down to the repository?
1214
if not isinstance(self.open_repository()._format,
1215
format.repository_format.__class__):
1216
# the repository needs an upgrade.
1218
except errors.NoRepositoryPresent:
1221
if not isinstance(self.open_branch()._format,
1222
format.get_branch_format().__class__):
1223
# the branch needs an upgrade.
1225
except errors.NotBranchError:
1228
my_wt = self.open_workingtree(recommend_upgrade=False)
1229
if not isinstance(my_wt._format,
1230
format.workingtree_format.__class__):
1231
# the workingtree needs an upgrade.
1233
except (errors.NoWorkingTree, errors.NotLocalUrl):
1237
def open_branch(self, unsupported=False):
1238
"""See BzrDir.open_branch."""
1239
format = self.find_branch_format()
1240
self._check_supported(format, unsupported)
1241
return format.open(self, _found=True)
1243
def open_repository(self, unsupported=False):
1244
"""See BzrDir.open_repository."""
1245
from bzrlib.repository import RepositoryFormat
1246
format = RepositoryFormat.find_format(self)
1247
self._check_supported(format, unsupported)
1248
return format.open(self, _found=True)
1250
def open_workingtree(self, unsupported=False,
1251
recommend_upgrade=True):
1252
"""See BzrDir.open_workingtree."""
1253
from bzrlib.workingtree import WorkingTreeFormat
1254
format = WorkingTreeFormat.find_format(self)
1255
self._check_supported(format, unsupported,
1257
basedir=self.root_transport.base)
1258
return format.open(self, _found=True)
1261
class BzrDirFormat(object):
1262
"""An encapsulation of the initialization and open routines for a format.
1264
Formats provide three things:
1265
* An initialization routine,
1269
Formats are placed in a dict by their format string for reference
1270
during bzrdir opening. These should be subclasses of BzrDirFormat
1273
Once a format is deprecated, just deprecate the initialize and open
1274
methods on the format class. Do not deprecate the object, as the
1275
object will be created every system load.
1278
_default_format = None
1279
"""The default format used for new .bzr dirs."""
1282
"""The known formats."""
1284
_control_formats = []
1285
"""The registered control formats - .bzr, ....
1287
This is a list of BzrDirFormat objects.
1290
_control_server_formats = []
1291
"""The registered control server formats, e.g. RemoteBzrDirs.
1293
This is a list of BzrDirFormat objects.
1296
_lock_file_name = 'branch-lock'
1298
# _lock_class must be set in subclasses to the lock type, typ.
1299
# TransportLock or LockDir
1302
def find_format(klass, transport, _server_formats=True):
1303
"""Return the format present at transport."""
1305
formats = klass._control_server_formats + klass._control_formats
1307
formats = klass._control_formats
1308
for format in formats:
1310
return format.probe_transport(transport)
1311
except errors.NotBranchError:
1312
# this format does not find a control dir here.
1314
raise errors.NotBranchError(path=transport.base)
1317
def probe_transport(klass, transport):
1318
"""Return the .bzrdir style format present in a directory."""
1320
format_string = transport.get(".bzr/branch-format").read()
1321
except errors.NoSuchFile:
1322
raise errors.NotBranchError(path=transport.base)
1325
return klass._formats[format_string]
1327
raise errors.UnknownFormatError(format=format_string)
1330
def get_default_format(klass):
1331
"""Return the current default format."""
1332
return klass._default_format
1334
def get_format_string(self):
1335
"""Return the ASCII format string that identifies this format."""
1336
raise NotImplementedError(self.get_format_string)
1338
def get_format_description(self):
1339
"""Return the short description for this format."""
1340
raise NotImplementedError(self.get_format_description)
1342
def get_converter(self, format=None):
1343
"""Return the converter to use to convert bzrdirs needing converts.
1345
This returns a bzrlib.bzrdir.Converter object.
1347
This should return the best upgrader to step this format towards the
1348
current default format. In the case of plugins we can/should provide
1349
some means for them to extend the range of returnable converters.
1351
:param format: Optional format to override the default format of the
1354
raise NotImplementedError(self.get_converter)
1356
def initialize(self, url, possible_transports=None):
1357
"""Create a bzr control dir at this url and return an opened copy.
1359
Subclasses should typically override initialize_on_transport
1360
instead of this method.
1362
return self.initialize_on_transport(get_transport(url,
1363
possible_transports))
1365
def initialize_on_transport(self, transport):
1366
"""Initialize a new bzrdir in the base directory of a Transport."""
1367
# Since we don't have a .bzr directory, inherit the
1368
# mode from the root directory
1369
temp_control = lockable_files.LockableFiles(transport,
1370
'', lockable_files.TransportLock)
1371
temp_control._transport.mkdir('.bzr',
1372
# FIXME: RBC 20060121 don't peek under
1374
mode=temp_control._dir_mode)
1375
if sys.platform == 'win32' and isinstance(transport, LocalTransport):
1376
win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1377
file_mode = temp_control._file_mode
1379
mutter('created control directory in ' + transport.base)
1380
control = transport.clone('.bzr')
1381
utf8_files = [('README',
1382
"This is a Bazaar-NG control directory.\n"
1383
"Do not change any files in this directory.\n"),
1384
('branch-format', self.get_format_string()),
1386
# NB: no need to escape relative paths that are url safe.
1387
control_files = lockable_files.LockableFiles(control,
1388
self._lock_file_name, self._lock_class)
1389
control_files.create_lock()
1390
control_files.lock_write()
1392
for file, content in utf8_files:
1393
control_files.put_utf8(file, content)
1395
control_files.unlock()
1396
return self.open(transport, _found=True)
1398
def is_supported(self):
1399
"""Is this format supported?
1401
Supported formats must be initializable and openable.
1402
Unsupported formats may not support initialization or committing or
1403
some other features depending on the reason for not being supported.
1407
def same_model(self, target_format):
1408
return (self.repository_format.rich_root_data ==
1409
target_format.rich_root_data)
1412
def known_formats(klass):
1413
"""Return all the known formats.
1415
Concrete formats should override _known_formats.
1417
# There is double indirection here to make sure that control
1418
# formats used by more than one dir format will only be probed
1419
# once. This can otherwise be quite expensive for remote connections.
1421
for format in klass._control_formats:
1422
result.update(format._known_formats())
1426
def _known_formats(klass):
1427
"""Return the known format instances for this control format."""
1428
return set(klass._formats.values())
1430
def open(self, transport, _found=False):
1431
"""Return an instance of this format for the dir transport points at.
1433
_found is a private parameter, do not use it.
1436
found_format = BzrDirFormat.find_format(transport)
1437
if not isinstance(found_format, self.__class__):
1438
raise AssertionError("%s was asked to open %s, but it seems to need "
1440
% (self, transport, found_format))
1441
return self._open(transport)
1443
def _open(self, transport):
1444
"""Template method helper for opening BzrDirectories.
1446
This performs the actual open and any additional logic or parameter
1449
raise NotImplementedError(self._open)
1452
def register_format(klass, format):
1453
klass._formats[format.get_format_string()] = format
1456
def register_control_format(klass, format):
1457
"""Register a format that does not use '.bzr' for its control dir.
1459
TODO: This should be pulled up into a 'ControlDirFormat' base class
1460
which BzrDirFormat can inherit from, and renamed to register_format
1461
there. It has been done without that for now for simplicity of
1464
klass._control_formats.append(format)
1467
def register_control_server_format(klass, format):
1468
"""Register a control format for client-server environments.
1470
These formats will be tried before ones registered with
1471
register_control_format. This gives implementations that decide to the
1472
chance to grab it before anything looks at the contents of the format
1475
klass._control_server_formats.append(format)
1478
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1479
def set_default_format(klass, format):
1480
klass._set_default_format(format)
1483
def _set_default_format(klass, format):
1484
"""Set default format (for testing behavior of defaults only)"""
1485
klass._default_format = format
1489
return self.get_format_string().rstrip()
1492
def unregister_format(klass, format):
1493
assert klass._formats[format.get_format_string()] is format
1494
del klass._formats[format.get_format_string()]
1497
def unregister_control_format(klass, format):
1498
klass._control_formats.remove(format)
1501
class BzrDirFormat4(BzrDirFormat):
1502
"""Bzr dir format 4.
1504
This format is a combined format for working tree, branch and repository.
1506
- Format 1 working trees [always]
1507
- Format 4 branches [always]
1508
- Format 4 repositories [always]
1510
This format is deprecated: it indexes texts using a text it which is
1511
removed in format 5; write support for this format has been removed.
1514
_lock_class = lockable_files.TransportLock
1516
def get_format_string(self):
1517
"""See BzrDirFormat.get_format_string()."""
1518
return "Bazaar-NG branch, format 0.0.4\n"
1520
def get_format_description(self):
1521
"""See BzrDirFormat.get_format_description()."""
1522
return "All-in-one format 4"
1524
def get_converter(self, format=None):
1525
"""See BzrDirFormat.get_converter()."""
1526
# there is one and only one upgrade path here.
1527
return ConvertBzrDir4To5()
1529
def initialize_on_transport(self, transport):
1530
"""Format 4 branches cannot be created."""
1531
raise errors.UninitializableFormat(self)
1533
def is_supported(self):
1534
"""Format 4 is not supported.
1536
It is not supported because the model changed from 4 to 5 and the
1537
conversion logic is expensive - so doing it on the fly was not
1542
def _open(self, transport):
1543
"""See BzrDirFormat._open."""
1544
return BzrDir4(transport, self)
1546
def __return_repository_format(self):
1547
"""Circular import protection."""
1548
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1549
return RepositoryFormat4()
1550
repository_format = property(__return_repository_format)
1553
class BzrDirFormat5(BzrDirFormat):
1554
"""Bzr control format 5.
1556
This format is a combined format for working tree, branch and repository.
1558
- Format 2 working trees [always]
1559
- Format 4 branches [always]
1560
- Format 5 repositories [always]
1561
Unhashed stores in the repository.
1564
_lock_class = lockable_files.TransportLock
1566
def get_format_string(self):
1567
"""See BzrDirFormat.get_format_string()."""
1568
return "Bazaar-NG branch, format 5\n"
1570
def get_format_description(self):
1571
"""See BzrDirFormat.get_format_description()."""
1572
return "All-in-one format 5"
1574
def get_converter(self, format=None):
1575
"""See BzrDirFormat.get_converter()."""
1576
# there is one and only one upgrade path here.
1577
return ConvertBzrDir5To6()
1579
def _initialize_for_clone(self, url):
1580
return self.initialize_on_transport(get_transport(url), _cloning=True)
1582
def initialize_on_transport(self, transport, _cloning=False):
1583
"""Format 5 dirs always have working tree, branch and repository.
1585
Except when they are being cloned.
1587
from bzrlib.branch import BzrBranchFormat4
1588
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1589
from bzrlib.workingtree import WorkingTreeFormat2
1590
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1591
RepositoryFormat5().initialize(result, _internal=True)
1593
branch = BzrBranchFormat4().initialize(result)
1595
WorkingTreeFormat2().initialize(result)
1596
except errors.NotLocalUrl:
1597
# Even though we can't access the working tree, we need to
1598
# create its control files.
1599
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1602
def _open(self, transport):
1603
"""See BzrDirFormat._open."""
1604
return BzrDir5(transport, self)
1606
def __return_repository_format(self):
1607
"""Circular import protection."""
1608
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1609
return RepositoryFormat5()
1610
repository_format = property(__return_repository_format)
1613
class BzrDirFormat6(BzrDirFormat):
1614
"""Bzr control format 6.
1616
This format is a combined format for working tree, branch and repository.
1618
- Format 2 working trees [always]
1619
- Format 4 branches [always]
1620
- Format 6 repositories [always]
1623
_lock_class = lockable_files.TransportLock
1625
def get_format_string(self):
1626
"""See BzrDirFormat.get_format_string()."""
1627
return "Bazaar-NG branch, format 6\n"
1629
def get_format_description(self):
1630
"""See BzrDirFormat.get_format_description()."""
1631
return "All-in-one format 6"
1633
def get_converter(self, format=None):
1634
"""See BzrDirFormat.get_converter()."""
1635
# there is one and only one upgrade path here.
1636
return ConvertBzrDir6ToMeta()
1638
def _initialize_for_clone(self, url):
1639
return self.initialize_on_transport(get_transport(url), _cloning=True)
1641
def initialize_on_transport(self, transport, _cloning=False):
1642
"""Format 6 dirs always have working tree, branch and repository.
1644
Except when they are being cloned.
1646
from bzrlib.branch import BzrBranchFormat4
1647
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1648
from bzrlib.workingtree import WorkingTreeFormat2
1649
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1650
RepositoryFormat6().initialize(result, _internal=True)
1652
branch = BzrBranchFormat4().initialize(result)
1654
WorkingTreeFormat2().initialize(result)
1655
except errors.NotLocalUrl:
1656
# Even though we can't access the working tree, we need to
1657
# create its control files.
1658
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1661
def _open(self, transport):
1662
"""See BzrDirFormat._open."""
1663
return BzrDir6(transport, self)
1665
def __return_repository_format(self):
1666
"""Circular import protection."""
1667
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1668
return RepositoryFormat6()
1669
repository_format = property(__return_repository_format)
1672
class BzrDirMetaFormat1(BzrDirFormat):
1673
"""Bzr meta control format 1
1675
This is the first format with split out working tree, branch and repository
1678
- Format 3 working trees [optional]
1679
- Format 5 branches [optional]
1680
- Format 7 repositories [optional]
1683
_lock_class = lockdir.LockDir
1686
self._workingtree_format = None
1687
self._branch_format = None
1689
def __eq__(self, other):
1690
if other.__class__ is not self.__class__:
1692
if other.repository_format != self.repository_format:
1694
if other.workingtree_format != self.workingtree_format:
1698
def __ne__(self, other):
1699
return not self == other
1701
def get_branch_format(self):
1702
if self._branch_format is None:
1703
from bzrlib.branch import BranchFormat
1704
self._branch_format = BranchFormat.get_default_format()
1705
return self._branch_format
1707
def set_branch_format(self, format):
1708
self._branch_format = format
1710
def get_converter(self, format=None):
1711
"""See BzrDirFormat.get_converter()."""
1713
format = BzrDirFormat.get_default_format()
1714
if not isinstance(self, format.__class__):
1715
# converting away from metadir is not implemented
1716
raise NotImplementedError(self.get_converter)
1717
return ConvertMetaToMeta(format)
1719
def get_format_string(self):
1720
"""See BzrDirFormat.get_format_string()."""
1721
return "Bazaar-NG meta directory, format 1\n"
1723
def get_format_description(self):
1724
"""See BzrDirFormat.get_format_description()."""
1725
return "Meta directory format 1"
1727
def _open(self, transport):
1728
"""See BzrDirFormat._open."""
1729
return BzrDirMeta1(transport, self)
1731
def __return_repository_format(self):
1732
"""Circular import protection."""
1733
if getattr(self, '_repository_format', None):
1734
return self._repository_format
1735
from bzrlib.repository import RepositoryFormat
1736
return RepositoryFormat.get_default_format()
1738
def __set_repository_format(self, value):
1739
"""Allow changing the repository format for metadir formats."""
1740
self._repository_format = value
1742
repository_format = property(__return_repository_format, __set_repository_format)
1744
def __get_workingtree_format(self):
1745
if self._workingtree_format is None:
1746
from bzrlib.workingtree import WorkingTreeFormat
1747
self._workingtree_format = WorkingTreeFormat.get_default_format()
1748
return self._workingtree_format
1750
def __set_workingtree_format(self, wt_format):
1751
self._workingtree_format = wt_format
1753
workingtree_format = property(__get_workingtree_format,
1754
__set_workingtree_format)
1757
# Register bzr control format
1758
BzrDirFormat.register_control_format(BzrDirFormat)
1760
# Register bzr formats
1761
BzrDirFormat.register_format(BzrDirFormat4())
1762
BzrDirFormat.register_format(BzrDirFormat5())
1763
BzrDirFormat.register_format(BzrDirFormat6())
1764
__default_format = BzrDirMetaFormat1()
1765
BzrDirFormat.register_format(__default_format)
1766
BzrDirFormat._default_format = __default_format
1769
class Converter(object):
1770
"""Converts a disk format object from one format to another."""
1772
def convert(self, to_convert, pb):
1773
"""Perform the conversion of to_convert, giving feedback via pb.
1775
:param to_convert: The disk object to convert.
1776
:param pb: a progress bar to use for progress information.
1779
def step(self, message):
1780
"""Update the pb by a step."""
1782
self.pb.update(message, self.count, self.total)
1785
class ConvertBzrDir4To5(Converter):
1786
"""Converts format 4 bzr dirs to format 5."""
1789
super(ConvertBzrDir4To5, self).__init__()
1790
self.converted_revs = set()
1791
self.absent_revisions = set()
1795
def convert(self, to_convert, pb):
1796
"""See Converter.convert()."""
1797
self.bzrdir = to_convert
1799
self.pb.note('starting upgrade from format 4 to 5')
1800
if isinstance(self.bzrdir.transport, LocalTransport):
1801
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1802
self._convert_to_weaves()
1803
return BzrDir.open(self.bzrdir.root_transport.base)
1805
def _convert_to_weaves(self):
1806
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1809
stat = self.bzrdir.transport.stat('weaves')
1810
if not S_ISDIR(stat.st_mode):
1811
self.bzrdir.transport.delete('weaves')
1812
self.bzrdir.transport.mkdir('weaves')
1813
except errors.NoSuchFile:
1814
self.bzrdir.transport.mkdir('weaves')
1815
# deliberately not a WeaveFile as we want to build it up slowly.
1816
self.inv_weave = Weave('inventory')
1817
# holds in-memory weaves for all files
1818
self.text_weaves = {}
1819
self.bzrdir.transport.delete('branch-format')
1820
self.branch = self.bzrdir.open_branch()
1821
self._convert_working_inv()
1822
rev_history = self.branch.revision_history()
1823
# to_read is a stack holding the revisions we still need to process;
1824
# appending to it adds new highest-priority revisions
1825
self.known_revisions = set(rev_history)
1826
self.to_read = rev_history[-1:]
1828
rev_id = self.to_read.pop()
1829
if (rev_id not in self.revisions
1830
and rev_id not in self.absent_revisions):
1831
self._load_one_rev(rev_id)
1833
to_import = self._make_order()
1834
for i, rev_id in enumerate(to_import):
1835
self.pb.update('converting revision', i, len(to_import))
1836
self._convert_one_rev(rev_id)
1838
self._write_all_weaves()
1839
self._write_all_revs()
1840
self.pb.note('upgraded to weaves:')
1841
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1842
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1843
self.pb.note(' %6d texts', self.text_count)
1844
self._cleanup_spare_files_after_format4()
1845
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1847
def _cleanup_spare_files_after_format4(self):
1848
# FIXME working tree upgrade foo.
1849
for n in 'merged-patches', 'pending-merged-patches':
1851
## assert os.path.getsize(p) == 0
1852
self.bzrdir.transport.delete(n)
1853
except errors.NoSuchFile:
1855
self.bzrdir.transport.delete_tree('inventory-store')
1856
self.bzrdir.transport.delete_tree('text-store')
1858
def _convert_working_inv(self):
1859
inv = xml4.serializer_v4.read_inventory(
1860
self.branch.control_files.get('inventory'))
1861
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
1862
# FIXME inventory is a working tree change.
1863
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1865
def _write_all_weaves(self):
1866
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1867
weave_transport = self.bzrdir.transport.clone('weaves')
1868
weaves = WeaveStore(weave_transport, prefixed=False)
1869
transaction = WriteTransaction()
1873
for file_id, file_weave in self.text_weaves.items():
1874
self.pb.update('writing weave', i, len(self.text_weaves))
1875
weaves._put_weave(file_id, file_weave, transaction)
1877
self.pb.update('inventory', 0, 1)
1878
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1879
self.pb.update('inventory', 1, 1)
1883
def _write_all_revs(self):
1884
"""Write all revisions out in new form."""
1885
self.bzrdir.transport.delete_tree('revision-store')
1886
self.bzrdir.transport.mkdir('revision-store')
1887
revision_transport = self.bzrdir.transport.clone('revision-store')
1889
_revision_store = TextRevisionStore(TextStore(revision_transport,
1893
transaction = WriteTransaction()
1894
for i, rev_id in enumerate(self.converted_revs):
1895
self.pb.update('write revision', i, len(self.converted_revs))
1896
_revision_store.add_revision(self.revisions[rev_id], transaction)
1900
def _load_one_rev(self, rev_id):
1901
"""Load a revision object into memory.
1903
Any parents not either loaded or abandoned get queued to be
1905
self.pb.update('loading revision',
1906
len(self.revisions),
1907
len(self.known_revisions))
1908
if not self.branch.repository.has_revision(rev_id):
1910
self.pb.note('revision {%s} not present in branch; '
1911
'will be converted as a ghost',
1913
self.absent_revisions.add(rev_id)
1915
rev = self.branch.repository._revision_store.get_revision(rev_id,
1916
self.branch.repository.get_transaction())
1917
for parent_id in rev.parent_ids:
1918
self.known_revisions.add(parent_id)
1919
self.to_read.append(parent_id)
1920
self.revisions[rev_id] = rev
1922
def _load_old_inventory(self, rev_id):
1923
assert rev_id not in self.converted_revs
1924
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1925
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1926
inv.revision_id = rev_id
1927
rev = self.revisions[rev_id]
1928
if rev.inventory_sha1:
1929
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1930
'inventory sha mismatch for {%s}' % rev_id
1933
def _load_updated_inventory(self, rev_id):
1934
assert rev_id in self.converted_revs
1935
inv_xml = self.inv_weave.get_text(rev_id)
1936
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1939
def _convert_one_rev(self, rev_id):
1940
"""Convert revision and all referenced objects to new format."""
1941
rev = self.revisions[rev_id]
1942
inv = self._load_old_inventory(rev_id)
1943
present_parents = [p for p in rev.parent_ids
1944
if p not in self.absent_revisions]
1945
self._convert_revision_contents(rev, inv, present_parents)
1946
self._store_new_inv(rev, inv, present_parents)
1947
self.converted_revs.add(rev_id)
1949
def _store_new_inv(self, rev, inv, present_parents):
1950
# the XML is now updated with text versions
1952
entries = inv.iter_entries()
1954
for path, ie in entries:
1955
assert getattr(ie, 'revision', None) is not None, \
1956
'no revision on {%s} in {%s}' % \
1957
(file_id, rev.revision_id)
1958
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1959
new_inv_sha1 = sha_string(new_inv_xml)
1960
self.inv_weave.add_lines(rev.revision_id,
1962
new_inv_xml.splitlines(True))
1963
rev.inventory_sha1 = new_inv_sha1
1965
def _convert_revision_contents(self, rev, inv, present_parents):
1966
"""Convert all the files within a revision.
1968
Also upgrade the inventory to refer to the text revision ids."""
1969
rev_id = rev.revision_id
1970
mutter('converting texts of revision {%s}',
1972
parent_invs = map(self._load_updated_inventory, present_parents)
1973
entries = inv.iter_entries()
1975
for path, ie in entries:
1976
self._convert_file_version(rev, ie, parent_invs)
1978
def _convert_file_version(self, rev, ie, parent_invs):
1979
"""Convert one version of one file.
1981
The file needs to be added into the weave if it is a merge
1982
of >=2 parents or if it's changed from its parent.
1984
file_id = ie.file_id
1985
rev_id = rev.revision_id
1986
w = self.text_weaves.get(file_id)
1989
self.text_weaves[file_id] = w
1990
text_changed = False
1991
parent_candiate_entries = ie.parent_candidates(parent_invs)
1992
for old_revision in parent_candiate_entries.keys():
1993
# if this fails, its a ghost ?
1994
assert old_revision in self.converted_revs, \
1995
"Revision {%s} not in converted_revs" % old_revision
1996
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
1997
# XXX: Note that this is unordered - and this is tolerable because
1998
# the previous code was also unordered.
1999
previous_entries = dict((head, parent_candiate_entries[head]) for head
2001
self.snapshot_ie(previous_entries, ie, w, rev_id)
2003
assert getattr(ie, 'revision', None) is not None
2005
def get_parents(self, revision_ids):
2006
for revision_id in revision_ids:
2007
yield self.revisions[revision_id].parent_ids
2009
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
2010
# TODO: convert this logic, which is ~= snapshot to
2011
# a call to:. This needs the path figured out. rather than a work_tree
2012
# a v4 revision_tree can be given, or something that looks enough like
2013
# one to give the file content to the entry if it needs it.
2014
# and we need something that looks like a weave store for snapshot to
2016
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
2017
if len(previous_revisions) == 1:
2018
previous_ie = previous_revisions.values()[0]
2019
if ie._unchanged(previous_ie):
2020
ie.revision = previous_ie.revision
2023
text = self.branch.repository.weave_store.get(ie.text_id)
2024
file_lines = text.readlines()
2025
assert sha_strings(file_lines) == ie.text_sha1
2026
assert sum(map(len, file_lines)) == ie.text_size
2027
w.add_lines(rev_id, previous_revisions, file_lines)
2028
self.text_count += 1
2030
w.add_lines(rev_id, previous_revisions, [])
2031
ie.revision = rev_id
2033
def _make_order(self):
2034
"""Return a suitable order for importing revisions.
2036
The order must be such that an revision is imported after all
2037
its (present) parents.
2039
todo = set(self.revisions.keys())
2040
done = self.absent_revisions.copy()
2043
# scan through looking for a revision whose parents
2045
for rev_id in sorted(list(todo)):
2046
rev = self.revisions[rev_id]
2047
parent_ids = set(rev.parent_ids)
2048
if parent_ids.issubset(done):
2049
# can take this one now
2050
order.append(rev_id)
2056
class ConvertBzrDir5To6(Converter):
2057
"""Converts format 5 bzr dirs to format 6."""
2059
def convert(self, to_convert, pb):
2060
"""See Converter.convert()."""
2061
self.bzrdir = to_convert
2063
self.pb.note('starting upgrade from format 5 to 6')
2064
self._convert_to_prefixed()
2065
return BzrDir.open(self.bzrdir.root_transport.base)
2067
def _convert_to_prefixed(self):
2068
from bzrlib.store import TransportStore
2069
self.bzrdir.transport.delete('branch-format')
2070
for store_name in ["weaves", "revision-store"]:
2071
self.pb.note("adding prefixes to %s" % store_name)
2072
store_transport = self.bzrdir.transport.clone(store_name)
2073
store = TransportStore(store_transport, prefixed=True)
2074
for urlfilename in store_transport.list_dir('.'):
2075
filename = urlutils.unescape(urlfilename)
2076
if (filename.endswith(".weave") or
2077
filename.endswith(".gz") or
2078
filename.endswith(".sig")):
2079
file_id = os.path.splitext(filename)[0]
2082
prefix_dir = store.hash_prefix(file_id)
2083
# FIXME keep track of the dirs made RBC 20060121
2085
store_transport.move(filename, prefix_dir + '/' + filename)
2086
except errors.NoSuchFile: # catches missing dirs strangely enough
2087
store_transport.mkdir(prefix_dir)
2088
store_transport.move(filename, prefix_dir + '/' + filename)
2089
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
2092
class ConvertBzrDir6ToMeta(Converter):
2093
"""Converts format 6 bzr dirs to metadirs."""
2095
def convert(self, to_convert, pb):
2096
"""See Converter.convert()."""
2097
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2098
from bzrlib.branch import BzrBranchFormat5
2099
self.bzrdir = to_convert
2102
self.total = 20 # the steps we know about
2103
self.garbage_inventories = []
2105
self.pb.note('starting upgrade from format 6 to metadir')
2106
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
2107
# its faster to move specific files around than to open and use the apis...
2108
# first off, nuke ancestry.weave, it was never used.
2110
self.step('Removing ancestry.weave')
2111
self.bzrdir.transport.delete('ancestry.weave')
2112
except errors.NoSuchFile:
2114
# find out whats there
2115
self.step('Finding branch files')
2116
last_revision = self.bzrdir.open_branch().last_revision()
2117
bzrcontents = self.bzrdir.transport.list_dir('.')
2118
for name in bzrcontents:
2119
if name.startswith('basis-inventory.'):
2120
self.garbage_inventories.append(name)
2121
# create new directories for repository, working tree and branch
2122
self.dir_mode = self.bzrdir._control_files._dir_mode
2123
self.file_mode = self.bzrdir._control_files._file_mode
2124
repository_names = [('inventory.weave', True),
2125
('revision-store', True),
2127
self.step('Upgrading repository ')
2128
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2129
self.make_lock('repository')
2130
# we hard code the formats here because we are converting into
2131
# the meta format. The meta format upgrader can take this to a
2132
# future format within each component.
2133
self.put_format('repository', RepositoryFormat7())
2134
for entry in repository_names:
2135
self.move_entry('repository', entry)
2137
self.step('Upgrading branch ')
2138
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2139
self.make_lock('branch')
2140
self.put_format('branch', BzrBranchFormat5())
2141
branch_files = [('revision-history', True),
2142
('branch-name', True),
2144
for entry in branch_files:
2145
self.move_entry('branch', entry)
2147
checkout_files = [('pending-merges', True),
2148
('inventory', True),
2149
('stat-cache', False)]
2150
# If a mandatory checkout file is not present, the branch does not have
2151
# a functional checkout. Do not create a checkout in the converted
2153
for name, mandatory in checkout_files:
2154
if mandatory and name not in bzrcontents:
2155
has_checkout = False
2159
if not has_checkout:
2160
self.pb.note('No working tree.')
2161
# If some checkout files are there, we may as well get rid of them.
2162
for name, mandatory in checkout_files:
2163
if name in bzrcontents:
2164
self.bzrdir.transport.delete(name)
2166
from bzrlib.workingtree import WorkingTreeFormat3
2167
self.step('Upgrading working tree')
2168
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2169
self.make_lock('checkout')
2171
'checkout', WorkingTreeFormat3())
2172
self.bzrdir.transport.delete_multi(
2173
self.garbage_inventories, self.pb)
2174
for entry in checkout_files:
2175
self.move_entry('checkout', entry)
2176
if last_revision is not None:
2177
self.bzrdir._control_files.put_utf8(
2178
'checkout/last-revision', last_revision)
2179
self.bzrdir._control_files.put_utf8(
2180
'branch-format', BzrDirMetaFormat1().get_format_string())
2181
return BzrDir.open(self.bzrdir.root_transport.base)
2183
def make_lock(self, name):
2184
"""Make a lock for the new control dir name."""
2185
self.step('Make %s lock' % name)
2186
ld = lockdir.LockDir(self.bzrdir.transport,
2188
file_modebits=self.file_mode,
2189
dir_modebits=self.dir_mode)
2192
def move_entry(self, new_dir, entry):
2193
"""Move then entry name into new_dir."""
2195
mandatory = entry[1]
2196
self.step('Moving %s' % name)
2198
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2199
except errors.NoSuchFile:
2203
def put_format(self, dirname, format):
2204
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2207
class ConvertMetaToMeta(Converter):
2208
"""Converts the components of metadirs."""
2210
def __init__(self, target_format):
2211
"""Create a metadir to metadir converter.
2213
:param target_format: The final metadir format that is desired.
2215
self.target_format = target_format
2217
def convert(self, to_convert, pb):
2218
"""See Converter.convert()."""
2219
self.bzrdir = to_convert
2223
self.step('checking repository format')
2225
repo = self.bzrdir.open_repository()
2226
except errors.NoRepositoryPresent:
2229
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2230
from bzrlib.repository import CopyConverter
2231
self.pb.note('starting repository conversion')
2232
converter = CopyConverter(self.target_format.repository_format)
2233
converter.convert(repo, pb)
2235
branch = self.bzrdir.open_branch()
2236
except errors.NotBranchError:
2239
# TODO: conversions of Branch and Tree should be done by
2240
# InterXFormat lookups
2241
# Avoid circular imports
2242
from bzrlib import branch as _mod_branch
2243
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2244
self.target_format.get_branch_format().__class__ is
2245
_mod_branch.BzrBranchFormat6):
2246
branch_converter = _mod_branch.Converter5to6()
2247
branch_converter.convert(branch)
2249
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2250
except (errors.NoWorkingTree, errors.NotLocalUrl):
2253
# TODO: conversions of Branch and Tree should be done by
2254
# InterXFormat lookups
2255
if (isinstance(tree, workingtree.WorkingTree3) and
2256
not isinstance(tree, workingtree_4.WorkingTree4) and
2257
isinstance(self.target_format.workingtree_format,
2258
workingtree_4.WorkingTreeFormat4)):
2259
workingtree_4.Converter3to4().convert(tree)
2263
# This is not in remote.py because it's small, and needs to be registered.
2264
# Putting it in remote.py creates a circular import problem.
2265
# we can make it a lazy object if the control formats is turned into something
2267
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2268
"""Format representing bzrdirs accessed via a smart server"""
2270
def get_format_description(self):
2271
return 'bzr remote bzrdir'
2274
def probe_transport(klass, transport):
2275
"""Return a RemoteBzrDirFormat object if it looks possible."""
2277
client = transport.get_smart_client()
2278
except (NotImplementedError, AttributeError,
2279
errors.TransportNotPossible):
2280
# no smart server, so not a branch for this format type.
2281
raise errors.NotBranchError(path=transport.base)
2283
# Send a 'hello' request in protocol version one, and decline to
2284
# open it if the server doesn't support our required version (2) so
2285
# that the VFS-based transport will do it.
2286
request = client.get_request()
2287
smart_protocol = protocol.SmartClientRequestProtocolOne(request)
2288
server_version = smart_protocol.query_version()
2289
if server_version != 2:
2290
raise errors.NotBranchError(path=transport.base)
2293
def initialize_on_transport(self, transport):
2295
# hand off the request to the smart server
2296
shared_medium = transport.get_shared_medium()
2297
except errors.NoSmartMedium:
2298
# TODO: lookup the local format from a server hint.
2299
local_dir_format = BzrDirMetaFormat1()
2300
return local_dir_format.initialize_on_transport(transport)
2301
client = _SmartClient(shared_medium)
2302
path = client.remote_path_from_transport(transport)
2303
response = _SmartClient(shared_medium).call('BzrDirFormat.initialize',
2305
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2306
return remote.RemoteBzrDir(transport)
2308
def _open(self, transport):
2309
return remote.RemoteBzrDir(transport)
2311
def __eq__(self, other):
2312
if not isinstance(other, RemoteBzrDirFormat):
2314
return self.get_format_description() == other.get_format_description()
2317
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2320
class BzrDirFormatInfo(object):
2322
def __init__(self, native, deprecated, hidden, experimental):
2323
self.deprecated = deprecated
2324
self.native = native
2325
self.hidden = hidden
2326
self.experimental = experimental
2329
class BzrDirFormatRegistry(registry.Registry):
2330
"""Registry of user-selectable BzrDir subformats.
2332
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2333
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2336
def register_metadir(self, key,
2337
repository_format, help, native=True, deprecated=False,
2341
experimental=False):
2342
"""Register a metadir subformat.
2344
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2345
by the Repository format.
2347
:param repository_format: The fully-qualified repository format class
2349
:param branch_format: Fully-qualified branch format class name as
2351
:param tree_format: Fully-qualified tree format class name as
2354
# This should be expanded to support setting WorkingTree and Branch
2355
# formats, once BzrDirMetaFormat1 supports that.
2356
def _load(full_name):
2357
mod_name, factory_name = full_name.rsplit('.', 1)
2359
mod = __import__(mod_name, globals(), locals(),
2361
except ImportError, e:
2362
raise ImportError('failed to load %s: %s' % (full_name, e))
2364
factory = getattr(mod, factory_name)
2365
except AttributeError:
2366
raise AttributeError('no factory %s in module %r'
2371
bd = BzrDirMetaFormat1()
2372
if branch_format is not None:
2373
bd.set_branch_format(_load(branch_format))
2374
if tree_format is not None:
2375
bd.workingtree_format = _load(tree_format)
2376
if repository_format is not None:
2377
bd.repository_format = _load(repository_format)
2379
self.register(key, helper, help, native, deprecated, hidden,
2382
def register(self, key, factory, help, native=True, deprecated=False,
2383
hidden=False, experimental=False):
2384
"""Register a BzrDirFormat factory.
2386
The factory must be a callable that takes one parameter: the key.
2387
It must produce an instance of the BzrDirFormat when called.
2389
This function mainly exists to prevent the info object from being
2392
registry.Registry.register(self, key, factory, help,
2393
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2395
def register_lazy(self, key, module_name, member_name, help, native=True,
2396
deprecated=False, hidden=False, experimental=False):
2397
registry.Registry.register_lazy(self, key, module_name, member_name,
2398
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2400
def set_default(self, key):
2401
"""Set the 'default' key to be a clone of the supplied key.
2403
This method must be called once and only once.
2405
registry.Registry.register(self, 'default', self.get(key),
2406
self.get_help(key), info=self.get_info(key))
2408
def set_default_repository(self, key):
2409
"""Set the FormatRegistry default and Repository default.
2411
This is a transitional method while Repository.set_default_format
2414
if 'default' in self:
2415
self.remove('default')
2416
self.set_default(key)
2417
format = self.get('default')()
2418
assert isinstance(format, BzrDirMetaFormat1)
2420
def make_bzrdir(self, key):
2421
return self.get(key)()
2423
def help_topic(self, topic):
2424
output = textwrap.dedent("""\
2425
These formats can be used for creating branches, working trees, and
2429
default_realkey = None
2430
default_help = self.get_help('default')
2432
for key in self.keys():
2433
if key == 'default':
2435
help = self.get_help(key)
2436
if help == default_help:
2437
default_realkey = key
2439
help_pairs.append((key, help))
2441
def wrapped(key, help, info):
2443
help = '(native) ' + help
2444
return ':%s:\n%s\n\n' % (key,
2445
textwrap.fill(help, initial_indent=' ',
2446
subsequent_indent=' '))
2447
if default_realkey is not None:
2448
output += wrapped(default_realkey, '(default) %s' % default_help,
2449
self.get_info('default'))
2450
deprecated_pairs = []
2451
experimental_pairs = []
2452
for key, help in help_pairs:
2453
info = self.get_info(key)
2456
elif info.deprecated:
2457
deprecated_pairs.append((key, help))
2458
elif info.experimental:
2459
experimental_pairs.append((key, help))
2461
output += wrapped(key, help, info)
2462
if len(experimental_pairs) > 0:
2463
output += "Experimental formats are shown below.\n\n"
2464
for key, help in experimental_pairs:
2465
info = self.get_info(key)
2466
output += wrapped(key, help, info)
2467
if len(deprecated_pairs) > 0:
2468
output += "Deprecated formats are shown below.\n\n"
2469
for key, help in deprecated_pairs:
2470
info = self.get_info(key)
2471
output += wrapped(key, help, info)
2476
format_registry = BzrDirFormatRegistry()
2477
format_registry.register('weave', BzrDirFormat6,
2478
'Pre-0.8 format. Slower than knit and does not'
2479
' support checkouts or shared repositories.',
2481
format_registry.register_metadir('knit',
2482
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2483
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2484
branch_format='bzrlib.branch.BzrBranchFormat5',
2485
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2486
format_registry.register_metadir('metaweave',
2487
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2488
'Transitional format in 0.8. Slower than knit.',
2489
branch_format='bzrlib.branch.BzrBranchFormat5',
2490
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2492
format_registry.register_metadir('dirstate',
2493
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2494
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2495
'above when accessed over the network.',
2496
branch_format='bzrlib.branch.BzrBranchFormat5',
2497
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2498
# directly from workingtree_4 triggers a circular import.
2499
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2501
format_registry.register_metadir('dirstate-tags',
2502
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2503
help='New in 0.15: Fast local operations and improved scaling for '
2504
'network operations. Additionally adds support for tags.'
2505
' Incompatible with bzr < 0.15.',
2506
branch_format='bzrlib.branch.BzrBranchFormat6',
2507
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2509
format_registry.register_metadir('rich-root',
2510
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
2511
help='New in 1.0. Better handling of tree roots. Incompatible with'
2513
branch_format='bzrlib.branch.BzrBranchFormat6',
2514
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2517
format_registry.register_metadir('dirstate-with-subtree',
2518
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2519
help='New in 0.15: Fast local operations and improved scaling for '
2520
'network operations. Additionally adds support for versioning nested '
2521
'bzr branches. Incompatible with bzr < 0.15.',
2522
branch_format='bzrlib.branch.BzrBranchFormat6',
2523
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2526
format_registry.register_metadir('pack-0.92',
2527
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2528
help='New in 0.92: Pack-based format with data compatible with '
2529
'dirstate-tags format repositories. Interoperates with '
2530
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2531
'Previously called knitpack-experimental. '
2532
'For more information, see '
2533
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2534
branch_format='bzrlib.branch.BzrBranchFormat6',
2535
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2538
format_registry.register_metadir('pack-0.92-subtree',
2539
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2540
help='New in 0.92: Pack-based format with data compatible with '
2541
'dirstate-with-subtree format repositories. Interoperates with '
2542
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2543
'Previously called knitpack-experimental. '
2544
'For more information, see '
2545
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2546
branch_format='bzrlib.branch.BzrBranchFormat6',
2547
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2551
format_registry.register_metadir('rich-root-pack',
2552
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
2553
help='New in 1.0: Pack-based format with data compatible with '
2554
'rich-root format repositories. Interoperates with '
2555
'bzr repositories before 0.92 but cannot be read by bzr < 1.0. '
2556
'NOTE: This format is experimental. Before using it, please read '
2557
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2558
branch_format='bzrlib.branch.BzrBranchFormat6',
2559
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2563
format_registry.set_default('pack-0.92')