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
basis = wt.basis_tree()
773
subtrees = basis.iter_references()
774
recurse_branch = wt.branch
775
elif source_branch is not None:
776
basis = source_branch.basis_tree()
778
subtrees = basis.iter_references()
779
recurse_branch = source_branch
784
for path, file_id in subtrees:
785
target = urlutils.join(url, urlutils.escape(path))
786
sublocation = source_branch.reference_parent(file_id, path)
787
sublocation.bzrdir.sprout(target,
788
basis.get_reference_revision(file_id, path),
789
force_new_repo=force_new_repo, recurse=recurse)
791
if basis is not None:
796
class BzrDirPreSplitOut(BzrDir):
797
"""A common class for the all-in-one formats."""
799
def __init__(self, _transport, _format):
800
"""See BzrDir.__init__."""
801
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
802
assert self._format._lock_class == lockable_files.TransportLock
803
assert self._format._lock_file_name == 'branch-lock'
804
self._control_files = lockable_files.LockableFiles(
805
self.get_branch_transport(None),
806
self._format._lock_file_name,
807
self._format._lock_class)
809
def break_lock(self):
810
"""Pre-splitout bzrdirs do not suffer from stale locks."""
811
raise NotImplementedError(self.break_lock)
813
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
814
"""See BzrDir.clone()."""
815
from bzrlib.workingtree import WorkingTreeFormat2
817
result = self._format._initialize_for_clone(url)
818
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
819
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
820
from_branch = self.open_branch()
821
from_branch.clone(result, revision_id=revision_id)
823
self.open_workingtree().clone(result, basis=basis_tree)
824
except errors.NotLocalUrl:
825
# make a new one, this format always has to have one.
827
WorkingTreeFormat2().initialize(result)
828
except errors.NotLocalUrl:
829
# but we cannot do it for remote trees.
830
to_branch = result.open_branch()
831
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
834
def create_branch(self):
835
"""See BzrDir.create_branch."""
836
return self.open_branch()
838
def create_repository(self, shared=False):
839
"""See BzrDir.create_repository."""
841
raise errors.IncompatibleFormat('shared repository', self._format)
842
return self.open_repository()
844
def create_workingtree(self, revision_id=None):
845
"""See BzrDir.create_workingtree."""
846
# this looks buggy but is not -really-
847
# clone and sprout will have set the revision_id
848
# and that will have set it for us, its only
849
# specific uses of create_workingtree in isolation
850
# that can do wonky stuff here, and that only
851
# happens for creating checkouts, which cannot be
852
# done on this format anyway. So - acceptable wart.
853
result = self.open_workingtree()
854
if revision_id is not None:
855
if revision_id == _mod_revision.NULL_REVISION:
856
result.set_parent_ids([])
858
result.set_parent_ids([revision_id])
861
def destroy_workingtree(self):
862
"""See BzrDir.destroy_workingtree."""
863
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
865
def destroy_workingtree_metadata(self):
866
"""See BzrDir.destroy_workingtree_metadata."""
867
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
870
def get_branch_transport(self, branch_format):
871
"""See BzrDir.get_branch_transport()."""
872
if branch_format is None:
873
return self.transport
875
branch_format.get_format_string()
876
except NotImplementedError:
877
return self.transport
878
raise errors.IncompatibleFormat(branch_format, self._format)
880
def get_repository_transport(self, repository_format):
881
"""See BzrDir.get_repository_transport()."""
882
if repository_format is None:
883
return self.transport
885
repository_format.get_format_string()
886
except NotImplementedError:
887
return self.transport
888
raise errors.IncompatibleFormat(repository_format, self._format)
890
def get_workingtree_transport(self, workingtree_format):
891
"""See BzrDir.get_workingtree_transport()."""
892
if workingtree_format is None:
893
return self.transport
895
workingtree_format.get_format_string()
896
except NotImplementedError:
897
return self.transport
898
raise errors.IncompatibleFormat(workingtree_format, self._format)
900
def needs_format_conversion(self, format=None):
901
"""See BzrDir.needs_format_conversion()."""
902
# if the format is not the same as the system default,
903
# an upgrade is needed.
905
format = BzrDirFormat.get_default_format()
906
return not isinstance(self._format, format.__class__)
908
def open_branch(self, unsupported=False):
909
"""See BzrDir.open_branch."""
910
from bzrlib.branch import BzrBranchFormat4
911
format = BzrBranchFormat4()
912
self._check_supported(format, unsupported)
913
return format.open(self, _found=True)
915
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
916
"""See BzrDir.sprout()."""
917
from bzrlib.workingtree import WorkingTreeFormat2
919
result = self._format._initialize_for_clone(url)
920
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
922
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
923
except errors.NoRepositoryPresent:
926
self.open_branch().sprout(result, revision_id=revision_id)
927
except errors.NotBranchError:
929
# we always want a working tree
930
WorkingTreeFormat2().initialize(result)
934
class BzrDir4(BzrDirPreSplitOut):
935
"""A .bzr version 4 control object.
937
This is a deprecated format and may be removed after sept 2006.
940
def create_repository(self, shared=False):
941
"""See BzrDir.create_repository."""
942
return self._format.repository_format.initialize(self, shared)
944
def needs_format_conversion(self, format=None):
945
"""Format 4 dirs are always in need of conversion."""
948
def open_repository(self):
949
"""See BzrDir.open_repository."""
950
from bzrlib.repofmt.weaverepo import RepositoryFormat4
951
return RepositoryFormat4().open(self, _found=True)
954
class BzrDir5(BzrDirPreSplitOut):
955
"""A .bzr version 5 control object.
957
This is a deprecated format and may be removed after sept 2006.
960
def open_repository(self):
961
"""See BzrDir.open_repository."""
962
from bzrlib.repofmt.weaverepo import RepositoryFormat5
963
return RepositoryFormat5().open(self, _found=True)
965
def open_workingtree(self, _unsupported=False):
966
"""See BzrDir.create_workingtree."""
967
from bzrlib.workingtree import WorkingTreeFormat2
968
return WorkingTreeFormat2().open(self, _found=True)
971
class BzrDir6(BzrDirPreSplitOut):
972
"""A .bzr version 6 control object.
974
This is a deprecated format and may be removed after sept 2006.
977
def open_repository(self):
978
"""See BzrDir.open_repository."""
979
from bzrlib.repofmt.weaverepo import RepositoryFormat6
980
return RepositoryFormat6().open(self, _found=True)
982
def open_workingtree(self, _unsupported=False):
983
"""See BzrDir.create_workingtree."""
984
from bzrlib.workingtree import WorkingTreeFormat2
985
return WorkingTreeFormat2().open(self, _found=True)
988
class BzrDirMeta1(BzrDir):
989
"""A .bzr meta version 1 control object.
991
This is the first control object where the
992
individual aspects are really split out: there are separate repository,
993
workingtree and branch subdirectories and any subset of the three can be
994
present within a BzrDir.
997
def can_convert_format(self):
998
"""See BzrDir.can_convert_format()."""
1001
def create_branch(self):
1002
"""See BzrDir.create_branch."""
1003
return self._format.get_branch_format().initialize(self)
1005
def create_repository(self, shared=False):
1006
"""See BzrDir.create_repository."""
1007
return self._format.repository_format.initialize(self, shared)
1009
def create_workingtree(self, revision_id=None):
1010
"""See BzrDir.create_workingtree."""
1011
from bzrlib.workingtree import WorkingTreeFormat
1012
return self._format.workingtree_format.initialize(self, revision_id)
1014
def destroy_workingtree(self):
1015
"""See BzrDir.destroy_workingtree."""
1016
wt = self.open_workingtree()
1017
repository = wt.branch.repository
1018
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1019
wt.revert([], old_tree=empty)
1020
self.destroy_workingtree_metadata()
1022
def destroy_workingtree_metadata(self):
1023
self.transport.delete_tree('checkout')
1025
def _get_mkdir_mode(self):
1026
"""Figure out the mode to use when creating a bzrdir subdir."""
1027
temp_control = lockable_files.LockableFiles(self.transport, '',
1028
lockable_files.TransportLock)
1029
return temp_control._dir_mode
1031
def get_branch_transport(self, branch_format):
1032
"""See BzrDir.get_branch_transport()."""
1033
if branch_format is None:
1034
return self.transport.clone('branch')
1036
branch_format.get_format_string()
1037
except NotImplementedError:
1038
raise errors.IncompatibleFormat(branch_format, self._format)
1040
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1041
except errors.FileExists:
1043
return self.transport.clone('branch')
1045
def get_repository_transport(self, repository_format):
1046
"""See BzrDir.get_repository_transport()."""
1047
if repository_format is None:
1048
return self.transport.clone('repository')
1050
repository_format.get_format_string()
1051
except NotImplementedError:
1052
raise errors.IncompatibleFormat(repository_format, self._format)
1054
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1055
except errors.FileExists:
1057
return self.transport.clone('repository')
1059
def get_workingtree_transport(self, workingtree_format):
1060
"""See BzrDir.get_workingtree_transport()."""
1061
if workingtree_format is None:
1062
return self.transport.clone('checkout')
1064
workingtree_format.get_format_string()
1065
except NotImplementedError:
1066
raise errors.IncompatibleFormat(workingtree_format, self._format)
1068
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1069
except errors.FileExists:
1071
return self.transport.clone('checkout')
1073
def needs_format_conversion(self, format=None):
1074
"""See BzrDir.needs_format_conversion()."""
1076
format = BzrDirFormat.get_default_format()
1077
if not isinstance(self._format, format.__class__):
1078
# it is not a meta dir format, conversion is needed.
1080
# we might want to push this down to the repository?
1082
if not isinstance(self.open_repository()._format,
1083
format.repository_format.__class__):
1084
# the repository needs an upgrade.
1086
except errors.NoRepositoryPresent:
1089
if not isinstance(self.open_branch()._format,
1090
format.get_branch_format().__class__):
1091
# the branch needs an upgrade.
1093
except errors.NotBranchError:
1096
if not isinstance(self.open_workingtree()._format,
1097
format.workingtree_format.__class__):
1098
# the workingtree needs an upgrade.
1100
except (errors.NoWorkingTree, errors.NotLocalUrl):
1104
def open_branch(self, unsupported=False):
1105
"""See BzrDir.open_branch."""
1106
from bzrlib.branch import BranchFormat
1107
format = BranchFormat.find_format(self)
1108
self._check_supported(format, unsupported)
1109
return format.open(self, _found=True)
1111
def open_repository(self, unsupported=False):
1112
"""See BzrDir.open_repository."""
1113
from bzrlib.repository import RepositoryFormat
1114
format = RepositoryFormat.find_format(self)
1115
self._check_supported(format, unsupported)
1116
return format.open(self, _found=True)
1118
def open_workingtree(self, unsupported=False):
1119
"""See BzrDir.open_workingtree."""
1120
from bzrlib.workingtree import WorkingTreeFormat
1121
format = WorkingTreeFormat.find_format(self)
1122
self._check_supported(format, unsupported)
1123
return format.open(self, _found=True)
1126
class BzrDirFormat(object):
1127
"""An encapsulation of the initialization and open routines for a format.
1129
Formats provide three things:
1130
* An initialization routine,
1134
Formats are placed in an dict by their format string for reference
1135
during bzrdir opening. These should be subclasses of BzrDirFormat
1138
Once a format is deprecated, just deprecate the initialize and open
1139
methods on the format class. Do not deprecate the object, as the
1140
object will be created every system load.
1143
_default_format = None
1144
"""The default format used for new .bzr dirs."""
1147
"""The known formats."""
1149
_control_formats = []
1150
"""The registered control formats - .bzr, ....
1152
This is a list of BzrDirFormat objects.
1155
_lock_file_name = 'branch-lock'
1157
# _lock_class must be set in subclasses to the lock type, typ.
1158
# TransportLock or LockDir
1161
def find_format(klass, transport):
1162
"""Return the format present at transport."""
1163
for format in klass._control_formats:
1165
return format.probe_transport(transport)
1166
except errors.NotBranchError:
1167
# this format does not find a control dir here.
1169
raise errors.NotBranchError(path=transport.base)
1172
def probe_transport(klass, transport):
1173
"""Return the .bzrdir style transport present at URL."""
1175
format_string = transport.get(".bzr/branch-format").read()
1176
except errors.NoSuchFile:
1177
raise errors.NotBranchError(path=transport.base)
1180
return klass._formats[format_string]
1182
raise errors.UnknownFormatError(format=format_string)
1185
def get_default_format(klass):
1186
"""Return the current default format."""
1187
return klass._default_format
1189
def get_format_string(self):
1190
"""Return the ASCII format string that identifies this format."""
1191
raise NotImplementedError(self.get_format_string)
1193
def get_format_description(self):
1194
"""Return the short description for this format."""
1195
raise NotImplementedError(self.get_format_description)
1197
def get_converter(self, format=None):
1198
"""Return the converter to use to convert bzrdirs needing converts.
1200
This returns a bzrlib.bzrdir.Converter object.
1202
This should return the best upgrader to step this format towards the
1203
current default format. In the case of plugins we can/should provide
1204
some means for them to extend the range of returnable converters.
1206
:param format: Optional format to override the default format of the
1209
raise NotImplementedError(self.get_converter)
1211
def initialize(self, url):
1212
"""Create a bzr control dir at this url and return an opened copy.
1214
Subclasses should typically override initialize_on_transport
1215
instead of this method.
1217
return self.initialize_on_transport(get_transport(url))
1219
def initialize_on_transport(self, transport):
1220
"""Initialize a new bzrdir in the base directory of a Transport."""
1221
# Since we don't have a .bzr directory, inherit the
1222
# mode from the root directory
1223
temp_control = lockable_files.LockableFiles(transport,
1224
'', lockable_files.TransportLock)
1225
temp_control._transport.mkdir('.bzr',
1226
# FIXME: RBC 20060121 don't peek under
1228
mode=temp_control._dir_mode)
1229
file_mode = temp_control._file_mode
1231
mutter('created control directory in ' + transport.base)
1232
control = transport.clone('.bzr')
1233
utf8_files = [('README',
1234
"This is a Bazaar-NG control directory.\n"
1235
"Do not change any files in this directory.\n"),
1236
('branch-format', self.get_format_string()),
1238
# NB: no need to escape relative paths that are url safe.
1239
control_files = lockable_files.LockableFiles(control,
1240
self._lock_file_name, self._lock_class)
1241
control_files.create_lock()
1242
control_files.lock_write()
1244
for file, content in utf8_files:
1245
control_files.put_utf8(file, content)
1247
control_files.unlock()
1248
return self.open(transport, _found=True)
1250
def is_supported(self):
1251
"""Is this format supported?
1253
Supported formats must be initializable and openable.
1254
Unsupported formats may not support initialization or committing or
1255
some other features depending on the reason for not being supported.
1259
def same_model(self, target_format):
1260
return (self.repository_format.rich_root_data ==
1261
target_format.rich_root_data)
1264
def known_formats(klass):
1265
"""Return all the known formats.
1267
Concrete formats should override _known_formats.
1269
# There is double indirection here to make sure that control
1270
# formats used by more than one dir format will only be probed
1271
# once. This can otherwise be quite expensive for remote connections.
1273
for format in klass._control_formats:
1274
result.update(format._known_formats())
1278
def _known_formats(klass):
1279
"""Return the known format instances for this control format."""
1280
return set(klass._formats.values())
1282
def open(self, transport, _found=False):
1283
"""Return an instance of this format for the dir transport points at.
1285
_found is a private parameter, do not use it.
1288
found_format = BzrDirFormat.find_format(transport)
1289
if not isinstance(found_format, self.__class__):
1290
raise AssertionError("%s was asked to open %s, but it seems to need "
1292
% (self, transport, found_format))
1293
return self._open(transport)
1295
def _open(self, transport):
1296
"""Template method helper for opening BzrDirectories.
1298
This performs the actual open and any additional logic or parameter
1301
raise NotImplementedError(self._open)
1304
def register_format(klass, format):
1305
klass._formats[format.get_format_string()] = format
1308
def register_control_format(klass, format):
1309
"""Register a format that does not use '.bzrdir' for its control dir.
1311
TODO: This should be pulled up into a 'ControlDirFormat' base class
1312
which BzrDirFormat can inherit from, and renamed to register_format
1313
there. It has been done without that for now for simplicity of
1316
klass._control_formats.append(format)
1319
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1320
def set_default_format(klass, format):
1321
klass._set_default_format(format)
1324
def _set_default_format(klass, format):
1325
"""Set default format (for testing behavior of defaults only)"""
1326
klass._default_format = format
1329
return self.get_format_string()[:-1]
1332
def unregister_format(klass, format):
1333
assert klass._formats[format.get_format_string()] is format
1334
del klass._formats[format.get_format_string()]
1337
def unregister_control_format(klass, format):
1338
klass._control_formats.remove(format)
1341
# register BzrDirFormat as a control format
1342
BzrDirFormat.register_control_format(BzrDirFormat)
1345
class BzrDirFormat4(BzrDirFormat):
1346
"""Bzr dir format 4.
1348
This format is a combined format for working tree, branch and repository.
1350
- Format 1 working trees [always]
1351
- Format 4 branches [always]
1352
- Format 4 repositories [always]
1354
This format is deprecated: it indexes texts using a text it which is
1355
removed in format 5; write support for this format has been removed.
1358
_lock_class = lockable_files.TransportLock
1360
def get_format_string(self):
1361
"""See BzrDirFormat.get_format_string()."""
1362
return "Bazaar-NG branch, format 0.0.4\n"
1364
def get_format_description(self):
1365
"""See BzrDirFormat.get_format_description()."""
1366
return "All-in-one format 4"
1368
def get_converter(self, format=None):
1369
"""See BzrDirFormat.get_converter()."""
1370
# there is one and only one upgrade path here.
1371
return ConvertBzrDir4To5()
1373
def initialize_on_transport(self, transport):
1374
"""Format 4 branches cannot be created."""
1375
raise errors.UninitializableFormat(self)
1377
def is_supported(self):
1378
"""Format 4 is not supported.
1380
It is not supported because the model changed from 4 to 5 and the
1381
conversion logic is expensive - so doing it on the fly was not
1386
def _open(self, transport):
1387
"""See BzrDirFormat._open."""
1388
return BzrDir4(transport, self)
1390
def __return_repository_format(self):
1391
"""Circular import protection."""
1392
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1393
return RepositoryFormat4()
1394
repository_format = property(__return_repository_format)
1397
class BzrDirFormat5(BzrDirFormat):
1398
"""Bzr control format 5.
1400
This format is a combined format for working tree, branch and repository.
1402
- Format 2 working trees [always]
1403
- Format 4 branches [always]
1404
- Format 5 repositories [always]
1405
Unhashed stores in the repository.
1408
_lock_class = lockable_files.TransportLock
1410
def get_format_string(self):
1411
"""See BzrDirFormat.get_format_string()."""
1412
return "Bazaar-NG branch, format 5\n"
1414
def get_format_description(self):
1415
"""See BzrDirFormat.get_format_description()."""
1416
return "All-in-one format 5"
1418
def get_converter(self, format=None):
1419
"""See BzrDirFormat.get_converter()."""
1420
# there is one and only one upgrade path here.
1421
return ConvertBzrDir5To6()
1423
def _initialize_for_clone(self, url):
1424
return self.initialize_on_transport(get_transport(url), _cloning=True)
1426
def initialize_on_transport(self, transport, _cloning=False):
1427
"""Format 5 dirs always have working tree, branch and repository.
1429
Except when they are being cloned.
1431
from bzrlib.branch import BzrBranchFormat4
1432
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1433
from bzrlib.workingtree import WorkingTreeFormat2
1434
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1435
RepositoryFormat5().initialize(result, _internal=True)
1437
branch = BzrBranchFormat4().initialize(result)
1439
WorkingTreeFormat2().initialize(result)
1440
except errors.NotLocalUrl:
1441
# Even though we can't access the working tree, we need to
1442
# create its control files.
1443
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1446
def _open(self, transport):
1447
"""See BzrDirFormat._open."""
1448
return BzrDir5(transport, self)
1450
def __return_repository_format(self):
1451
"""Circular import protection."""
1452
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1453
return RepositoryFormat5()
1454
repository_format = property(__return_repository_format)
1457
class BzrDirFormat6(BzrDirFormat):
1458
"""Bzr control format 6.
1460
This format is a combined format for working tree, branch and repository.
1462
- Format 2 working trees [always]
1463
- Format 4 branches [always]
1464
- Format 6 repositories [always]
1467
_lock_class = lockable_files.TransportLock
1469
def get_format_string(self):
1470
"""See BzrDirFormat.get_format_string()."""
1471
return "Bazaar-NG branch, format 6\n"
1473
def get_format_description(self):
1474
"""See BzrDirFormat.get_format_description()."""
1475
return "All-in-one format 6"
1477
def get_converter(self, format=None):
1478
"""See BzrDirFormat.get_converter()."""
1479
# there is one and only one upgrade path here.
1480
return ConvertBzrDir6ToMeta()
1482
def _initialize_for_clone(self, url):
1483
return self.initialize_on_transport(get_transport(url), _cloning=True)
1485
def initialize_on_transport(self, transport, _cloning=False):
1486
"""Format 6 dirs always have working tree, branch and repository.
1488
Except when they are being cloned.
1490
from bzrlib.branch import BzrBranchFormat4
1491
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1492
from bzrlib.workingtree import WorkingTreeFormat2
1493
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1494
RepositoryFormat6().initialize(result, _internal=True)
1496
branch = BzrBranchFormat4().initialize(result)
1498
WorkingTreeFormat2().initialize(result)
1499
except errors.NotLocalUrl:
1500
# Even though we can't access the working tree, we need to
1501
# create its control files.
1502
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1505
def _open(self, transport):
1506
"""See BzrDirFormat._open."""
1507
return BzrDir6(transport, self)
1509
def __return_repository_format(self):
1510
"""Circular import protection."""
1511
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1512
return RepositoryFormat6()
1513
repository_format = property(__return_repository_format)
1516
class BzrDirMetaFormat1(BzrDirFormat):
1517
"""Bzr meta control format 1
1519
This is the first format with split out working tree, branch and repository
1522
- Format 3 working trees [optional]
1523
- Format 5 branches [optional]
1524
- Format 7 repositories [optional]
1527
_lock_class = lockdir.LockDir
1530
self._workingtree_format = None
1531
self._branch_format = None
1533
def __eq__(self, other):
1534
if other.__class__ is not self.__class__:
1536
if other.repository_format != self.repository_format:
1538
if other.workingtree_format != self.workingtree_format:
1542
def __ne__(self, other):
1543
return not self == other
1545
def get_branch_format(self):
1546
if self._branch_format is None:
1547
from bzrlib.branch import BranchFormat
1548
self._branch_format = BranchFormat.get_default_format()
1549
return self._branch_format
1551
def set_branch_format(self, format):
1552
self._branch_format = format
1554
def get_converter(self, format=None):
1555
"""See BzrDirFormat.get_converter()."""
1557
format = BzrDirFormat.get_default_format()
1558
if not isinstance(self, format.__class__):
1559
# converting away from metadir is not implemented
1560
raise NotImplementedError(self.get_converter)
1561
return ConvertMetaToMeta(format)
1563
def get_format_string(self):
1564
"""See BzrDirFormat.get_format_string()."""
1565
return "Bazaar-NG meta directory, format 1\n"
1567
def get_format_description(self):
1568
"""See BzrDirFormat.get_format_description()."""
1569
return "Meta directory format 1"
1571
def _open(self, transport):
1572
"""See BzrDirFormat._open."""
1573
return BzrDirMeta1(transport, self)
1575
def __return_repository_format(self):
1576
"""Circular import protection."""
1577
if getattr(self, '_repository_format', None):
1578
return self._repository_format
1579
from bzrlib.repository import RepositoryFormat
1580
return RepositoryFormat.get_default_format()
1582
def __set_repository_format(self, value):
1583
"""Allow changint the repository format for metadir formats."""
1584
self._repository_format = value
1586
repository_format = property(__return_repository_format, __set_repository_format)
1588
def __get_workingtree_format(self):
1589
if self._workingtree_format is None:
1590
from bzrlib.workingtree import WorkingTreeFormat
1591
self._workingtree_format = WorkingTreeFormat.get_default_format()
1592
return self._workingtree_format
1594
def __set_workingtree_format(self, wt_format):
1595
self._workingtree_format = wt_format
1597
workingtree_format = property(__get_workingtree_format,
1598
__set_workingtree_format)
1601
BzrDirFormat.register_format(BzrDirFormat4())
1602
BzrDirFormat.register_format(BzrDirFormat5())
1603
BzrDirFormat.register_format(BzrDirFormat6())
1604
__default_format = BzrDirMetaFormat1()
1605
BzrDirFormat.register_format(__default_format)
1606
BzrDirFormat._default_format = __default_format
1609
class BzrDirTestProviderAdapter(object):
1610
"""A tool to generate a suite testing multiple bzrdir formats at once.
1612
This is done by copying the test once for each transport and injecting
1613
the transport_server, transport_readonly_server, and bzrdir_format
1614
classes into each copy. Each copy is also given a new id() to make it
1618
def __init__(self, transport_server, transport_readonly_server, formats):
1619
self._transport_server = transport_server
1620
self._transport_readonly_server = transport_readonly_server
1621
self._formats = formats
1623
def adapt(self, test):
1624
result = unittest.TestSuite()
1625
for format in self._formats:
1626
new_test = deepcopy(test)
1627
new_test.transport_server = self._transport_server
1628
new_test.transport_readonly_server = self._transport_readonly_server
1629
new_test.bzrdir_format = format
1630
def make_new_test_id():
1631
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1632
return lambda: new_id
1633
new_test.id = make_new_test_id()
1634
result.addTest(new_test)
1638
class Converter(object):
1639
"""Converts a disk format object from one format to another."""
1641
def convert(self, to_convert, pb):
1642
"""Perform the conversion of to_convert, giving feedback via pb.
1644
:param to_convert: The disk object to convert.
1645
:param pb: a progress bar to use for progress information.
1648
def step(self, message):
1649
"""Update the pb by a step."""
1651
self.pb.update(message, self.count, self.total)
1654
class ConvertBzrDir4To5(Converter):
1655
"""Converts format 4 bzr dirs to format 5."""
1658
super(ConvertBzrDir4To5, self).__init__()
1659
self.converted_revs = set()
1660
self.absent_revisions = set()
1664
def convert(self, to_convert, pb):
1665
"""See Converter.convert()."""
1666
self.bzrdir = to_convert
1668
self.pb.note('starting upgrade from format 4 to 5')
1669
if isinstance(self.bzrdir.transport, LocalTransport):
1670
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1671
self._convert_to_weaves()
1672
return BzrDir.open(self.bzrdir.root_transport.base)
1674
def _convert_to_weaves(self):
1675
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1678
stat = self.bzrdir.transport.stat('weaves')
1679
if not S_ISDIR(stat.st_mode):
1680
self.bzrdir.transport.delete('weaves')
1681
self.bzrdir.transport.mkdir('weaves')
1682
except errors.NoSuchFile:
1683
self.bzrdir.transport.mkdir('weaves')
1684
# deliberately not a WeaveFile as we want to build it up slowly.
1685
self.inv_weave = Weave('inventory')
1686
# holds in-memory weaves for all files
1687
self.text_weaves = {}
1688
self.bzrdir.transport.delete('branch-format')
1689
self.branch = self.bzrdir.open_branch()
1690
self._convert_working_inv()
1691
rev_history = self.branch.revision_history()
1692
# to_read is a stack holding the revisions we still need to process;
1693
# appending to it adds new highest-priority revisions
1694
self.known_revisions = set(rev_history)
1695
self.to_read = rev_history[-1:]
1697
rev_id = self.to_read.pop()
1698
if (rev_id not in self.revisions
1699
and rev_id not in self.absent_revisions):
1700
self._load_one_rev(rev_id)
1702
to_import = self._make_order()
1703
for i, rev_id in enumerate(to_import):
1704
self.pb.update('converting revision', i, len(to_import))
1705
self._convert_one_rev(rev_id)
1707
self._write_all_weaves()
1708
self._write_all_revs()
1709
self.pb.note('upgraded to weaves:')
1710
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1711
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1712
self.pb.note(' %6d texts', self.text_count)
1713
self._cleanup_spare_files_after_format4()
1714
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1716
def _cleanup_spare_files_after_format4(self):
1717
# FIXME working tree upgrade foo.
1718
for n in 'merged-patches', 'pending-merged-patches':
1720
## assert os.path.getsize(p) == 0
1721
self.bzrdir.transport.delete(n)
1722
except errors.NoSuchFile:
1724
self.bzrdir.transport.delete_tree('inventory-store')
1725
self.bzrdir.transport.delete_tree('text-store')
1727
def _convert_working_inv(self):
1728
inv = xml4.serializer_v4.read_inventory(
1729
self.branch.control_files.get('inventory'))
1730
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1731
# FIXME inventory is a working tree change.
1732
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1734
def _write_all_weaves(self):
1735
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1736
weave_transport = self.bzrdir.transport.clone('weaves')
1737
weaves = WeaveStore(weave_transport, prefixed=False)
1738
transaction = WriteTransaction()
1742
for file_id, file_weave in self.text_weaves.items():
1743
self.pb.update('writing weave', i, len(self.text_weaves))
1744
weaves._put_weave(file_id, file_weave, transaction)
1746
self.pb.update('inventory', 0, 1)
1747
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1748
self.pb.update('inventory', 1, 1)
1752
def _write_all_revs(self):
1753
"""Write all revisions out in new form."""
1754
self.bzrdir.transport.delete_tree('revision-store')
1755
self.bzrdir.transport.mkdir('revision-store')
1756
revision_transport = self.bzrdir.transport.clone('revision-store')
1758
_revision_store = TextRevisionStore(TextStore(revision_transport,
1762
transaction = WriteTransaction()
1763
for i, rev_id in enumerate(self.converted_revs):
1764
self.pb.update('write revision', i, len(self.converted_revs))
1765
_revision_store.add_revision(self.revisions[rev_id], transaction)
1769
def _load_one_rev(self, rev_id):
1770
"""Load a revision object into memory.
1772
Any parents not either loaded or abandoned get queued to be
1774
self.pb.update('loading revision',
1775
len(self.revisions),
1776
len(self.known_revisions))
1777
if not self.branch.repository.has_revision(rev_id):
1779
self.pb.note('revision {%s} not present in branch; '
1780
'will be converted as a ghost',
1782
self.absent_revisions.add(rev_id)
1784
rev = self.branch.repository._revision_store.get_revision(rev_id,
1785
self.branch.repository.get_transaction())
1786
for parent_id in rev.parent_ids:
1787
self.known_revisions.add(parent_id)
1788
self.to_read.append(parent_id)
1789
self.revisions[rev_id] = rev
1791
def _load_old_inventory(self, rev_id):
1792
assert rev_id not in self.converted_revs
1793
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1794
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1795
inv.revision_id = rev_id
1796
rev = self.revisions[rev_id]
1797
if rev.inventory_sha1:
1798
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1799
'inventory sha mismatch for {%s}' % rev_id
1802
def _load_updated_inventory(self, rev_id):
1803
assert rev_id in self.converted_revs
1804
inv_xml = self.inv_weave.get_text(rev_id)
1805
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1808
def _convert_one_rev(self, rev_id):
1809
"""Convert revision and all referenced objects to new format."""
1810
rev = self.revisions[rev_id]
1811
inv = self._load_old_inventory(rev_id)
1812
present_parents = [p for p in rev.parent_ids
1813
if p not in self.absent_revisions]
1814
self._convert_revision_contents(rev, inv, present_parents)
1815
self._store_new_weave(rev, inv, present_parents)
1816
self.converted_revs.add(rev_id)
1818
def _store_new_weave(self, rev, inv, present_parents):
1819
# the XML is now updated with text versions
1821
entries = inv.iter_entries()
1823
for path, ie in entries:
1824
assert getattr(ie, 'revision', None) is not None, \
1825
'no revision on {%s} in {%s}' % \
1826
(file_id, rev.revision_id)
1827
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1828
new_inv_sha1 = sha_string(new_inv_xml)
1829
self.inv_weave.add_lines(rev.revision_id,
1831
new_inv_xml.splitlines(True))
1832
rev.inventory_sha1 = new_inv_sha1
1834
def _convert_revision_contents(self, rev, inv, present_parents):
1835
"""Convert all the files within a revision.
1837
Also upgrade the inventory to refer to the text revision ids."""
1838
rev_id = rev.revision_id
1839
mutter('converting texts of revision {%s}',
1841
parent_invs = map(self._load_updated_inventory, present_parents)
1842
entries = inv.iter_entries()
1844
for path, ie in entries:
1845
self._convert_file_version(rev, ie, parent_invs)
1847
def _convert_file_version(self, rev, ie, parent_invs):
1848
"""Convert one version of one file.
1850
The file needs to be added into the weave if it is a merge
1851
of >=2 parents or if it's changed from its parent.
1853
file_id = ie.file_id
1854
rev_id = rev.revision_id
1855
w = self.text_weaves.get(file_id)
1858
self.text_weaves[file_id] = w
1859
text_changed = False
1860
previous_entries = ie.find_previous_heads(parent_invs,
1864
for old_revision in previous_entries:
1865
# if this fails, its a ghost ?
1866
assert old_revision in self.converted_revs, \
1867
"Revision {%s} not in converted_revs" % old_revision
1868
self.snapshot_ie(previous_entries, ie, w, rev_id)
1870
assert getattr(ie, 'revision', None) is not None
1872
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1873
# TODO: convert this logic, which is ~= snapshot to
1874
# a call to:. This needs the path figured out. rather than a work_tree
1875
# a v4 revision_tree can be given, or something that looks enough like
1876
# one to give the file content to the entry if it needs it.
1877
# and we need something that looks like a weave store for snapshot to
1879
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1880
if len(previous_revisions) == 1:
1881
previous_ie = previous_revisions.values()[0]
1882
if ie._unchanged(previous_ie):
1883
ie.revision = previous_ie.revision
1886
text = self.branch.repository.text_store.get(ie.text_id)
1887
file_lines = text.readlines()
1888
assert sha_strings(file_lines) == ie.text_sha1
1889
assert sum(map(len, file_lines)) == ie.text_size
1890
w.add_lines(rev_id, previous_revisions, file_lines)
1891
self.text_count += 1
1893
w.add_lines(rev_id, previous_revisions, [])
1894
ie.revision = rev_id
1896
def _make_order(self):
1897
"""Return a suitable order for importing revisions.
1899
The order must be such that an revision is imported after all
1900
its (present) parents.
1902
todo = set(self.revisions.keys())
1903
done = self.absent_revisions.copy()
1906
# scan through looking for a revision whose parents
1908
for rev_id in sorted(list(todo)):
1909
rev = self.revisions[rev_id]
1910
parent_ids = set(rev.parent_ids)
1911
if parent_ids.issubset(done):
1912
# can take this one now
1913
order.append(rev_id)
1919
class ConvertBzrDir5To6(Converter):
1920
"""Converts format 5 bzr dirs to format 6."""
1922
def convert(self, to_convert, pb):
1923
"""See Converter.convert()."""
1924
self.bzrdir = to_convert
1926
self.pb.note('starting upgrade from format 5 to 6')
1927
self._convert_to_prefixed()
1928
return BzrDir.open(self.bzrdir.root_transport.base)
1930
def _convert_to_prefixed(self):
1931
from bzrlib.store import TransportStore
1932
self.bzrdir.transport.delete('branch-format')
1933
for store_name in ["weaves", "revision-store"]:
1934
self.pb.note("adding prefixes to %s" % store_name)
1935
store_transport = self.bzrdir.transport.clone(store_name)
1936
store = TransportStore(store_transport, prefixed=True)
1937
for urlfilename in store_transport.list_dir('.'):
1938
filename = urlutils.unescape(urlfilename)
1939
if (filename.endswith(".weave") or
1940
filename.endswith(".gz") or
1941
filename.endswith(".sig")):
1942
file_id = os.path.splitext(filename)[0]
1945
prefix_dir = store.hash_prefix(file_id)
1946
# FIXME keep track of the dirs made RBC 20060121
1948
store_transport.move(filename, prefix_dir + '/' + filename)
1949
except errors.NoSuchFile: # catches missing dirs strangely enough
1950
store_transport.mkdir(prefix_dir)
1951
store_transport.move(filename, prefix_dir + '/' + filename)
1952
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1955
class ConvertBzrDir6ToMeta(Converter):
1956
"""Converts format 6 bzr dirs to metadirs."""
1958
def convert(self, to_convert, pb):
1959
"""See Converter.convert()."""
1960
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1961
from bzrlib.branch import BzrBranchFormat5
1962
self.bzrdir = to_convert
1965
self.total = 20 # the steps we know about
1966
self.garbage_inventories = []
1968
self.pb.note('starting upgrade from format 6 to metadir')
1969
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1970
# its faster to move specific files around than to open and use the apis...
1971
# first off, nuke ancestry.weave, it was never used.
1973
self.step('Removing ancestry.weave')
1974
self.bzrdir.transport.delete('ancestry.weave')
1975
except errors.NoSuchFile:
1977
# find out whats there
1978
self.step('Finding branch files')
1979
last_revision = self.bzrdir.open_branch().last_revision()
1980
bzrcontents = self.bzrdir.transport.list_dir('.')
1981
for name in bzrcontents:
1982
if name.startswith('basis-inventory.'):
1983
self.garbage_inventories.append(name)
1984
# create new directories for repository, working tree and branch
1985
self.dir_mode = self.bzrdir._control_files._dir_mode
1986
self.file_mode = self.bzrdir._control_files._file_mode
1987
repository_names = [('inventory.weave', True),
1988
('revision-store', True),
1990
self.step('Upgrading repository ')
1991
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1992
self.make_lock('repository')
1993
# we hard code the formats here because we are converting into
1994
# the meta format. The meta format upgrader can take this to a
1995
# future format within each component.
1996
self.put_format('repository', RepositoryFormat7())
1997
for entry in repository_names:
1998
self.move_entry('repository', entry)
2000
self.step('Upgrading branch ')
2001
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2002
self.make_lock('branch')
2003
self.put_format('branch', BzrBranchFormat5())
2004
branch_files = [('revision-history', True),
2005
('branch-name', True),
2007
for entry in branch_files:
2008
self.move_entry('branch', entry)
2010
checkout_files = [('pending-merges', True),
2011
('inventory', True),
2012
('stat-cache', False)]
2013
# If a mandatory checkout file is not present, the branch does not have
2014
# a functional checkout. Do not create a checkout in the converted
2016
for name, mandatory in checkout_files:
2017
if mandatory and name not in bzrcontents:
2018
has_checkout = False
2022
if not has_checkout:
2023
self.pb.note('No working tree.')
2024
# If some checkout files are there, we may as well get rid of them.
2025
for name, mandatory in checkout_files:
2026
if name in bzrcontents:
2027
self.bzrdir.transport.delete(name)
2029
from bzrlib.workingtree import WorkingTreeFormat3
2030
self.step('Upgrading working tree')
2031
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2032
self.make_lock('checkout')
2034
'checkout', WorkingTreeFormat3())
2035
self.bzrdir.transport.delete_multi(
2036
self.garbage_inventories, self.pb)
2037
for entry in checkout_files:
2038
self.move_entry('checkout', entry)
2039
if last_revision is not None:
2040
self.bzrdir._control_files.put_utf8(
2041
'checkout/last-revision', last_revision)
2042
self.bzrdir._control_files.put_utf8(
2043
'branch-format', BzrDirMetaFormat1().get_format_string())
2044
return BzrDir.open(self.bzrdir.root_transport.base)
2046
def make_lock(self, name):
2047
"""Make a lock for the new control dir name."""
2048
self.step('Make %s lock' % name)
2049
ld = lockdir.LockDir(self.bzrdir.transport,
2051
file_modebits=self.file_mode,
2052
dir_modebits=self.dir_mode)
2055
def move_entry(self, new_dir, entry):
2056
"""Move then entry name into new_dir."""
2058
mandatory = entry[1]
2059
self.step('Moving %s' % name)
2061
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2062
except errors.NoSuchFile:
2066
def put_format(self, dirname, format):
2067
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2070
class ConvertMetaToMeta(Converter):
2071
"""Converts the components of metadirs."""
2073
def __init__(self, target_format):
2074
"""Create a metadir to metadir converter.
2076
:param target_format: The final metadir format that is desired.
2078
self.target_format = target_format
2080
def convert(self, to_convert, pb):
2081
"""See Converter.convert()."""
2082
self.bzrdir = to_convert
2086
self.step('checking repository format')
2088
repo = self.bzrdir.open_repository()
2089
except errors.NoRepositoryPresent:
2092
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2093
from bzrlib.repository import CopyConverter
2094
self.pb.note('starting repository conversion')
2095
converter = CopyConverter(self.target_format.repository_format)
2096
converter.convert(repo, pb)
2098
branch = self.bzrdir.open_branch()
2099
except errors.NotBranchError:
2102
# TODO: conversions of Branch and Tree should be done by
2103
# InterXFormat lookups
2104
# Avoid circular imports
2105
from bzrlib import branch as _mod_branch
2106
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2107
self.target_format.get_branch_format().__class__ is
2108
_mod_branch.BzrBranchFormat6):
2109
branch_converter = _mod_branch.Converter5to6()
2110
branch_converter.convert(branch)
2112
tree = self.bzrdir.open_workingtree()
2113
except (errors.NoWorkingTree, errors.NotLocalUrl):
2116
# TODO: conversions of Branch and Tree should be done by
2117
# InterXFormat lookups
2118
if (isinstance(tree, workingtree.WorkingTree3) and
2119
not isinstance(tree, workingtree_4.WorkingTree4) and
2120
isinstance(self.target_format.workingtree_format,
2121
workingtree_4.WorkingTreeFormat4)):
2122
workingtree_4.Converter3to4().convert(tree)
2126
class BzrDirFormatInfo(object):
2128
def __init__(self, native, deprecated):
2129
self.deprecated = deprecated
2130
self.native = native
2133
class BzrDirFormatRegistry(registry.Registry):
2134
"""Registry of user-selectable BzrDir subformats.
2136
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2137
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2140
def register_metadir(self, key,
2141
repository_format, help, native=True, deprecated=False,
2144
"""Register a metadir subformat.
2146
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2147
by the Repository format.
2149
:param repository_format: The fully-qualified repository format class
2151
:param branch_format: Fully-qualified branch format class name as
2153
:param tree_format: Fully-qualified tree format class name as
2156
# This should be expanded to support setting WorkingTree and Branch
2157
# formats, once BzrDirMetaFormat1 supports that.
2158
def _load(full_name):
2159
mod_name, factory_name = full_name.rsplit('.', 1)
2161
mod = __import__(mod_name, globals(), locals(),
2163
except ImportError, e:
2164
raise ImportError('failed to load %s: %s' % (full_name, e))
2166
factory = getattr(mod, factory_name)
2167
except AttributeError:
2168
raise AttributeError('no factory %s in module %r'
2173
bd = BzrDirMetaFormat1()
2174
if branch_format is not None:
2175
bd.set_branch_format(_load(branch_format))
2176
if tree_format is not None:
2177
bd.workingtree_format = _load(tree_format)
2178
if repository_format is not None:
2179
bd.repository_format = _load(repository_format)
2181
self.register(key, helper, help, native, deprecated)
2183
def register(self, key, factory, help, native=True, deprecated=False):
2184
"""Register a BzrDirFormat factory.
2186
The factory must be a callable that takes one parameter: the key.
2187
It must produce an instance of the BzrDirFormat when called.
2189
This function mainly exists to prevent the info object from being
2192
registry.Registry.register(self, key, factory, help,
2193
BzrDirFormatInfo(native, deprecated))
2195
def register_lazy(self, key, module_name, member_name, help, native=True,
2197
registry.Registry.register_lazy(self, key, module_name, member_name,
2198
help, BzrDirFormatInfo(native, deprecated))
2200
def set_default(self, key):
2201
"""Set the 'default' key to be a clone of the supplied key.
2203
This method must be called once and only once.
2205
registry.Registry.register(self, 'default', self.get(key),
2206
self.get_help(key), info=self.get_info(key))
2208
def set_default_repository(self, key):
2209
"""Set the FormatRegistry default and Repository default.
2211
This is a transitional method while Repository.set_default_format
2214
if 'default' in self:
2215
self.remove('default')
2216
self.set_default(key)
2217
format = self.get('default')()
2218
assert isinstance(format, BzrDirMetaFormat1)
2220
def make_bzrdir(self, key):
2221
return self.get(key)()
2223
def help_topic(self, topic):
2224
output = textwrap.dedent("""\
2225
Bazaar directory formats
2226
------------------------
2228
These formats can be used for creating branches, working trees, and
2232
default_help = self.get_help('default')
2234
for key in self.keys():
2235
if key == 'default':
2237
help = self.get_help(key)
2238
if help == default_help:
2239
default_realkey = key
2241
help_pairs.append((key, help))
2243
def wrapped(key, help, info):
2245
help = '(native) ' + help
2246
return ' %s:\n%s\n\n' % (key,
2247
textwrap.fill(help, initial_indent=' ',
2248
subsequent_indent=' '))
2249
output += wrapped('%s/default' % default_realkey, default_help,
2250
self.get_info('default'))
2251
deprecated_pairs = []
2252
for key, help in help_pairs:
2253
info = self.get_info(key)
2255
deprecated_pairs.append((key, help))
2257
output += wrapped(key, help, info)
2258
if len(deprecated_pairs) > 0:
2259
output += "Deprecated formats\n------------------\n\n"
2260
for key, help in deprecated_pairs:
2261
info = self.get_info(key)
2262
output += wrapped(key, help, info)
2267
format_registry = BzrDirFormatRegistry()
2268
format_registry.register('weave', BzrDirFormat6,
2269
'Pre-0.8 format. Slower than knit and does not'
2270
' support checkouts or shared repositories.',
2272
format_registry.register_metadir('knit',
2273
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2274
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2275
branch_format='bzrlib.branch.BzrBranchFormat5',
2276
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2277
format_registry.register_metadir('metaweave',
2278
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2279
'Transitional format in 0.8. Slower than knit.',
2280
branch_format='bzrlib.branch.BzrBranchFormat5',
2281
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2283
format_registry.register_metadir('dirstate',
2284
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2285
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2286
'above when accessed over the network.',
2287
branch_format='bzrlib.branch.BzrBranchFormat5',
2288
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2289
# directly from workingtree_4 triggers a circular import.
2290
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2292
format_registry.register_metadir('dirstate-with-subtree',
2293
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2294
help='New in 0.15: Fast local operations and improved scaling for '
2295
'network operations. Additionally adds support for versioning nested '
2296
'bzr branches. Incompatible with bzr < 0.15.',
2297
branch_format='bzrlib.branch.BzrBranchFormat6',
2298
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2300
format_registry.set_default('dirstate')