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
23
# TODO: remove unittest dependency; put that stuff inside the test suite
25
# TODO: The Format probe_transport seems a bit redundant with just trying to
26
# open the bzrdir. -- mbp
28
# TODO: Can we move specific formats into separate modules to make this file
31
from cStringIO import StringIO
35
from bzrlib.lazy_import import lazy_import
36
lazy_import(globals(), """
37
from copy import deepcopy
38
from stat import S_ISDIR
47
revision as _mod_revision,
55
from bzrlib.osutils import (
60
from bzrlib.store.revision.text import TextRevisionStore
61
from bzrlib.store.text import TextStore
62
from bzrlib.store.versioned import WeaveStore
63
from bzrlib.transactions import WriteTransaction
64
from bzrlib.transport import get_transport
65
from bzrlib.weave import Weave
68
from bzrlib.trace import mutter, note
69
from bzrlib.transport.local import LocalTransport
73
"""A .bzr control diretory.
75
BzrDir instances let you create or open any of the things that can be
76
found within .bzr - checkouts, branches and repositories.
79
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
81
a transport connected to the directory this bzr was opened from.
85
"""Invoke break_lock on the first object in the bzrdir.
87
If there is a tree, the tree is opened and break_lock() called.
88
Otherwise, branch is tried, and finally repository.
91
thing_to_unlock = self.open_workingtree()
92
except (errors.NotLocalUrl, errors.NoWorkingTree):
94
thing_to_unlock = self.open_branch()
95
except errors.NotBranchError:
97
thing_to_unlock = self.open_repository()
98
except errors.NoRepositoryPresent:
100
thing_to_unlock.break_lock()
102
def can_convert_format(self):
103
"""Return true if this bzrdir is one whose format we can convert from."""
106
def check_conversion_target(self, target_format):
107
target_repo_format = target_format.repository_format
108
source_repo_format = self._format.repository_format
109
source_repo_format.check_conversion_target(target_repo_format)
112
def _check_supported(format, allow_unsupported):
113
"""Check whether format is a supported format.
115
If allow_unsupported is True, this is a no-op.
117
if not allow_unsupported and not format.is_supported():
118
# see open_downlevel to open legacy branches.
119
raise errors.UnsupportedFormatError(format=format)
121
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
122
"""Clone this bzrdir and its contents to url verbatim.
124
If urls last component does not exist, it will be created.
126
if revision_id is not None, then the clone operation may tune
127
itself to download less data.
128
:param force_new_repo: Do not use a shared repository for the target
129
even if one is available.
132
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
133
result = self._format.initialize(url)
135
local_repo = self.find_repository()
136
except errors.NoRepositoryPresent:
139
# may need to copy content in
141
result_repo = local_repo.clone(
143
revision_id=revision_id,
145
result_repo.set_make_working_trees(local_repo.make_working_trees())
148
result_repo = result.find_repository()
149
# fetch content this dir needs.
151
# XXX FIXME RBC 20060214 need tests for this when the basis
153
result_repo.fetch(basis_repo, revision_id=revision_id)
154
result_repo.fetch(local_repo, revision_id=revision_id)
155
except errors.NoRepositoryPresent:
156
# needed to make one anyway.
157
result_repo = local_repo.clone(
159
revision_id=revision_id,
161
result_repo.set_make_working_trees(local_repo.make_working_trees())
162
# 1 if there is a branch present
163
# make sure its content is available in the target repository
166
self.open_branch().clone(result, revision_id=revision_id)
167
except errors.NotBranchError:
170
self.open_workingtree().clone(result, basis=basis_tree)
171
except (errors.NoWorkingTree, errors.NotLocalUrl):
175
def _get_basis_components(self, basis):
176
"""Retrieve the basis components that are available at basis."""
178
return None, None, None
180
basis_tree = basis.open_workingtree()
181
basis_branch = basis_tree.branch
182
basis_repo = basis_branch.repository
183
except (errors.NoWorkingTree, errors.NotLocalUrl):
186
basis_branch = basis.open_branch()
187
basis_repo = basis_branch.repository
188
except errors.NotBranchError:
191
basis_repo = basis.open_repository()
192
except errors.NoRepositoryPresent:
194
return basis_repo, basis_branch, basis_tree
196
# TODO: This should be given a Transport, and should chdir up; otherwise
197
# this will open a new connection.
198
def _make_tail(self, url):
199
head, tail = urlutils.split(url)
200
if tail and tail != '.':
201
t = get_transport(head)
204
except errors.FileExists:
207
# TODO: Should take a Transport
209
def create(cls, base, format=None):
210
"""Create a new BzrDir at the url 'base'.
212
This will call the current default formats initialize with base
213
as the only parameter.
215
:param format: If supplied, the format of branch to create. If not
216
supplied, the default is used.
218
if cls is not BzrDir:
219
raise AssertionError("BzrDir.create always creates the default"
220
" format, not one of %r" % cls)
221
head, tail = urlutils.split(base)
222
if tail and tail != '.':
223
t = get_transport(head)
226
except errors.FileExists:
229
format = BzrDirFormat.get_default_format()
230
return format.initialize(safe_unicode(base))
232
def create_branch(self):
233
"""Create a branch in this BzrDir.
235
The bzrdirs format will control what branch format is created.
236
For more control see BranchFormatXX.create(a_bzrdir).
238
raise NotImplementedError(self.create_branch)
241
def create_branch_and_repo(base, force_new_repo=False, format=None):
242
"""Create a new BzrDir, Branch and Repository at the url 'base'.
244
This will use the current default BzrDirFormat, and use whatever
245
repository format that that uses via bzrdir.create_branch and
246
create_repository. If a shared repository is available that is used
249
The created Branch object is returned.
251
:param base: The URL to create the branch at.
252
:param force_new_repo: If True a new repository is always created.
254
bzrdir = BzrDir.create(base, format)
255
bzrdir._find_or_create_repository(force_new_repo)
256
return bzrdir.create_branch()
258
def _find_or_create_repository(self, force_new_repo):
259
"""Create a new repository if needed, returning the repository."""
261
return self.create_repository()
263
return self.find_repository()
264
except errors.NoRepositoryPresent:
265
return self.create_repository()
268
def create_branch_convenience(base, force_new_repo=False,
269
force_new_tree=None, format=None):
270
"""Create a new BzrDir, Branch and Repository at the url 'base'.
272
This is a convenience function - it will use an existing repository
273
if possible, can be told explicitly whether to create a working tree or
276
This will use the current default BzrDirFormat, and use whatever
277
repository format that that uses via bzrdir.create_branch and
278
create_repository. If a shared repository is available that is used
279
preferentially. Whatever repository is used, its tree creation policy
282
The created Branch object is returned.
283
If a working tree cannot be made due to base not being a file:// url,
284
no error is raised unless force_new_tree is True, in which case no
285
data is created on disk and NotLocalUrl is raised.
287
:param base: The URL to create the branch at.
288
:param force_new_repo: If True a new repository is always created.
289
:param force_new_tree: If True or False force creation of a tree or
290
prevent such creation respectively.
291
:param format: Override for the for the bzrdir format to create
294
# check for non local urls
295
t = get_transport(safe_unicode(base))
296
if not isinstance(t, LocalTransport):
297
raise errors.NotLocalUrl(base)
298
bzrdir = BzrDir.create(base, format)
299
repo = bzrdir._find_or_create_repository(force_new_repo)
300
result = bzrdir.create_branch()
301
if force_new_tree or (repo.make_working_trees() and
302
force_new_tree is None):
304
bzrdir.create_workingtree()
305
except errors.NotLocalUrl:
310
def create_repository(base, shared=False, format=None):
311
"""Create a new BzrDir and Repository at the url 'base'.
313
If no format is supplied, this will default to the current default
314
BzrDirFormat by default, and use whatever repository format that that
315
uses for bzrdirformat.create_repository.
317
:param shared: Create a shared repository rather than a standalone
319
The Repository object is returned.
321
This must be overridden as an instance method in child classes, where
322
it should take no parameters and construct whatever repository format
323
that child class desires.
325
bzrdir = BzrDir.create(base, format)
326
return bzrdir.create_repository(shared)
329
def create_standalone_workingtree(base, format=None):
330
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
332
'base' must be a local path or a file:// url.
334
This will use the current default BzrDirFormat, and use whatever
335
repository format that that uses for bzrdirformat.create_workingtree,
336
create_branch and create_repository.
338
:return: The WorkingTree object.
340
t = get_transport(safe_unicode(base))
341
if not isinstance(t, LocalTransport):
342
raise errors.NotLocalUrl(base)
343
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
345
format=format).bzrdir
346
return bzrdir.create_workingtree()
348
def create_workingtree(self, revision_id=None):
349
"""Create a working tree at this BzrDir.
351
revision_id: create it as of this revision id.
353
raise NotImplementedError(self.create_workingtree)
355
def retire_bzrdir(self):
356
"""Permanently disable the bzrdir.
358
This is done by renaming it to give the user some ability to recover
359
if there was a problem.
361
This will have horrible consequences if anyone has anything locked or
364
for i in xrange(10000):
366
to_path = '.bzr.retired.%d' % i
367
self.root_transport.rename('.bzr', to_path)
368
note("renamed %s to %s"
369
% (self.root_transport.abspath('.bzr'), to_path))
371
except (errors.TransportError, IOError, errors.PathError):
374
def destroy_workingtree(self):
375
"""Destroy the working tree at this BzrDir.
377
Formats that do not support this may raise UnsupportedOperation.
379
raise NotImplementedError(self.destroy_workingtree)
381
def destroy_workingtree_metadata(self):
382
"""Destroy the control files for the working tree at this BzrDir.
384
The contents of working tree files are not affected.
385
Formats that do not support this may raise UnsupportedOperation.
387
raise NotImplementedError(self.destroy_workingtree_metadata)
389
def find_repository(self):
390
"""Find the repository that should be used for a_bzrdir.
392
This does not require a branch as we use it to find the repo for
393
new branches as well as to hook existing branches up to their
397
return self.open_repository()
398
except errors.NoRepositoryPresent:
400
next_transport = self.root_transport.clone('..')
402
# find the next containing bzrdir
404
found_bzrdir = BzrDir.open_containing_from_transport(
406
except errors.NotBranchError:
408
raise errors.NoRepositoryPresent(self)
409
# does it have a repository ?
411
repository = found_bzrdir.open_repository()
412
except errors.NoRepositoryPresent:
413
next_transport = found_bzrdir.root_transport.clone('..')
414
if (found_bzrdir.root_transport.base == next_transport.base):
415
# top of the file system
419
if ((found_bzrdir.root_transport.base ==
420
self.root_transport.base) or repository.is_shared()):
423
raise errors.NoRepositoryPresent(self)
424
raise errors.NoRepositoryPresent(self)
426
def get_branch_transport(self, branch_format):
427
"""Get the transport for use by branch format in this BzrDir.
429
Note that bzr dirs that do not support format strings will raise
430
IncompatibleFormat if the branch format they are given has
431
a format string, and vice versa.
433
If branch_format is None, the transport is returned with no
434
checking. if it is not None, then the returned transport is
435
guaranteed to point to an existing directory ready for use.
437
raise NotImplementedError(self.get_branch_transport)
439
def get_repository_transport(self, repository_format):
440
"""Get the transport for use by repository format in this BzrDir.
442
Note that bzr dirs that do not support format strings will raise
443
IncompatibleFormat if the repository format they are given has
444
a format string, and vice versa.
446
If repository_format is None, the transport is returned with no
447
checking. if it is not None, then the returned transport is
448
guaranteed to point to an existing directory ready for use.
450
raise NotImplementedError(self.get_repository_transport)
452
def get_workingtree_transport(self, tree_format):
453
"""Get the transport for use by workingtree format in this BzrDir.
455
Note that bzr dirs that do not support format strings will raise
456
IncompatibleFormat if the workingtree format they are given has a
457
format string, and vice versa.
459
If workingtree_format is None, the transport is returned with no
460
checking. if it is not None, then the returned transport is
461
guaranteed to point to an existing directory ready for use.
463
raise NotImplementedError(self.get_workingtree_transport)
465
def __init__(self, _transport, _format):
466
"""Initialize a Bzr control dir object.
468
Only really common logic should reside here, concrete classes should be
469
made with varying behaviours.
471
:param _format: the format that is creating this BzrDir instance.
472
:param _transport: the transport this dir is based at.
474
self._format = _format
475
self.transport = _transport.clone('.bzr')
476
self.root_transport = _transport
478
def is_control_filename(self, filename):
479
"""True if filename is the name of a path which is reserved for bzrdir's.
481
:param filename: A filename within the root transport of this bzrdir.
483
This is true IF and ONLY IF the filename is part of the namespace reserved
484
for bzr control dirs. Currently this is the '.bzr' directory in the root
485
of the root_transport. it is expected that plugins will need to extend
486
this in the future - for instance to make bzr talk with svn working
489
# this might be better on the BzrDirFormat class because it refers to
490
# all the possible bzrdir disk formats.
491
# This method is tested via the workingtree is_control_filename tests-
492
# it was extracted from WorkingTree.is_control_filename. If the methods
493
# contract is extended beyond the current trivial implementation please
494
# add new tests for it to the appropriate place.
495
return filename == '.bzr' or filename.startswith('.bzr/')
497
def needs_format_conversion(self, format=None):
498
"""Return true if this bzrdir needs convert_format run on it.
500
For instance, if the repository format is out of date but the
501
branch and working tree are not, this should return True.
503
:param format: Optional parameter indicating a specific desired
504
format we plan to arrive at.
506
raise NotImplementedError(self.needs_format_conversion)
509
def open_unsupported(base):
510
"""Open a branch which is not supported."""
511
return BzrDir.open(base, _unsupported=True)
514
def open(base, _unsupported=False):
515
"""Open an existing bzrdir, rooted at 'base' (url)
517
_unsupported is a private parameter to the BzrDir class.
519
t = get_transport(base)
520
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
523
def open_from_transport(transport, _unsupported=False):
524
"""Open a bzrdir within a particular directory.
526
:param transport: Transport containing the bzrdir.
527
:param _unsupported: private.
529
format = BzrDirFormat.find_format(transport)
530
BzrDir._check_supported(format, _unsupported)
531
return format.open(transport, _found=True)
533
def open_branch(self, unsupported=False):
534
"""Open the branch object at this BzrDir if one is present.
536
If unsupported is True, then no longer supported branch formats can
539
TODO: static convenience version of this?
541
raise NotImplementedError(self.open_branch)
544
def open_containing(url):
545
"""Open an existing branch which contains url.
547
:param url: url to search from.
548
See open_containing_from_transport for more detail.
550
return BzrDir.open_containing_from_transport(get_transport(url))
553
def open_containing_from_transport(a_transport):
554
"""Open an existing branch which contains a_transport.base
556
This probes for a branch at a_transport, and searches upwards from there.
558
Basically we keep looking up until we find the control directory or
559
run into the root. If there isn't one, raises NotBranchError.
560
If there is one and it is either an unrecognised format or an unsupported
561
format, UnknownFormatError or UnsupportedFormatError are raised.
562
If there is one, it is returned, along with the unused portion of url.
564
:return: The BzrDir that contains the path, and a Unicode path
565
for the rest of the URL.
567
# this gets the normalised url back. I.e. '.' -> the full path.
568
url = a_transport.base
571
result = BzrDir.open_from_transport(a_transport)
572
return result, urlutils.unescape(a_transport.relpath(url))
573
except errors.NotBranchError, e:
575
new_t = a_transport.clone('..')
576
if new_t.base == a_transport.base:
577
# reached the root, whatever that may be
578
raise errors.NotBranchError(path=url)
582
def open_containing_tree_or_branch(klass, location):
583
"""Return the branch and working tree contained by a location.
585
Returns (tree, branch, relpath).
586
If there is no tree at containing the location, tree will be None.
587
If there is no branch containing the location, an exception will be
589
relpath is the portion of the path that is contained by the branch.
591
bzrdir, relpath = klass.open_containing(location)
593
tree = bzrdir.open_workingtree()
594
except (errors.NoWorkingTree, errors.NotLocalUrl):
596
branch = bzrdir.open_branch()
599
return tree, branch, relpath
601
def open_repository(self, _unsupported=False):
602
"""Open the repository object at this BzrDir if one is present.
604
This will not follow the Branch object pointer - its strictly a direct
605
open facility. Most client code should use open_branch().repository to
608
_unsupported is a private parameter, not part of the api.
609
TODO: static convenience version of this?
611
raise NotImplementedError(self.open_repository)
613
def open_workingtree(self, _unsupported=False):
614
"""Open the workingtree object at this BzrDir if one is present.
616
TODO: static convenience version of this?
618
raise NotImplementedError(self.open_workingtree)
620
def has_branch(self):
621
"""Tell if this bzrdir contains a branch.
623
Note: if you're going to open the branch, you should just go ahead
624
and try, and not ask permission first. (This method just opens the
625
branch and discards it, and that's somewhat expensive.)
630
except errors.NotBranchError:
633
def has_workingtree(self):
634
"""Tell if this bzrdir contains a working tree.
636
This will still raise an exception if the bzrdir has a workingtree that
637
is remote & inaccessible.
639
Note: if you're going to open the working tree, you should just go ahead
640
and try, and not ask permission first. (This method just opens the
641
workingtree and discards it, and that's somewhat expensive.)
644
self.open_workingtree()
646
except errors.NoWorkingTree:
649
def _cloning_metadir(self, basis=None):
650
def related_repository(bzrdir):
652
branch = bzrdir.open_branch()
653
return branch.repository
654
except errors.NotBranchError:
656
return bzrdir.open_repository()
657
result_format = self._format.__class__()
660
source_repository = related_repository(self)
661
except errors.NoRepositoryPresent:
664
source_repository = related_repository(self)
665
result_format.repository_format = source_repository._format
666
except errors.NoRepositoryPresent:
667
source_repository = None
669
tree = self.open_workingtree()
670
except (errors.NoWorkingTree, errors.NotLocalUrl):
671
result_format.workingtree_format = None
673
result_format.workingtree_format = tree._format.__class__()
674
return result_format, source_repository
676
def cloning_metadir(self, basis=None):
677
"""Produce a metadir suitable for cloning or sprouting with.
679
These operations may produce workingtrees (yes, even though they're
680
"cloning" something that doesn't have a tree, so a viable workingtree
681
format must be selected.
683
format, repository = self._cloning_metadir()
684
if format._workingtree_format is None:
685
if repository is None:
687
tree_format = repository._format._matchingbzrdir.workingtree_format
688
format.workingtree_format = tree_format.__class__()
691
def checkout_metadir(self):
692
return self.cloning_metadir()
694
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
696
"""Create a copy of this bzrdir prepared for use as a new line of
699
If urls last component does not exist, it will be created.
701
Attributes related to the identity of the source branch like
702
branch nickname will be cleaned, a working tree is created
703
whether one existed before or not; and a local branch is always
706
if revision_id is not None, then the clone operation may tune
707
itself to download less data.
710
cloning_format = self.cloning_metadir(basis)
711
result = cloning_format.initialize(url)
712
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
714
source_branch = self.open_branch()
715
source_repository = source_branch.repository
716
except errors.NotBranchError:
719
source_repository = self.open_repository()
720
except errors.NoRepositoryPresent:
721
# copy the entire basis one if there is one
722
# but there is no repository.
723
source_repository = basis_repo
728
result_repo = result.find_repository()
729
except errors.NoRepositoryPresent:
731
if source_repository is None and result_repo is not None:
733
elif source_repository is None and result_repo is None:
734
# no repo available, make a new one
735
result.create_repository()
736
elif source_repository is not None and result_repo is None:
737
# have source, and want to make a new target repo
738
# we don't clone the repo because that preserves attributes
739
# like is_shared(), and we have not yet implemented a
740
# repository sprout().
741
result_repo = result.create_repository()
742
if result_repo is not None:
743
# fetch needed content into target.
745
# XXX FIXME RBC 20060214 need tests for this when the basis
747
result_repo.fetch(basis_repo, revision_id=revision_id)
748
if source_repository is not None:
749
result_repo.fetch(source_repository, revision_id=revision_id)
750
if source_branch is not None:
751
source_branch.sprout(result, revision_id=revision_id)
753
result.create_branch()
754
# TODO: jam 20060426 we probably need a test in here in the
755
# case that the newly sprouted branch is a remote one
756
if result_repo is None or result_repo.make_working_trees():
757
wt = result.create_workingtree()
760
if wt.path2id('') is None:
762
wt.set_root_id(self.open_workingtree.get_root_id())
763
except errors.NoWorkingTree:
769
if recurse == 'down':
771
entries = wt.iter_reference_entries()
772
recurse_branch = wt.branch
773
elif source_branch is not None:
774
entries = source_branch.basis_tree().iter_reference_entries()
775
recurse_branch = source_branch
778
for path, entry in entries:
779
target = urlutils.join(url, urlutils.escape(path))
780
sublocation = source_branch.reference_parent(entry.file_id,
782
sublocation.bzrdir.sprout(target, entry.reference_revision,
783
force_new_repo=force_new_repo, recurse=recurse)
787
class BzrDirPreSplitOut(BzrDir):
788
"""A common class for the all-in-one formats."""
790
def __init__(self, _transport, _format):
791
"""See BzrDir.__init__."""
792
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
793
assert self._format._lock_class == lockable_files.TransportLock
794
assert self._format._lock_file_name == 'branch-lock'
795
self._control_files = lockable_files.LockableFiles(
796
self.get_branch_transport(None),
797
self._format._lock_file_name,
798
self._format._lock_class)
800
def break_lock(self):
801
"""Pre-splitout bzrdirs do not suffer from stale locks."""
802
raise NotImplementedError(self.break_lock)
804
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
805
"""See BzrDir.clone()."""
806
from bzrlib.workingtree import WorkingTreeFormat2
808
result = self._format._initialize_for_clone(url)
809
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
810
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
811
from_branch = self.open_branch()
812
from_branch.clone(result, revision_id=revision_id)
814
self.open_workingtree().clone(result, basis=basis_tree)
815
except errors.NotLocalUrl:
816
# make a new one, this format always has to have one.
818
WorkingTreeFormat2().initialize(result)
819
except errors.NotLocalUrl:
820
# but we cannot do it for remote trees.
821
to_branch = result.open_branch()
822
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
825
def create_branch(self):
826
"""See BzrDir.create_branch."""
827
return self.open_branch()
829
def create_repository(self, shared=False):
830
"""See BzrDir.create_repository."""
832
raise errors.IncompatibleFormat('shared repository', self._format)
833
return self.open_repository()
835
def create_workingtree(self, revision_id=None):
836
"""See BzrDir.create_workingtree."""
837
# this looks buggy but is not -really-
838
# clone and sprout will have set the revision_id
839
# and that will have set it for us, its only
840
# specific uses of create_workingtree in isolation
841
# that can do wonky stuff here, and that only
842
# happens for creating checkouts, which cannot be
843
# done on this format anyway. So - acceptable wart.
844
result = self.open_workingtree()
845
if revision_id is not None:
846
if revision_id == _mod_revision.NULL_REVISION:
847
result.set_parent_ids([])
849
result.set_parent_ids([revision_id])
852
def destroy_workingtree(self):
853
"""See BzrDir.destroy_workingtree."""
854
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
856
def destroy_workingtree_metadata(self):
857
"""See BzrDir.destroy_workingtree_metadata."""
858
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
861
def get_branch_transport(self, branch_format):
862
"""See BzrDir.get_branch_transport()."""
863
if branch_format is None:
864
return self.transport
866
branch_format.get_format_string()
867
except NotImplementedError:
868
return self.transport
869
raise errors.IncompatibleFormat(branch_format, self._format)
871
def get_repository_transport(self, repository_format):
872
"""See BzrDir.get_repository_transport()."""
873
if repository_format is None:
874
return self.transport
876
repository_format.get_format_string()
877
except NotImplementedError:
878
return self.transport
879
raise errors.IncompatibleFormat(repository_format, self._format)
881
def get_workingtree_transport(self, workingtree_format):
882
"""See BzrDir.get_workingtree_transport()."""
883
if workingtree_format is None:
884
return self.transport
886
workingtree_format.get_format_string()
887
except NotImplementedError:
888
return self.transport
889
raise errors.IncompatibleFormat(workingtree_format, self._format)
891
def needs_format_conversion(self, format=None):
892
"""See BzrDir.needs_format_conversion()."""
893
# if the format is not the same as the system default,
894
# an upgrade is needed.
896
format = BzrDirFormat.get_default_format()
897
return not isinstance(self._format, format.__class__)
899
def open_branch(self, unsupported=False):
900
"""See BzrDir.open_branch."""
901
from bzrlib.branch import BzrBranchFormat4
902
format = BzrBranchFormat4()
903
self._check_supported(format, unsupported)
904
return format.open(self, _found=True)
906
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
907
"""See BzrDir.sprout()."""
908
from bzrlib.workingtree import WorkingTreeFormat2
910
result = self._format._initialize_for_clone(url)
911
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
913
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
914
except errors.NoRepositoryPresent:
917
self.open_branch().sprout(result, revision_id=revision_id)
918
except errors.NotBranchError:
920
# we always want a working tree
921
WorkingTreeFormat2().initialize(result)
925
class BzrDir4(BzrDirPreSplitOut):
926
"""A .bzr version 4 control object.
928
This is a deprecated format and may be removed after sept 2006.
931
def create_repository(self, shared=False):
932
"""See BzrDir.create_repository."""
933
return self._format.repository_format.initialize(self, shared)
935
def needs_format_conversion(self, format=None):
936
"""Format 4 dirs are always in need of conversion."""
939
def open_repository(self):
940
"""See BzrDir.open_repository."""
941
from bzrlib.repofmt.weaverepo import RepositoryFormat4
942
return RepositoryFormat4().open(self, _found=True)
945
class BzrDir5(BzrDirPreSplitOut):
946
"""A .bzr version 5 control object.
948
This is a deprecated format and may be removed after sept 2006.
951
def open_repository(self):
952
"""See BzrDir.open_repository."""
953
from bzrlib.repofmt.weaverepo import RepositoryFormat5
954
return RepositoryFormat5().open(self, _found=True)
956
def open_workingtree(self, _unsupported=False):
957
"""See BzrDir.create_workingtree."""
958
from bzrlib.workingtree import WorkingTreeFormat2
959
return WorkingTreeFormat2().open(self, _found=True)
962
class BzrDir6(BzrDirPreSplitOut):
963
"""A .bzr version 6 control object.
965
This is a deprecated format and may be removed after sept 2006.
968
def open_repository(self):
969
"""See BzrDir.open_repository."""
970
from bzrlib.repofmt.weaverepo import RepositoryFormat6
971
return RepositoryFormat6().open(self, _found=True)
973
def open_workingtree(self, _unsupported=False):
974
"""See BzrDir.create_workingtree."""
975
from bzrlib.workingtree import WorkingTreeFormat2
976
return WorkingTreeFormat2().open(self, _found=True)
979
class BzrDirMeta1(BzrDir):
980
"""A .bzr meta version 1 control object.
982
This is the first control object where the
983
individual aspects are really split out: there are separate repository,
984
workingtree and branch subdirectories and any subset of the three can be
985
present within a BzrDir.
988
def can_convert_format(self):
989
"""See BzrDir.can_convert_format()."""
992
def create_branch(self):
993
"""See BzrDir.create_branch."""
994
return self._format.get_branch_format().initialize(self)
996
def create_repository(self, shared=False):
997
"""See BzrDir.create_repository."""
998
return self._format.repository_format.initialize(self, shared)
1000
def create_workingtree(self, revision_id=None):
1001
"""See BzrDir.create_workingtree."""
1002
from bzrlib.workingtree import WorkingTreeFormat
1003
return self._format.workingtree_format.initialize(self, revision_id)
1005
def destroy_workingtree(self):
1006
"""See BzrDir.destroy_workingtree."""
1007
wt = self.open_workingtree()
1008
repository = wt.branch.repository
1009
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1010
wt.revert([], old_tree=empty)
1011
self.destroy_workingtree_metadata()
1013
def destroy_workingtree_metadata(self):
1014
self.transport.delete_tree('checkout')
1016
def _get_mkdir_mode(self):
1017
"""Figure out the mode to use when creating a bzrdir subdir."""
1018
temp_control = lockable_files.LockableFiles(self.transport, '',
1019
lockable_files.TransportLock)
1020
return temp_control._dir_mode
1022
def get_branch_transport(self, branch_format):
1023
"""See BzrDir.get_branch_transport()."""
1024
if branch_format is None:
1025
return self.transport.clone('branch')
1027
branch_format.get_format_string()
1028
except NotImplementedError:
1029
raise errors.IncompatibleFormat(branch_format, self._format)
1031
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1032
except errors.FileExists:
1034
return self.transport.clone('branch')
1036
def get_repository_transport(self, repository_format):
1037
"""See BzrDir.get_repository_transport()."""
1038
if repository_format is None:
1039
return self.transport.clone('repository')
1041
repository_format.get_format_string()
1042
except NotImplementedError:
1043
raise errors.IncompatibleFormat(repository_format, self._format)
1045
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1046
except errors.FileExists:
1048
return self.transport.clone('repository')
1050
def get_workingtree_transport(self, workingtree_format):
1051
"""See BzrDir.get_workingtree_transport()."""
1052
if workingtree_format is None:
1053
return self.transport.clone('checkout')
1055
workingtree_format.get_format_string()
1056
except NotImplementedError:
1057
raise errors.IncompatibleFormat(workingtree_format, self._format)
1059
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1060
except errors.FileExists:
1062
return self.transport.clone('checkout')
1064
def needs_format_conversion(self, format=None):
1065
"""See BzrDir.needs_format_conversion()."""
1067
format = BzrDirFormat.get_default_format()
1068
if not isinstance(self._format, format.__class__):
1069
# it is not a meta dir format, conversion is needed.
1071
# we might want to push this down to the repository?
1073
if not isinstance(self.open_repository()._format,
1074
format.repository_format.__class__):
1075
# the repository needs an upgrade.
1077
except errors.NoRepositoryPresent:
1080
if not isinstance(self.open_branch()._format,
1081
format.get_branch_format().__class__):
1082
# the branch needs an upgrade.
1084
except errors.NotBranchError:
1087
if not isinstance(self.open_workingtree()._format,
1088
format.workingtree_format.__class__):
1089
# the workingtree needs an upgrade.
1091
except (errors.NoWorkingTree, errors.NotLocalUrl):
1095
def open_branch(self, unsupported=False):
1096
"""See BzrDir.open_branch."""
1097
from bzrlib.branch import BranchFormat
1098
format = BranchFormat.find_format(self)
1099
self._check_supported(format, unsupported)
1100
return format.open(self, _found=True)
1102
def open_repository(self, unsupported=False):
1103
"""See BzrDir.open_repository."""
1104
from bzrlib.repository import RepositoryFormat
1105
format = RepositoryFormat.find_format(self)
1106
self._check_supported(format, unsupported)
1107
return format.open(self, _found=True)
1109
def open_workingtree(self, unsupported=False):
1110
"""See BzrDir.open_workingtree."""
1111
from bzrlib.workingtree import WorkingTreeFormat
1112
format = WorkingTreeFormat.find_format(self)
1113
self._check_supported(format, unsupported)
1114
return format.open(self, _found=True)
1117
class BzrDirFormat(object):
1118
"""An encapsulation of the initialization and open routines for a format.
1120
Formats provide three things:
1121
* An initialization routine,
1125
Formats are placed in an dict by their format string for reference
1126
during bzrdir opening. These should be subclasses of BzrDirFormat
1129
Once a format is deprecated, just deprecate the initialize and open
1130
methods on the format class. Do not deprecate the object, as the
1131
object will be created every system load.
1134
_default_format = None
1135
"""The default format used for new .bzr dirs."""
1138
"""The known formats."""
1140
_control_formats = []
1141
"""The registered control formats - .bzr, ....
1143
This is a list of BzrDirFormat objects.
1146
_lock_file_name = 'branch-lock'
1148
# _lock_class must be set in subclasses to the lock type, typ.
1149
# TransportLock or LockDir
1152
def find_format(klass, transport):
1153
"""Return the format present at transport."""
1154
for format in klass._control_formats:
1156
return format.probe_transport(transport)
1157
except errors.NotBranchError:
1158
# this format does not find a control dir here.
1160
raise errors.NotBranchError(path=transport.base)
1163
def probe_transport(klass, transport):
1164
"""Return the .bzrdir style transport present at URL."""
1166
format_string = transport.get(".bzr/branch-format").read()
1167
except errors.NoSuchFile:
1168
raise errors.NotBranchError(path=transport.base)
1171
return klass._formats[format_string]
1173
raise errors.UnknownFormatError(format=format_string)
1176
def get_default_format(klass):
1177
"""Return the current default format."""
1178
return klass._default_format
1180
def get_format_string(self):
1181
"""Return the ASCII format string that identifies this format."""
1182
raise NotImplementedError(self.get_format_string)
1184
def get_format_description(self):
1185
"""Return the short description for this format."""
1186
raise NotImplementedError(self.get_format_description)
1188
def get_converter(self, format=None):
1189
"""Return the converter to use to convert bzrdirs needing converts.
1191
This returns a bzrlib.bzrdir.Converter object.
1193
This should return the best upgrader to step this format towards the
1194
current default format. In the case of plugins we can/should provide
1195
some means for them to extend the range of returnable converters.
1197
:param format: Optional format to override the default format of the
1200
raise NotImplementedError(self.get_converter)
1202
def initialize(self, url):
1203
"""Create a bzr control dir at this url and return an opened copy.
1205
Subclasses should typically override initialize_on_transport
1206
instead of this method.
1208
return self.initialize_on_transport(get_transport(url))
1210
def initialize_on_transport(self, transport):
1211
"""Initialize a new bzrdir in the base directory of a Transport."""
1212
# Since we don't have a .bzr directory, inherit the
1213
# mode from the root directory
1214
temp_control = lockable_files.LockableFiles(transport,
1215
'', lockable_files.TransportLock)
1216
temp_control._transport.mkdir('.bzr',
1217
# FIXME: RBC 20060121 don't peek under
1219
mode=temp_control._dir_mode)
1220
file_mode = temp_control._file_mode
1222
mutter('created control directory in ' + transport.base)
1223
control = transport.clone('.bzr')
1224
utf8_files = [('README',
1225
"This is a Bazaar-NG control directory.\n"
1226
"Do not change any files in this directory.\n"),
1227
('branch-format', self.get_format_string()),
1229
# NB: no need to escape relative paths that are url safe.
1230
control_files = lockable_files.LockableFiles(control,
1231
self._lock_file_name, self._lock_class)
1232
control_files.create_lock()
1233
control_files.lock_write()
1235
for file, content in utf8_files:
1236
control_files.put_utf8(file, content)
1238
control_files.unlock()
1239
return self.open(transport, _found=True)
1241
def is_supported(self):
1242
"""Is this format supported?
1244
Supported formats must be initializable and openable.
1245
Unsupported formats may not support initialization or committing or
1246
some other features depending on the reason for not being supported.
1250
def same_model(self, target_format):
1251
return (self.repository_format.rich_root_data ==
1252
target_format.rich_root_data)
1255
def known_formats(klass):
1256
"""Return all the known formats.
1258
Concrete formats should override _known_formats.
1260
# There is double indirection here to make sure that control
1261
# formats used by more than one dir format will only be probed
1262
# once. This can otherwise be quite expensive for remote connections.
1264
for format in klass._control_formats:
1265
result.update(format._known_formats())
1269
def _known_formats(klass):
1270
"""Return the known format instances for this control format."""
1271
return set(klass._formats.values())
1273
def open(self, transport, _found=False):
1274
"""Return an instance of this format for the dir transport points at.
1276
_found is a private parameter, do not use it.
1279
found_format = BzrDirFormat.find_format(transport)
1280
if not isinstance(found_format, self.__class__):
1281
raise AssertionError("%s was asked to open %s, but it seems to need "
1283
% (self, transport, found_format))
1284
return self._open(transport)
1286
def _open(self, transport):
1287
"""Template method helper for opening BzrDirectories.
1289
This performs the actual open and any additional logic or parameter
1292
raise NotImplementedError(self._open)
1295
def register_format(klass, format):
1296
klass._formats[format.get_format_string()] = format
1299
def register_control_format(klass, format):
1300
"""Register a format that does not use '.bzrdir' for its control dir.
1302
TODO: This should be pulled up into a 'ControlDirFormat' base class
1303
which BzrDirFormat can inherit from, and renamed to register_format
1304
there. It has been done without that for now for simplicity of
1307
klass._control_formats.append(format)
1310
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1311
def set_default_format(klass, format):
1312
klass._set_default_format(format)
1315
def _set_default_format(klass, format):
1316
"""Set default format (for testing behavior of defaults only)"""
1317
klass._default_format = format
1320
return self.get_format_string()[:-1]
1323
def unregister_format(klass, format):
1324
assert klass._formats[format.get_format_string()] is format
1325
del klass._formats[format.get_format_string()]
1328
def unregister_control_format(klass, format):
1329
klass._control_formats.remove(format)
1332
# register BzrDirFormat as a control format
1333
BzrDirFormat.register_control_format(BzrDirFormat)
1336
class BzrDirFormat4(BzrDirFormat):
1337
"""Bzr dir format 4.
1339
This format is a combined format for working tree, branch and repository.
1341
- Format 1 working trees [always]
1342
- Format 4 branches [always]
1343
- Format 4 repositories [always]
1345
This format is deprecated: it indexes texts using a text it which is
1346
removed in format 5; write support for this format has been removed.
1349
_lock_class = lockable_files.TransportLock
1351
def get_format_string(self):
1352
"""See BzrDirFormat.get_format_string()."""
1353
return "Bazaar-NG branch, format 0.0.4\n"
1355
def get_format_description(self):
1356
"""See BzrDirFormat.get_format_description()."""
1357
return "All-in-one format 4"
1359
def get_converter(self, format=None):
1360
"""See BzrDirFormat.get_converter()."""
1361
# there is one and only one upgrade path here.
1362
return ConvertBzrDir4To5()
1364
def initialize_on_transport(self, transport):
1365
"""Format 4 branches cannot be created."""
1366
raise errors.UninitializableFormat(self)
1368
def is_supported(self):
1369
"""Format 4 is not supported.
1371
It is not supported because the model changed from 4 to 5 and the
1372
conversion logic is expensive - so doing it on the fly was not
1377
def _open(self, transport):
1378
"""See BzrDirFormat._open."""
1379
return BzrDir4(transport, self)
1381
def __return_repository_format(self):
1382
"""Circular import protection."""
1383
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1384
return RepositoryFormat4()
1385
repository_format = property(__return_repository_format)
1388
class BzrDirFormat5(BzrDirFormat):
1389
"""Bzr control format 5.
1391
This format is a combined format for working tree, branch and repository.
1393
- Format 2 working trees [always]
1394
- Format 4 branches [always]
1395
- Format 5 repositories [always]
1396
Unhashed stores in the repository.
1399
_lock_class = lockable_files.TransportLock
1401
def get_format_string(self):
1402
"""See BzrDirFormat.get_format_string()."""
1403
return "Bazaar-NG branch, format 5\n"
1405
def get_format_description(self):
1406
"""See BzrDirFormat.get_format_description()."""
1407
return "All-in-one format 5"
1409
def get_converter(self, format=None):
1410
"""See BzrDirFormat.get_converter()."""
1411
# there is one and only one upgrade path here.
1412
return ConvertBzrDir5To6()
1414
def _initialize_for_clone(self, url):
1415
return self.initialize_on_transport(get_transport(url), _cloning=True)
1417
def initialize_on_transport(self, transport, _cloning=False):
1418
"""Format 5 dirs always have working tree, branch and repository.
1420
Except when they are being cloned.
1422
from bzrlib.branch import BzrBranchFormat4
1423
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1424
from bzrlib.workingtree import WorkingTreeFormat2
1425
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1426
RepositoryFormat5().initialize(result, _internal=True)
1428
branch = BzrBranchFormat4().initialize(result)
1430
WorkingTreeFormat2().initialize(result)
1431
except errors.NotLocalUrl:
1432
# Even though we can't access the working tree, we need to
1433
# create its control files.
1434
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1437
def _open(self, transport):
1438
"""See BzrDirFormat._open."""
1439
return BzrDir5(transport, self)
1441
def __return_repository_format(self):
1442
"""Circular import protection."""
1443
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1444
return RepositoryFormat5()
1445
repository_format = property(__return_repository_format)
1448
class BzrDirFormat6(BzrDirFormat):
1449
"""Bzr control format 6.
1451
This format is a combined format for working tree, branch and repository.
1453
- Format 2 working trees [always]
1454
- Format 4 branches [always]
1455
- Format 6 repositories [always]
1458
_lock_class = lockable_files.TransportLock
1460
def get_format_string(self):
1461
"""See BzrDirFormat.get_format_string()."""
1462
return "Bazaar-NG branch, format 6\n"
1464
def get_format_description(self):
1465
"""See BzrDirFormat.get_format_description()."""
1466
return "All-in-one format 6"
1468
def get_converter(self, format=None):
1469
"""See BzrDirFormat.get_converter()."""
1470
# there is one and only one upgrade path here.
1471
return ConvertBzrDir6ToMeta()
1473
def _initialize_for_clone(self, url):
1474
return self.initialize_on_transport(get_transport(url), _cloning=True)
1476
def initialize_on_transport(self, transport, _cloning=False):
1477
"""Format 6 dirs always have working tree, branch and repository.
1479
Except when they are being cloned.
1481
from bzrlib.branch import BzrBranchFormat4
1482
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1483
from bzrlib.workingtree import WorkingTreeFormat2
1484
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1485
RepositoryFormat6().initialize(result, _internal=True)
1487
branch = BzrBranchFormat4().initialize(result)
1489
WorkingTreeFormat2().initialize(result)
1490
except errors.NotLocalUrl:
1491
# Even though we can't access the working tree, we need to
1492
# create its control files.
1493
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1496
def _open(self, transport):
1497
"""See BzrDirFormat._open."""
1498
return BzrDir6(transport, self)
1500
def __return_repository_format(self):
1501
"""Circular import protection."""
1502
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1503
return RepositoryFormat6()
1504
repository_format = property(__return_repository_format)
1507
class BzrDirMetaFormat1(BzrDirFormat):
1508
"""Bzr meta control format 1
1510
This is the first format with split out working tree, branch and repository
1513
- Format 3 working trees [optional]
1514
- Format 5 branches [optional]
1515
- Format 7 repositories [optional]
1518
_lock_class = lockdir.LockDir
1521
self._workingtree_format = None
1522
self._branch_format = None
1524
def __eq__(self, other):
1525
if other.__class__ is not self.__class__:
1527
if other.repository_format != self.repository_format:
1529
if other.workingtree_format != self.workingtree_format:
1533
def __ne__(self, other):
1534
return not self == other
1536
def get_branch_format(self):
1537
if self._branch_format is None:
1538
from bzrlib.branch import BranchFormat
1539
self._branch_format = BranchFormat.get_default_format()
1540
return self._branch_format
1542
def set_branch_format(self, format):
1543
self._branch_format = format
1545
def get_converter(self, format=None):
1546
"""See BzrDirFormat.get_converter()."""
1548
format = BzrDirFormat.get_default_format()
1549
if not isinstance(self, format.__class__):
1550
# converting away from metadir is not implemented
1551
raise NotImplementedError(self.get_converter)
1552
return ConvertMetaToMeta(format)
1554
def get_format_string(self):
1555
"""See BzrDirFormat.get_format_string()."""
1556
return "Bazaar-NG meta directory, format 1\n"
1558
def get_format_description(self):
1559
"""See BzrDirFormat.get_format_description()."""
1560
return "Meta directory format 1"
1562
def _open(self, transport):
1563
"""See BzrDirFormat._open."""
1564
return BzrDirMeta1(transport, self)
1566
def __return_repository_format(self):
1567
"""Circular import protection."""
1568
if getattr(self, '_repository_format', None):
1569
return self._repository_format
1570
from bzrlib.repository import RepositoryFormat
1571
return RepositoryFormat.get_default_format()
1573
def __set_repository_format(self, value):
1574
"""Allow changint the repository format for metadir formats."""
1575
self._repository_format = value
1577
repository_format = property(__return_repository_format, __set_repository_format)
1579
def __get_workingtree_format(self):
1580
if self._workingtree_format is None:
1581
from bzrlib.workingtree import WorkingTreeFormat
1582
self._workingtree_format = WorkingTreeFormat.get_default_format()
1583
return self._workingtree_format
1585
def __set_workingtree_format(self, wt_format):
1586
self._workingtree_format = wt_format
1588
workingtree_format = property(__get_workingtree_format,
1589
__set_workingtree_format)
1592
BzrDirFormat.register_format(BzrDirFormat4())
1593
BzrDirFormat.register_format(BzrDirFormat5())
1594
BzrDirFormat.register_format(BzrDirFormat6())
1595
__default_format = BzrDirMetaFormat1()
1596
BzrDirFormat.register_format(__default_format)
1597
BzrDirFormat._default_format = __default_format
1600
class BzrDirTestProviderAdapter(object):
1601
"""A tool to generate a suite testing multiple bzrdir formats at once.
1603
This is done by copying the test once for each transport and injecting
1604
the transport_server, transport_readonly_server, and bzrdir_format
1605
classes into each copy. Each copy is also given a new id() to make it
1609
def __init__(self, transport_server, transport_readonly_server, formats):
1610
self._transport_server = transport_server
1611
self._transport_readonly_server = transport_readonly_server
1612
self._formats = formats
1614
def adapt(self, test):
1615
result = unittest.TestSuite()
1616
for format in self._formats:
1617
new_test = deepcopy(test)
1618
new_test.transport_server = self._transport_server
1619
new_test.transport_readonly_server = self._transport_readonly_server
1620
new_test.bzrdir_format = format
1621
def make_new_test_id():
1622
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1623
return lambda: new_id
1624
new_test.id = make_new_test_id()
1625
result.addTest(new_test)
1629
class Converter(object):
1630
"""Converts a disk format object from one format to another."""
1632
def convert(self, to_convert, pb):
1633
"""Perform the conversion of to_convert, giving feedback via pb.
1635
:param to_convert: The disk object to convert.
1636
:param pb: a progress bar to use for progress information.
1639
def step(self, message):
1640
"""Update the pb by a step."""
1642
self.pb.update(message, self.count, self.total)
1645
class ConvertBzrDir4To5(Converter):
1646
"""Converts format 4 bzr dirs to format 5."""
1649
super(ConvertBzrDir4To5, self).__init__()
1650
self.converted_revs = set()
1651
self.absent_revisions = set()
1655
def convert(self, to_convert, pb):
1656
"""See Converter.convert()."""
1657
self.bzrdir = to_convert
1659
self.pb.note('starting upgrade from format 4 to 5')
1660
if isinstance(self.bzrdir.transport, LocalTransport):
1661
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1662
self._convert_to_weaves()
1663
return BzrDir.open(self.bzrdir.root_transport.base)
1665
def _convert_to_weaves(self):
1666
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1669
stat = self.bzrdir.transport.stat('weaves')
1670
if not S_ISDIR(stat.st_mode):
1671
self.bzrdir.transport.delete('weaves')
1672
self.bzrdir.transport.mkdir('weaves')
1673
except errors.NoSuchFile:
1674
self.bzrdir.transport.mkdir('weaves')
1675
# deliberately not a WeaveFile as we want to build it up slowly.
1676
self.inv_weave = Weave('inventory')
1677
# holds in-memory weaves for all files
1678
self.text_weaves = {}
1679
self.bzrdir.transport.delete('branch-format')
1680
self.branch = self.bzrdir.open_branch()
1681
self._convert_working_inv()
1682
rev_history = self.branch.revision_history()
1683
# to_read is a stack holding the revisions we still need to process;
1684
# appending to it adds new highest-priority revisions
1685
self.known_revisions = set(rev_history)
1686
self.to_read = rev_history[-1:]
1688
rev_id = self.to_read.pop()
1689
if (rev_id not in self.revisions
1690
and rev_id not in self.absent_revisions):
1691
self._load_one_rev(rev_id)
1693
to_import = self._make_order()
1694
for i, rev_id in enumerate(to_import):
1695
self.pb.update('converting revision', i, len(to_import))
1696
self._convert_one_rev(rev_id)
1698
self._write_all_weaves()
1699
self._write_all_revs()
1700
self.pb.note('upgraded to weaves:')
1701
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1702
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1703
self.pb.note(' %6d texts', self.text_count)
1704
self._cleanup_spare_files_after_format4()
1705
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1707
def _cleanup_spare_files_after_format4(self):
1708
# FIXME working tree upgrade foo.
1709
for n in 'merged-patches', 'pending-merged-patches':
1711
## assert os.path.getsize(p) == 0
1712
self.bzrdir.transport.delete(n)
1713
except errors.NoSuchFile:
1715
self.bzrdir.transport.delete_tree('inventory-store')
1716
self.bzrdir.transport.delete_tree('text-store')
1718
def _convert_working_inv(self):
1719
inv = xml4.serializer_v4.read_inventory(
1720
self.branch.control_files.get('inventory'))
1721
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1722
# FIXME inventory is a working tree change.
1723
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1725
def _write_all_weaves(self):
1726
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1727
weave_transport = self.bzrdir.transport.clone('weaves')
1728
weaves = WeaveStore(weave_transport, prefixed=False)
1729
transaction = WriteTransaction()
1733
for file_id, file_weave in self.text_weaves.items():
1734
self.pb.update('writing weave', i, len(self.text_weaves))
1735
weaves._put_weave(file_id, file_weave, transaction)
1737
self.pb.update('inventory', 0, 1)
1738
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1739
self.pb.update('inventory', 1, 1)
1743
def _write_all_revs(self):
1744
"""Write all revisions out in new form."""
1745
self.bzrdir.transport.delete_tree('revision-store')
1746
self.bzrdir.transport.mkdir('revision-store')
1747
revision_transport = self.bzrdir.transport.clone('revision-store')
1749
_revision_store = TextRevisionStore(TextStore(revision_transport,
1753
transaction = WriteTransaction()
1754
for i, rev_id in enumerate(self.converted_revs):
1755
self.pb.update('write revision', i, len(self.converted_revs))
1756
_revision_store.add_revision(self.revisions[rev_id], transaction)
1760
def _load_one_rev(self, rev_id):
1761
"""Load a revision object into memory.
1763
Any parents not either loaded or abandoned get queued to be
1765
self.pb.update('loading revision',
1766
len(self.revisions),
1767
len(self.known_revisions))
1768
if not self.branch.repository.has_revision(rev_id):
1770
self.pb.note('revision {%s} not present in branch; '
1771
'will be converted as a ghost',
1773
self.absent_revisions.add(rev_id)
1775
rev = self.branch.repository._revision_store.get_revision(rev_id,
1776
self.branch.repository.get_transaction())
1777
for parent_id in rev.parent_ids:
1778
self.known_revisions.add(parent_id)
1779
self.to_read.append(parent_id)
1780
self.revisions[rev_id] = rev
1782
def _load_old_inventory(self, rev_id):
1783
assert rev_id not in self.converted_revs
1784
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1785
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1786
inv.revision_id = rev_id
1787
rev = self.revisions[rev_id]
1788
if rev.inventory_sha1:
1789
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1790
'inventory sha mismatch for {%s}' % rev_id
1793
def _load_updated_inventory(self, rev_id):
1794
assert rev_id in self.converted_revs
1795
inv_xml = self.inv_weave.get_text(rev_id)
1796
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1799
def _convert_one_rev(self, rev_id):
1800
"""Convert revision and all referenced objects to new format."""
1801
rev = self.revisions[rev_id]
1802
inv = self._load_old_inventory(rev_id)
1803
present_parents = [p for p in rev.parent_ids
1804
if p not in self.absent_revisions]
1805
self._convert_revision_contents(rev, inv, present_parents)
1806
self._store_new_weave(rev, inv, present_parents)
1807
self.converted_revs.add(rev_id)
1809
def _store_new_weave(self, rev, inv, present_parents):
1810
# the XML is now updated with text versions
1812
entries = inv.iter_entries()
1814
for path, ie in entries:
1815
assert getattr(ie, 'revision', None) is not None, \
1816
'no revision on {%s} in {%s}' % \
1817
(file_id, rev.revision_id)
1818
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1819
new_inv_sha1 = sha_string(new_inv_xml)
1820
self.inv_weave.add_lines(rev.revision_id,
1822
new_inv_xml.splitlines(True))
1823
rev.inventory_sha1 = new_inv_sha1
1825
def _convert_revision_contents(self, rev, inv, present_parents):
1826
"""Convert all the files within a revision.
1828
Also upgrade the inventory to refer to the text revision ids."""
1829
rev_id = rev.revision_id
1830
mutter('converting texts of revision {%s}',
1832
parent_invs = map(self._load_updated_inventory, present_parents)
1833
entries = inv.iter_entries()
1835
for path, ie in entries:
1836
self._convert_file_version(rev, ie, parent_invs)
1838
def _convert_file_version(self, rev, ie, parent_invs):
1839
"""Convert one version of one file.
1841
The file needs to be added into the weave if it is a merge
1842
of >=2 parents or if it's changed from its parent.
1844
file_id = ie.file_id
1845
rev_id = rev.revision_id
1846
w = self.text_weaves.get(file_id)
1849
self.text_weaves[file_id] = w
1850
text_changed = False
1851
previous_entries = ie.find_previous_heads(parent_invs,
1855
for old_revision in previous_entries:
1856
# if this fails, its a ghost ?
1857
assert old_revision in self.converted_revs, \
1858
"Revision {%s} not in converted_revs" % old_revision
1859
self.snapshot_ie(previous_entries, ie, w, rev_id)
1861
assert getattr(ie, 'revision', None) is not None
1863
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1864
# TODO: convert this logic, which is ~= snapshot to
1865
# a call to:. This needs the path figured out. rather than a work_tree
1866
# a v4 revision_tree can be given, or something that looks enough like
1867
# one to give the file content to the entry if it needs it.
1868
# and we need something that looks like a weave store for snapshot to
1870
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1871
if len(previous_revisions) == 1:
1872
previous_ie = previous_revisions.values()[0]
1873
if ie._unchanged(previous_ie):
1874
ie.revision = previous_ie.revision
1877
text = self.branch.repository.text_store.get(ie.text_id)
1878
file_lines = text.readlines()
1879
assert sha_strings(file_lines) == ie.text_sha1
1880
assert sum(map(len, file_lines)) == ie.text_size
1881
w.add_lines(rev_id, previous_revisions, file_lines)
1882
self.text_count += 1
1884
w.add_lines(rev_id, previous_revisions, [])
1885
ie.revision = rev_id
1887
def _make_order(self):
1888
"""Return a suitable order for importing revisions.
1890
The order must be such that an revision is imported after all
1891
its (present) parents.
1893
todo = set(self.revisions.keys())
1894
done = self.absent_revisions.copy()
1897
# scan through looking for a revision whose parents
1899
for rev_id in sorted(list(todo)):
1900
rev = self.revisions[rev_id]
1901
parent_ids = set(rev.parent_ids)
1902
if parent_ids.issubset(done):
1903
# can take this one now
1904
order.append(rev_id)
1910
class ConvertBzrDir5To6(Converter):
1911
"""Converts format 5 bzr dirs to format 6."""
1913
def convert(self, to_convert, pb):
1914
"""See Converter.convert()."""
1915
self.bzrdir = to_convert
1917
self.pb.note('starting upgrade from format 5 to 6')
1918
self._convert_to_prefixed()
1919
return BzrDir.open(self.bzrdir.root_transport.base)
1921
def _convert_to_prefixed(self):
1922
from bzrlib.store import TransportStore
1923
self.bzrdir.transport.delete('branch-format')
1924
for store_name in ["weaves", "revision-store"]:
1925
self.pb.note("adding prefixes to %s" % store_name)
1926
store_transport = self.bzrdir.transport.clone(store_name)
1927
store = TransportStore(store_transport, prefixed=True)
1928
for urlfilename in store_transport.list_dir('.'):
1929
filename = urlutils.unescape(urlfilename)
1930
if (filename.endswith(".weave") or
1931
filename.endswith(".gz") or
1932
filename.endswith(".sig")):
1933
file_id = os.path.splitext(filename)[0]
1936
prefix_dir = store.hash_prefix(file_id)
1937
# FIXME keep track of the dirs made RBC 20060121
1939
store_transport.move(filename, prefix_dir + '/' + filename)
1940
except errors.NoSuchFile: # catches missing dirs strangely enough
1941
store_transport.mkdir(prefix_dir)
1942
store_transport.move(filename, prefix_dir + '/' + filename)
1943
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1946
class ConvertBzrDir6ToMeta(Converter):
1947
"""Converts format 6 bzr dirs to metadirs."""
1949
def convert(self, to_convert, pb):
1950
"""See Converter.convert()."""
1951
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1952
from bzrlib.branch import BzrBranchFormat5
1953
self.bzrdir = to_convert
1956
self.total = 20 # the steps we know about
1957
self.garbage_inventories = []
1959
self.pb.note('starting upgrade from format 6 to metadir')
1960
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1961
# its faster to move specific files around than to open and use the apis...
1962
# first off, nuke ancestry.weave, it was never used.
1964
self.step('Removing ancestry.weave')
1965
self.bzrdir.transport.delete('ancestry.weave')
1966
except errors.NoSuchFile:
1968
# find out whats there
1969
self.step('Finding branch files')
1970
last_revision = self.bzrdir.open_branch().last_revision()
1971
bzrcontents = self.bzrdir.transport.list_dir('.')
1972
for name in bzrcontents:
1973
if name.startswith('basis-inventory.'):
1974
self.garbage_inventories.append(name)
1975
# create new directories for repository, working tree and branch
1976
self.dir_mode = self.bzrdir._control_files._dir_mode
1977
self.file_mode = self.bzrdir._control_files._file_mode
1978
repository_names = [('inventory.weave', True),
1979
('revision-store', True),
1981
self.step('Upgrading repository ')
1982
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1983
self.make_lock('repository')
1984
# we hard code the formats here because we are converting into
1985
# the meta format. The meta format upgrader can take this to a
1986
# future format within each component.
1987
self.put_format('repository', RepositoryFormat7())
1988
for entry in repository_names:
1989
self.move_entry('repository', entry)
1991
self.step('Upgrading branch ')
1992
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1993
self.make_lock('branch')
1994
self.put_format('branch', BzrBranchFormat5())
1995
branch_files = [('revision-history', True),
1996
('branch-name', True),
1998
for entry in branch_files:
1999
self.move_entry('branch', entry)
2001
checkout_files = [('pending-merges', True),
2002
('inventory', True),
2003
('stat-cache', False)]
2004
# If a mandatory checkout file is not present, the branch does not have
2005
# a functional checkout. Do not create a checkout in the converted
2007
for name, mandatory in checkout_files:
2008
if mandatory and name not in bzrcontents:
2009
has_checkout = False
2013
if not has_checkout:
2014
self.pb.note('No working tree.')
2015
# If some checkout files are there, we may as well get rid of them.
2016
for name, mandatory in checkout_files:
2017
if name in bzrcontents:
2018
self.bzrdir.transport.delete(name)
2020
from bzrlib.workingtree import WorkingTreeFormat3
2021
self.step('Upgrading working tree')
2022
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2023
self.make_lock('checkout')
2025
'checkout', WorkingTreeFormat3())
2026
self.bzrdir.transport.delete_multi(
2027
self.garbage_inventories, self.pb)
2028
for entry in checkout_files:
2029
self.move_entry('checkout', entry)
2030
if last_revision is not None:
2031
self.bzrdir._control_files.put_utf8(
2032
'checkout/last-revision', last_revision)
2033
self.bzrdir._control_files.put_utf8(
2034
'branch-format', BzrDirMetaFormat1().get_format_string())
2035
return BzrDir.open(self.bzrdir.root_transport.base)
2037
def make_lock(self, name):
2038
"""Make a lock for the new control dir name."""
2039
self.step('Make %s lock' % name)
2040
ld = lockdir.LockDir(self.bzrdir.transport,
2042
file_modebits=self.file_mode,
2043
dir_modebits=self.dir_mode)
2046
def move_entry(self, new_dir, entry):
2047
"""Move then entry name into new_dir."""
2049
mandatory = entry[1]
2050
self.step('Moving %s' % name)
2052
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2053
except errors.NoSuchFile:
2057
def put_format(self, dirname, format):
2058
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2061
class ConvertMetaToMeta(Converter):
2062
"""Converts the components of metadirs."""
2064
def __init__(self, target_format):
2065
"""Create a metadir to metadir converter.
2067
:param target_format: The final metadir format that is desired.
2069
self.target_format = target_format
2071
def convert(self, to_convert, pb):
2072
"""See Converter.convert()."""
2073
self.bzrdir = to_convert
2077
self.step('checking repository format')
2079
repo = self.bzrdir.open_repository()
2080
except errors.NoRepositoryPresent:
2083
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2084
from bzrlib.repository import CopyConverter
2085
self.pb.note('starting repository conversion')
2086
converter = CopyConverter(self.target_format.repository_format)
2087
converter.convert(repo, pb)
2089
branch = self.bzrdir.open_branch()
2090
except errors.NotBranchError:
2093
# TODO: conversions of Branch and Tree should be done by
2094
# InterXFormat lookups
2095
# Avoid circular imports
2096
from bzrlib import branch as _mod_branch
2097
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2098
self.target_format.get_branch_format().__class__ is
2099
_mod_branch.BzrBranchFormat6):
2100
branch_converter = _mod_branch.Converter5to6()
2101
branch_converter.convert(branch)
2103
tree = self.bzrdir.open_workingtree()
2104
except (errors.NoWorkingTree, errors.NotLocalUrl):
2107
# TODO: conversions of Branch and Tree should be done by
2108
# InterXFormat lookups
2109
if (isinstance(tree, workingtree.WorkingTree3) and
2110
not isinstance(tree, workingtree_4.WorkingTree4) and
2111
isinstance(self.target_format.workingtree_format,
2112
workingtree_4.WorkingTreeFormat4)):
2113
workingtree_4.Converter3to4().convert(tree)
2117
class BzrDirFormatInfo(object):
2119
def __init__(self, native, deprecated):
2120
self.deprecated = deprecated
2121
self.native = native
2124
class BzrDirFormatRegistry(registry.Registry):
2125
"""Registry of user-selectable BzrDir subformats.
2127
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2128
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2131
def register_metadir(self, key,
2132
repository_format, help, native=True, deprecated=False,
2135
"""Register a metadir subformat.
2137
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2138
by the Repository format.
2140
:param repository_format: The fully-qualified repository format class
2142
:param branch_format: Fully-qualified branch format class name as
2144
:param tree_format: Fully-qualified tree format class name as
2147
# This should be expanded to support setting WorkingTree and Branch
2148
# formats, once BzrDirMetaFormat1 supports that.
2149
def _load(full_name):
2150
mod_name, factory_name = full_name.rsplit('.', 1)
2152
mod = __import__(mod_name, globals(), locals(),
2154
except ImportError, e:
2155
raise ImportError('failed to load %s: %s' % (full_name, e))
2157
factory = getattr(mod, factory_name)
2158
except AttributeError:
2159
raise AttributeError('no factory %s in module %r'
2164
bd = BzrDirMetaFormat1()
2165
if branch_format is not None:
2166
bd.set_branch_format(_load(branch_format))
2167
if tree_format is not None:
2168
bd.workingtree_format = _load(tree_format)
2169
if repository_format is not None:
2170
bd.repository_format = _load(repository_format)
2172
self.register(key, helper, help, native, deprecated)
2174
def register(self, key, factory, help, native=True, deprecated=False):
2175
"""Register a BzrDirFormat factory.
2177
The factory must be a callable that takes one parameter: the key.
2178
It must produce an instance of the BzrDirFormat when called.
2180
This function mainly exists to prevent the info object from being
2183
registry.Registry.register(self, key, factory, help,
2184
BzrDirFormatInfo(native, deprecated))
2186
def register_lazy(self, key, module_name, member_name, help, native=True,
2188
registry.Registry.register_lazy(self, key, module_name, member_name,
2189
help, BzrDirFormatInfo(native, deprecated))
2191
def set_default(self, key):
2192
"""Set the 'default' key to be a clone of the supplied key.
2194
This method must be called once and only once.
2196
registry.Registry.register(self, 'default', self.get(key),
2197
self.get_help(key), info=self.get_info(key))
2199
def set_default_repository(self, key):
2200
"""Set the FormatRegistry default and Repository default.
2202
This is a transitional method while Repository.set_default_format
2205
if 'default' in self:
2206
self.remove('default')
2207
self.set_default(key)
2208
format = self.get('default')()
2209
assert isinstance(format, BzrDirMetaFormat1)
2211
def make_bzrdir(self, key):
2212
return self.get(key)()
2214
def help_topic(self, topic):
2215
output = textwrap.dedent("""\
2216
Bazaar directory formats
2217
------------------------
2219
These formats can be used for creating branches, working trees, and
2223
default_help = self.get_help('default')
2225
for key in self.keys():
2226
if key == 'default':
2228
help = self.get_help(key)
2229
if help == default_help:
2230
default_realkey = key
2232
help_pairs.append((key, help))
2234
def wrapped(key, help, info):
2236
help = '(native) ' + help
2237
return ' %s:\n%s\n\n' % (key,
2238
textwrap.fill(help, initial_indent=' ',
2239
subsequent_indent=' '))
2240
output += wrapped('%s/default' % default_realkey, default_help,
2241
self.get_info('default'))
2242
deprecated_pairs = []
2243
for key, help in help_pairs:
2244
info = self.get_info(key)
2246
deprecated_pairs.append((key, help))
2248
output += wrapped(key, help, info)
2249
if len(deprecated_pairs) > 0:
2250
output += "Deprecated formats\n------------------\n\n"
2251
for key, help in deprecated_pairs:
2252
info = self.get_info(key)
2253
output += wrapped(key, help, info)
2258
format_registry = BzrDirFormatRegistry()
2259
format_registry.register('weave', BzrDirFormat6,
2260
'Pre-0.8 format. Slower than knit and does not'
2261
' support checkouts or shared repositories.',
2263
format_registry.register_metadir('knit',
2264
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2265
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2266
branch_format='bzrlib.branch.BzrBranchFormat5',
2267
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2268
format_registry.register_metadir('metaweave',
2269
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2270
'Transitional format in 0.8. Slower than knit.',
2271
branch_format='bzrlib.branch.BzrBranchFormat5',
2272
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2274
format_registry.register_metadir('dirstate',
2275
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2276
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2277
'above when accessed over the network.',
2278
branch_format='bzrlib.branch.BzrBranchFormat5',
2279
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2280
# directly from workingtree_4 triggers a circular import.
2281
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2283
format_registry.register_metadir('dirstate-with-subtree',
2284
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2285
help='New in 0.15: Fast local operations and improved scaling for '
2286
'network operations. Additionally adds support for versioning nested '
2287
'bzr branches. Incompatible with bzr < 0.15.',
2288
branch_format='bzrlib.branch.BzrBranchFormat6',
2289
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2291
format_registry.set_default('dirstate')