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
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 destroy_workingtree(self):
356
"""Destroy the working tree at this BzrDir.
358
Formats that do not support this may raise UnsupportedOperation.
360
raise NotImplementedError(self.destroy_workingtree)
362
def destroy_workingtree_metadata(self):
363
"""Destroy the control files for the working tree at this BzrDir.
365
The contents of working tree files are not affected.
366
Formats that do not support this may raise UnsupportedOperation.
368
raise NotImplementedError(self.destroy_workingtree_metadata)
370
def find_repository(self):
371
"""Find the repository that should be used for a_bzrdir.
373
This does not require a branch as we use it to find the repo for
374
new branches as well as to hook existing branches up to their
378
return self.open_repository()
379
except errors.NoRepositoryPresent:
381
next_transport = self.root_transport.clone('..')
383
# find the next containing bzrdir
385
found_bzrdir = BzrDir.open_containing_from_transport(
387
except errors.NotBranchError:
389
raise errors.NoRepositoryPresent(self)
390
# does it have a repository ?
392
repository = found_bzrdir.open_repository()
393
except errors.NoRepositoryPresent:
394
next_transport = found_bzrdir.root_transport.clone('..')
395
if (found_bzrdir.root_transport.base == next_transport.base):
396
# top of the file system
400
if ((found_bzrdir.root_transport.base ==
401
self.root_transport.base) or repository.is_shared()):
404
raise errors.NoRepositoryPresent(self)
405
raise errors.NoRepositoryPresent(self)
407
def get_branch_transport(self, branch_format):
408
"""Get the transport for use by branch format in this BzrDir.
410
Note that bzr dirs that do not support format strings will raise
411
IncompatibleFormat if the branch format they are given has
412
a format string, and vice versa.
414
If branch_format is None, the transport is returned with no
415
checking. if it is not None, then the returned transport is
416
guaranteed to point to an existing directory ready for use.
418
raise NotImplementedError(self.get_branch_transport)
420
def get_repository_transport(self, repository_format):
421
"""Get the transport for use by repository format in this BzrDir.
423
Note that bzr dirs that do not support format strings will raise
424
IncompatibleFormat if the repository format they are given has
425
a format string, and vice versa.
427
If repository_format is None, the transport is returned with no
428
checking. if it is not None, then the returned transport is
429
guaranteed to point to an existing directory ready for use.
431
raise NotImplementedError(self.get_repository_transport)
433
def get_workingtree_transport(self, tree_format):
434
"""Get the transport for use by workingtree format in this BzrDir.
436
Note that bzr dirs that do not support format strings will raise
437
IncompatibleFormat if the workingtree format they are given has a
438
format string, and vice versa.
440
If workingtree_format is None, the transport is returned with no
441
checking. if it is not None, then the returned transport is
442
guaranteed to point to an existing directory ready for use.
444
raise NotImplementedError(self.get_workingtree_transport)
446
def __init__(self, _transport, _format):
447
"""Initialize a Bzr control dir object.
449
Only really common logic should reside here, concrete classes should be
450
made with varying behaviours.
452
:param _format: the format that is creating this BzrDir instance.
453
:param _transport: the transport this dir is based at.
455
self._format = _format
456
self.transport = _transport.clone('.bzr')
457
self.root_transport = _transport
459
def is_control_filename(self, filename):
460
"""True if filename is the name of a path which is reserved for bzrdir's.
462
:param filename: A filename within the root transport of this bzrdir.
464
This is true IF and ONLY IF the filename is part of the namespace reserved
465
for bzr control dirs. Currently this is the '.bzr' directory in the root
466
of the root_transport. it is expected that plugins will need to extend
467
this in the future - for instance to make bzr talk with svn working
470
# this might be better on the BzrDirFormat class because it refers to
471
# all the possible bzrdir disk formats.
472
# This method is tested via the workingtree is_control_filename tests-
473
# it was extracted from WorkingTree.is_control_filename. If the methods
474
# contract is extended beyond the current trivial implementation please
475
# add new tests for it to the appropriate place.
476
return filename == '.bzr' or filename.startswith('.bzr/')
478
def needs_format_conversion(self, format=None):
479
"""Return true if this bzrdir needs convert_format run on it.
481
For instance, if the repository format is out of date but the
482
branch and working tree are not, this should return True.
484
:param format: Optional parameter indicating a specific desired
485
format we plan to arrive at.
487
raise NotImplementedError(self.needs_format_conversion)
490
def open_unsupported(base):
491
"""Open a branch which is not supported."""
492
return BzrDir.open(base, _unsupported=True)
495
def open(base, _unsupported=False):
496
"""Open an existing bzrdir, rooted at 'base' (url)
498
_unsupported is a private parameter to the BzrDir class.
500
t = get_transport(base)
501
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
504
def open_from_transport(transport, _unsupported=False):
505
"""Open a bzrdir within a particular directory.
507
:param transport: Transport containing the bzrdir.
508
:param _unsupported: private.
510
format = BzrDirFormat.find_format(transport)
511
BzrDir._check_supported(format, _unsupported)
512
return format.open(transport, _found=True)
514
def open_branch(self, unsupported=False):
515
"""Open the branch object at this BzrDir if one is present.
517
If unsupported is True, then no longer supported branch formats can
520
TODO: static convenience version of this?
522
raise NotImplementedError(self.open_branch)
525
def open_containing(url):
526
"""Open an existing branch which contains url.
528
:param url: url to search from.
529
See open_containing_from_transport for more detail.
531
return BzrDir.open_containing_from_transport(get_transport(url))
534
def open_containing_from_transport(a_transport):
535
"""Open an existing branch which contains a_transport.base
537
This probes for a branch at a_transport, and searches upwards from there.
539
Basically we keep looking up until we find the control directory or
540
run into the root. If there isn't one, raises NotBranchError.
541
If there is one and it is either an unrecognised format or an unsupported
542
format, UnknownFormatError or UnsupportedFormatError are raised.
543
If there is one, it is returned, along with the unused portion of url.
545
:return: The BzrDir that contains the path, and a Unicode path
546
for the rest of the URL.
548
# this gets the normalised url back. I.e. '.' -> the full path.
549
url = a_transport.base
552
result = BzrDir.open_from_transport(a_transport)
553
return result, urlutils.unescape(a_transport.relpath(url))
554
except errors.NotBranchError, e:
556
new_t = a_transport.clone('..')
557
if new_t.base == a_transport.base:
558
# reached the root, whatever that may be
559
raise errors.NotBranchError(path=url)
563
def open_containing_tree_or_branch(klass, location):
564
"""Return the branch and working tree contained by a location.
566
Returns (tree, branch, relpath).
567
If there is no tree at containing the location, tree will be None.
568
If there is no branch containing the location, an exception will be
570
relpath is the portion of the path that is contained by the branch.
572
bzrdir, relpath = klass.open_containing(location)
574
tree = bzrdir.open_workingtree()
575
except (errors.NoWorkingTree, errors.NotLocalUrl):
577
branch = bzrdir.open_branch()
580
return tree, branch, relpath
582
def open_repository(self, _unsupported=False):
583
"""Open the repository object at this BzrDir if one is present.
585
This will not follow the Branch object pointer - its strictly a direct
586
open facility. Most client code should use open_branch().repository to
589
_unsupported is a private parameter, not part of the api.
590
TODO: static convenience version of this?
592
raise NotImplementedError(self.open_repository)
594
def open_workingtree(self, _unsupported=False):
595
"""Open the workingtree object at this BzrDir if one is present.
597
TODO: static convenience version of this?
599
raise NotImplementedError(self.open_workingtree)
601
def has_branch(self):
602
"""Tell if this bzrdir contains a branch.
604
Note: if you're going to open the branch, you should just go ahead
605
and try, and not ask permission first. (This method just opens the
606
branch and discards it, and that's somewhat expensive.)
611
except errors.NotBranchError:
614
def has_workingtree(self):
615
"""Tell if this bzrdir contains a working tree.
617
This will still raise an exception if the bzrdir has a workingtree that
618
is remote & inaccessible.
620
Note: if you're going to open the working tree, you should just go ahead
621
and try, and not ask permission first. (This method just opens the
622
workingtree and discards it, and that's somewhat expensive.)
625
self.open_workingtree()
627
except errors.NoWorkingTree:
630
def _cloning_metadir(self, basis=None):
631
def related_repository(bzrdir):
633
branch = bzrdir.open_branch()
634
return branch.repository
635
except errors.NotBranchError:
637
return bzrdir.open_repository()
638
result_format = self._format.__class__()
641
source_repository = related_repository(self)
642
except errors.NoRepositoryPresent:
645
source_repository = related_repository(self)
646
result_format.repository_format = source_repository._format
647
except errors.NoRepositoryPresent:
648
source_repository = None
650
tree = self.open_workingtree()
651
except (errors.NoWorkingTree, errors.NotLocalUrl):
652
result_format.workingtree_format = None
654
result_format.workingtree_format = tree._format.__class__()
655
return result_format, source_repository
657
def cloning_metadir(self, basis=None):
658
"""Produce a metadir suitable for cloning or sprouting with.
660
These operations may produce workingtrees (yes, even though they're
661
"cloning" something that doesn't have a tree, so a viable workingtree
662
format must be selected.
664
format, repository = self._cloning_metadir()
665
if format._workingtree_format is None:
666
if repository is None:
668
tree_format = repository._format._matchingbzrdir.workingtree_format
669
format.workingtree_format = tree_format.__class__()
672
def checkout_metadir(self):
673
return self.cloning_metadir()
675
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
677
"""Create a copy of this bzrdir prepared for use as a new line of
680
If urls last component does not exist, it will be created.
682
Attributes related to the identity of the source branch like
683
branch nickname will be cleaned, a working tree is created
684
whether one existed before or not; and a local branch is always
687
if revision_id is not None, then the clone operation may tune
688
itself to download less data.
691
cloning_format = self.cloning_metadir(basis)
692
result = cloning_format.initialize(url)
693
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
695
source_branch = self.open_branch()
696
source_repository = source_branch.repository
697
except errors.NotBranchError:
700
source_repository = self.open_repository()
701
except errors.NoRepositoryPresent:
702
# copy the entire basis one if there is one
703
# but there is no repository.
704
source_repository = basis_repo
709
result_repo = result.find_repository()
710
except errors.NoRepositoryPresent:
712
if source_repository is None and result_repo is not None:
714
elif source_repository is None and result_repo is None:
715
# no repo available, make a new one
716
result.create_repository()
717
elif source_repository is not None and result_repo is None:
718
# have source, and want to make a new target repo
719
# we don't clone the repo because that preserves attributes
720
# like is_shared(), and we have not yet implemented a
721
# repository sprout().
722
result_repo = result.create_repository()
723
if result_repo is not None:
724
# fetch needed content into target.
726
# XXX FIXME RBC 20060214 need tests for this when the basis
728
result_repo.fetch(basis_repo, revision_id=revision_id)
729
if source_repository is not None:
730
result_repo.fetch(source_repository, revision_id=revision_id)
731
if source_branch is not None:
732
source_branch.sprout(result, revision_id=revision_id)
734
result.create_branch()
735
# TODO: jam 20060426 we probably need a test in here in the
736
# case that the newly sprouted branch is a remote one
737
if result_repo is None or result_repo.make_working_trees():
738
wt = result.create_workingtree()
741
if wt.path2id('') is None:
743
wt.set_root_id(self.open_workingtree.get_root_id())
744
except errors.NoWorkingTree:
750
if recurse == 'down':
752
entries = wt.iter_reference_entries()
753
recurse_branch = wt.branch
754
elif source_branch is not None:
755
entries = source_branch.basis_tree().iter_reference_entries()
756
recurse_branch = source_branch
759
for path, entry in entries:
760
target = urlutils.join(url, urlutils.escape(path))
761
sublocation = source_branch.reference_parent(entry.file_id,
763
sublocation.bzrdir.sprout(target, entry.reference_revision,
764
force_new_repo=force_new_repo, recurse=recurse)
768
class BzrDirPreSplitOut(BzrDir):
769
"""A common class for the all-in-one formats."""
771
def __init__(self, _transport, _format):
772
"""See BzrDir.__init__."""
773
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
774
assert self._format._lock_class == lockable_files.TransportLock
775
assert self._format._lock_file_name == 'branch-lock'
776
self._control_files = lockable_files.LockableFiles(
777
self.get_branch_transport(None),
778
self._format._lock_file_name,
779
self._format._lock_class)
781
def break_lock(self):
782
"""Pre-splitout bzrdirs do not suffer from stale locks."""
783
raise NotImplementedError(self.break_lock)
785
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
786
"""See BzrDir.clone()."""
787
from bzrlib.workingtree import WorkingTreeFormat2
789
result = self._format._initialize_for_clone(url)
790
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
791
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
792
from_branch = self.open_branch()
793
from_branch.clone(result, revision_id=revision_id)
795
self.open_workingtree().clone(result, basis=basis_tree)
796
except errors.NotLocalUrl:
797
# make a new one, this format always has to have one.
799
WorkingTreeFormat2().initialize(result)
800
except errors.NotLocalUrl:
801
# but we cannot do it for remote trees.
802
to_branch = result.open_branch()
803
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
806
def create_branch(self):
807
"""See BzrDir.create_branch."""
808
return self.open_branch()
810
def create_repository(self, shared=False):
811
"""See BzrDir.create_repository."""
813
raise errors.IncompatibleFormat('shared repository', self._format)
814
return self.open_repository()
816
def create_workingtree(self, revision_id=None):
817
"""See BzrDir.create_workingtree."""
818
# this looks buggy but is not -really-
819
# clone and sprout will have set the revision_id
820
# and that will have set it for us, its only
821
# specific uses of create_workingtree in isolation
822
# that can do wonky stuff here, and that only
823
# happens for creating checkouts, which cannot be
824
# done on this format anyway. So - acceptable wart.
825
result = self.open_workingtree()
826
if revision_id is not None:
827
if revision_id == _mod_revision.NULL_REVISION:
828
result.set_parent_ids([])
830
result.set_parent_ids([revision_id])
833
def destroy_workingtree(self):
834
"""See BzrDir.destroy_workingtree."""
835
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
837
def destroy_workingtree_metadata(self):
838
"""See BzrDir.destroy_workingtree_metadata."""
839
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
842
def get_branch_transport(self, branch_format):
843
"""See BzrDir.get_branch_transport()."""
844
if branch_format is None:
845
return self.transport
847
branch_format.get_format_string()
848
except NotImplementedError:
849
return self.transport
850
raise errors.IncompatibleFormat(branch_format, self._format)
852
def get_repository_transport(self, repository_format):
853
"""See BzrDir.get_repository_transport()."""
854
if repository_format is None:
855
return self.transport
857
repository_format.get_format_string()
858
except NotImplementedError:
859
return self.transport
860
raise errors.IncompatibleFormat(repository_format, self._format)
862
def get_workingtree_transport(self, workingtree_format):
863
"""See BzrDir.get_workingtree_transport()."""
864
if workingtree_format is None:
865
return self.transport
867
workingtree_format.get_format_string()
868
except NotImplementedError:
869
return self.transport
870
raise errors.IncompatibleFormat(workingtree_format, self._format)
872
def needs_format_conversion(self, format=None):
873
"""See BzrDir.needs_format_conversion()."""
874
# if the format is not the same as the system default,
875
# an upgrade is needed.
877
format = BzrDirFormat.get_default_format()
878
return not isinstance(self._format, format.__class__)
880
def open_branch(self, unsupported=False):
881
"""See BzrDir.open_branch."""
882
from bzrlib.branch import BzrBranchFormat4
883
format = BzrBranchFormat4()
884
self._check_supported(format, unsupported)
885
return format.open(self, _found=True)
887
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
888
"""See BzrDir.sprout()."""
889
from bzrlib.workingtree import WorkingTreeFormat2
891
result = self._format._initialize_for_clone(url)
892
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
894
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
895
except errors.NoRepositoryPresent:
898
self.open_branch().sprout(result, revision_id=revision_id)
899
except errors.NotBranchError:
901
# we always want a working tree
902
WorkingTreeFormat2().initialize(result)
906
class BzrDir4(BzrDirPreSplitOut):
907
"""A .bzr version 4 control object.
909
This is a deprecated format and may be removed after sept 2006.
912
def create_repository(self, shared=False):
913
"""See BzrDir.create_repository."""
914
return self._format.repository_format.initialize(self, shared)
916
def needs_format_conversion(self, format=None):
917
"""Format 4 dirs are always in need of conversion."""
920
def open_repository(self):
921
"""See BzrDir.open_repository."""
922
from bzrlib.repofmt.weaverepo import RepositoryFormat4
923
return RepositoryFormat4().open(self, _found=True)
926
class BzrDir5(BzrDirPreSplitOut):
927
"""A .bzr version 5 control object.
929
This is a deprecated format and may be removed after sept 2006.
932
def open_repository(self):
933
"""See BzrDir.open_repository."""
934
from bzrlib.repofmt.weaverepo import RepositoryFormat5
935
return RepositoryFormat5().open(self, _found=True)
937
def open_workingtree(self, _unsupported=False):
938
"""See BzrDir.create_workingtree."""
939
from bzrlib.workingtree import WorkingTreeFormat2
940
return WorkingTreeFormat2().open(self, _found=True)
943
class BzrDir6(BzrDirPreSplitOut):
944
"""A .bzr version 6 control object.
946
This is a deprecated format and may be removed after sept 2006.
949
def open_repository(self):
950
"""See BzrDir.open_repository."""
951
from bzrlib.repofmt.weaverepo import RepositoryFormat6
952
return RepositoryFormat6().open(self, _found=True)
954
def open_workingtree(self, _unsupported=False):
955
"""See BzrDir.create_workingtree."""
956
from bzrlib.workingtree import WorkingTreeFormat2
957
return WorkingTreeFormat2().open(self, _found=True)
960
class BzrDirMeta1(BzrDir):
961
"""A .bzr meta version 1 control object.
963
This is the first control object where the
964
individual aspects are really split out: there are separate repository,
965
workingtree and branch subdirectories and any subset of the three can be
966
present within a BzrDir.
969
def can_convert_format(self):
970
"""See BzrDir.can_convert_format()."""
973
def create_branch(self):
974
"""See BzrDir.create_branch."""
975
return self._format.get_branch_format().initialize(self)
977
def create_repository(self, shared=False):
978
"""See BzrDir.create_repository."""
979
return self._format.repository_format.initialize(self, shared)
981
def create_workingtree(self, revision_id=None):
982
"""See BzrDir.create_workingtree."""
983
from bzrlib.workingtree import WorkingTreeFormat
984
return self._format.workingtree_format.initialize(self, revision_id)
986
def destroy_workingtree(self):
987
"""See BzrDir.destroy_workingtree."""
988
wt = self.open_workingtree()
989
repository = wt.branch.repository
990
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
991
wt.revert([], old_tree=empty)
992
self.destroy_workingtree_metadata()
994
def destroy_workingtree_metadata(self):
995
self.transport.delete_tree('checkout')
997
def _get_mkdir_mode(self):
998
"""Figure out the mode to use when creating a bzrdir subdir."""
999
temp_control = lockable_files.LockableFiles(self.transport, '',
1000
lockable_files.TransportLock)
1001
return temp_control._dir_mode
1003
def get_branch_transport(self, branch_format):
1004
"""See BzrDir.get_branch_transport()."""
1005
if branch_format is None:
1006
return self.transport.clone('branch')
1008
branch_format.get_format_string()
1009
except NotImplementedError:
1010
raise errors.IncompatibleFormat(branch_format, self._format)
1012
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1013
except errors.FileExists:
1015
return self.transport.clone('branch')
1017
def get_repository_transport(self, repository_format):
1018
"""See BzrDir.get_repository_transport()."""
1019
if repository_format is None:
1020
return self.transport.clone('repository')
1022
repository_format.get_format_string()
1023
except NotImplementedError:
1024
raise errors.IncompatibleFormat(repository_format, self._format)
1026
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1027
except errors.FileExists:
1029
return self.transport.clone('repository')
1031
def get_workingtree_transport(self, workingtree_format):
1032
"""See BzrDir.get_workingtree_transport()."""
1033
if workingtree_format is None:
1034
return self.transport.clone('checkout')
1036
workingtree_format.get_format_string()
1037
except NotImplementedError:
1038
raise errors.IncompatibleFormat(workingtree_format, self._format)
1040
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1041
except errors.FileExists:
1043
return self.transport.clone('checkout')
1045
def needs_format_conversion(self, format=None):
1046
"""See BzrDir.needs_format_conversion()."""
1048
format = BzrDirFormat.get_default_format()
1049
if not isinstance(self._format, format.__class__):
1050
# it is not a meta dir format, conversion is needed.
1052
# we might want to push this down to the repository?
1054
if not isinstance(self.open_repository()._format,
1055
format.repository_format.__class__):
1056
# the repository needs an upgrade.
1058
except errors.NoRepositoryPresent:
1061
if not isinstance(self.open_branch()._format,
1062
format.get_branch_format().__class__):
1063
# the branch needs an upgrade.
1065
except errors.NotBranchError:
1068
if not isinstance(self.open_workingtree()._format,
1069
format.workingtree_format.__class__):
1070
# the workingtree needs an upgrade.
1072
except (errors.NoWorkingTree, errors.NotLocalUrl):
1076
def open_branch(self, unsupported=False):
1077
"""See BzrDir.open_branch."""
1078
from bzrlib.branch import BranchFormat
1079
format = BranchFormat.find_format(self)
1080
self._check_supported(format, unsupported)
1081
return format.open(self, _found=True)
1083
def open_repository(self, unsupported=False):
1084
"""See BzrDir.open_repository."""
1085
from bzrlib.repository import RepositoryFormat
1086
format = RepositoryFormat.find_format(self)
1087
self._check_supported(format, unsupported)
1088
return format.open(self, _found=True)
1090
def open_workingtree(self, unsupported=False):
1091
"""See BzrDir.open_workingtree."""
1092
from bzrlib.workingtree import WorkingTreeFormat
1093
format = WorkingTreeFormat.find_format(self)
1094
self._check_supported(format, unsupported)
1095
return format.open(self, _found=True)
1098
class BzrDirFormat(object):
1099
"""An encapsulation of the initialization and open routines for a format.
1101
Formats provide three things:
1102
* An initialization routine,
1106
Formats are placed in an dict by their format string for reference
1107
during bzrdir opening. These should be subclasses of BzrDirFormat
1110
Once a format is deprecated, just deprecate the initialize and open
1111
methods on the format class. Do not deprecate the object, as the
1112
object will be created every system load.
1115
_default_format = None
1116
"""The default format used for new .bzr dirs."""
1119
"""The known formats."""
1121
_control_formats = []
1122
"""The registered control formats - .bzr, ....
1124
This is a list of BzrDirFormat objects.
1127
_lock_file_name = 'branch-lock'
1129
# _lock_class must be set in subclasses to the lock type, typ.
1130
# TransportLock or LockDir
1133
def find_format(klass, transport):
1134
"""Return the format present at transport."""
1135
for format in klass._control_formats:
1137
return format.probe_transport(transport)
1138
except errors.NotBranchError:
1139
# this format does not find a control dir here.
1141
raise errors.NotBranchError(path=transport.base)
1144
def probe_transport(klass, transport):
1145
"""Return the .bzrdir style transport present at URL."""
1147
format_string = transport.get(".bzr/branch-format").read()
1148
except errors.NoSuchFile:
1149
raise errors.NotBranchError(path=transport.base)
1152
return klass._formats[format_string]
1154
raise errors.UnknownFormatError(format=format_string)
1157
def get_default_format(klass):
1158
"""Return the current default format."""
1159
return klass._default_format
1161
def get_format_string(self):
1162
"""Return the ASCII format string that identifies this format."""
1163
raise NotImplementedError(self.get_format_string)
1165
def get_format_description(self):
1166
"""Return the short description for this format."""
1167
raise NotImplementedError(self.get_format_description)
1169
def get_converter(self, format=None):
1170
"""Return the converter to use to convert bzrdirs needing converts.
1172
This returns a bzrlib.bzrdir.Converter object.
1174
This should return the best upgrader to step this format towards the
1175
current default format. In the case of plugins we can/should provide
1176
some means for them to extend the range of returnable converters.
1178
:param format: Optional format to override the default format of the
1181
raise NotImplementedError(self.get_converter)
1183
def initialize(self, url):
1184
"""Create a bzr control dir at this url and return an opened copy.
1186
Subclasses should typically override initialize_on_transport
1187
instead of this method.
1189
return self.initialize_on_transport(get_transport(url))
1191
def initialize_on_transport(self, transport):
1192
"""Initialize a new bzrdir in the base directory of a Transport."""
1193
# Since we don't have a .bzr directory, inherit the
1194
# mode from the root directory
1195
temp_control = lockable_files.LockableFiles(transport,
1196
'', lockable_files.TransportLock)
1197
temp_control._transport.mkdir('.bzr',
1198
# FIXME: RBC 20060121 don't peek under
1200
mode=temp_control._dir_mode)
1201
file_mode = temp_control._file_mode
1203
mutter('created control directory in ' + transport.base)
1204
control = transport.clone('.bzr')
1205
utf8_files = [('README',
1206
"This is a Bazaar-NG control directory.\n"
1207
"Do not change any files in this directory.\n"),
1208
('branch-format', self.get_format_string()),
1210
# NB: no need to escape relative paths that are url safe.
1211
control_files = lockable_files.LockableFiles(control,
1212
self._lock_file_name, self._lock_class)
1213
control_files.create_lock()
1214
control_files.lock_write()
1216
for file, content in utf8_files:
1217
control_files.put_utf8(file, content)
1219
control_files.unlock()
1220
return self.open(transport, _found=True)
1222
def is_supported(self):
1223
"""Is this format supported?
1225
Supported formats must be initializable and openable.
1226
Unsupported formats may not support initialization or committing or
1227
some other features depending on the reason for not being supported.
1231
def same_model(self, target_format):
1232
return (self.repository_format.rich_root_data ==
1233
target_format.rich_root_data)
1236
def known_formats(klass):
1237
"""Return all the known formats.
1239
Concrete formats should override _known_formats.
1241
# There is double indirection here to make sure that control
1242
# formats used by more than one dir format will only be probed
1243
# once. This can otherwise be quite expensive for remote connections.
1245
for format in klass._control_formats:
1246
result.update(format._known_formats())
1250
def _known_formats(klass):
1251
"""Return the known format instances for this control format."""
1252
return set(klass._formats.values())
1254
def open(self, transport, _found=False):
1255
"""Return an instance of this format for the dir transport points at.
1257
_found is a private parameter, do not use it.
1260
found_format = BzrDirFormat.find_format(transport)
1261
if not isinstance(found_format, self.__class__):
1262
raise AssertionError("%s was asked to open %s, but it seems to need "
1264
% (self, transport, found_format))
1265
return self._open(transport)
1267
def _open(self, transport):
1268
"""Template method helper for opening BzrDirectories.
1270
This performs the actual open and any additional logic or parameter
1273
raise NotImplementedError(self._open)
1276
def register_format(klass, format):
1277
klass._formats[format.get_format_string()] = format
1280
def register_control_format(klass, format):
1281
"""Register a format that does not use '.bzrdir' for its control dir.
1283
TODO: This should be pulled up into a 'ControlDirFormat' base class
1284
which BzrDirFormat can inherit from, and renamed to register_format
1285
there. It has been done without that for now for simplicity of
1288
klass._control_formats.append(format)
1291
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1292
def set_default_format(klass, format):
1293
klass._set_default_format(format)
1296
def _set_default_format(klass, format):
1297
"""Set default format (for testing behavior of defaults only)"""
1298
klass._default_format = format
1301
return self.get_format_string()[:-1]
1304
def unregister_format(klass, format):
1305
assert klass._formats[format.get_format_string()] is format
1306
del klass._formats[format.get_format_string()]
1309
def unregister_control_format(klass, format):
1310
klass._control_formats.remove(format)
1313
# register BzrDirFormat as a control format
1314
BzrDirFormat.register_control_format(BzrDirFormat)
1317
class BzrDirFormat4(BzrDirFormat):
1318
"""Bzr dir format 4.
1320
This format is a combined format for working tree, branch and repository.
1322
- Format 1 working trees [always]
1323
- Format 4 branches [always]
1324
- Format 4 repositories [always]
1326
This format is deprecated: it indexes texts using a text it which is
1327
removed in format 5; write support for this format has been removed.
1330
_lock_class = lockable_files.TransportLock
1332
def get_format_string(self):
1333
"""See BzrDirFormat.get_format_string()."""
1334
return "Bazaar-NG branch, format 0.0.4\n"
1336
def get_format_description(self):
1337
"""See BzrDirFormat.get_format_description()."""
1338
return "All-in-one format 4"
1340
def get_converter(self, format=None):
1341
"""See BzrDirFormat.get_converter()."""
1342
# there is one and only one upgrade path here.
1343
return ConvertBzrDir4To5()
1345
def initialize_on_transport(self, transport):
1346
"""Format 4 branches cannot be created."""
1347
raise errors.UninitializableFormat(self)
1349
def is_supported(self):
1350
"""Format 4 is not supported.
1352
It is not supported because the model changed from 4 to 5 and the
1353
conversion logic is expensive - so doing it on the fly was not
1358
def _open(self, transport):
1359
"""See BzrDirFormat._open."""
1360
return BzrDir4(transport, self)
1362
def __return_repository_format(self):
1363
"""Circular import protection."""
1364
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1365
return RepositoryFormat4()
1366
repository_format = property(__return_repository_format)
1369
class BzrDirFormat5(BzrDirFormat):
1370
"""Bzr control format 5.
1372
This format is a combined format for working tree, branch and repository.
1374
- Format 2 working trees [always]
1375
- Format 4 branches [always]
1376
- Format 5 repositories [always]
1377
Unhashed stores in the repository.
1380
_lock_class = lockable_files.TransportLock
1382
def get_format_string(self):
1383
"""See BzrDirFormat.get_format_string()."""
1384
return "Bazaar-NG branch, format 5\n"
1386
def get_format_description(self):
1387
"""See BzrDirFormat.get_format_description()."""
1388
return "All-in-one format 5"
1390
def get_converter(self, format=None):
1391
"""See BzrDirFormat.get_converter()."""
1392
# there is one and only one upgrade path here.
1393
return ConvertBzrDir5To6()
1395
def _initialize_for_clone(self, url):
1396
return self.initialize_on_transport(get_transport(url), _cloning=True)
1398
def initialize_on_transport(self, transport, _cloning=False):
1399
"""Format 5 dirs always have working tree, branch and repository.
1401
Except when they are being cloned.
1403
from bzrlib.branch import BzrBranchFormat4
1404
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1405
from bzrlib.workingtree import WorkingTreeFormat2
1406
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1407
RepositoryFormat5().initialize(result, _internal=True)
1409
branch = BzrBranchFormat4().initialize(result)
1411
WorkingTreeFormat2().initialize(result)
1412
except errors.NotLocalUrl:
1413
# Even though we can't access the working tree, we need to
1414
# create its control files.
1415
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1418
def _open(self, transport):
1419
"""See BzrDirFormat._open."""
1420
return BzrDir5(transport, self)
1422
def __return_repository_format(self):
1423
"""Circular import protection."""
1424
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1425
return RepositoryFormat5()
1426
repository_format = property(__return_repository_format)
1429
class BzrDirFormat6(BzrDirFormat):
1430
"""Bzr control format 6.
1432
This format is a combined format for working tree, branch and repository.
1434
- Format 2 working trees [always]
1435
- Format 4 branches [always]
1436
- Format 6 repositories [always]
1439
_lock_class = lockable_files.TransportLock
1441
def get_format_string(self):
1442
"""See BzrDirFormat.get_format_string()."""
1443
return "Bazaar-NG branch, format 6\n"
1445
def get_format_description(self):
1446
"""See BzrDirFormat.get_format_description()."""
1447
return "All-in-one format 6"
1449
def get_converter(self, format=None):
1450
"""See BzrDirFormat.get_converter()."""
1451
# there is one and only one upgrade path here.
1452
return ConvertBzrDir6ToMeta()
1454
def _initialize_for_clone(self, url):
1455
return self.initialize_on_transport(get_transport(url), _cloning=True)
1457
def initialize_on_transport(self, transport, _cloning=False):
1458
"""Format 6 dirs always have working tree, branch and repository.
1460
Except when they are being cloned.
1462
from bzrlib.branch import BzrBranchFormat4
1463
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1464
from bzrlib.workingtree import WorkingTreeFormat2
1465
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1466
RepositoryFormat6().initialize(result, _internal=True)
1468
branch = BzrBranchFormat4().initialize(result)
1470
WorkingTreeFormat2().initialize(result)
1471
except errors.NotLocalUrl:
1472
# Even though we can't access the working tree, we need to
1473
# create its control files.
1474
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1477
def _open(self, transport):
1478
"""See BzrDirFormat._open."""
1479
return BzrDir6(transport, self)
1481
def __return_repository_format(self):
1482
"""Circular import protection."""
1483
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1484
return RepositoryFormat6()
1485
repository_format = property(__return_repository_format)
1488
class BzrDirMetaFormat1(BzrDirFormat):
1489
"""Bzr meta control format 1
1491
This is the first format with split out working tree, branch and repository
1494
- Format 3 working trees [optional]
1495
- Format 5 branches [optional]
1496
- Format 7 repositories [optional]
1499
_lock_class = lockdir.LockDir
1502
self._workingtree_format = None
1503
self._branch_format = None
1505
def __eq__(self, other):
1506
if other.__class__ is not self.__class__:
1508
if other.repository_format != self.repository_format:
1510
if other.workingtree_format != self.workingtree_format:
1514
def __ne__(self, other):
1515
return not self == other
1517
def get_branch_format(self):
1518
if self._branch_format is None:
1519
from bzrlib.branch import BranchFormat
1520
self._branch_format = BranchFormat.get_default_format()
1521
return self._branch_format
1523
def set_branch_format(self, format):
1524
self._branch_format = format
1526
def get_converter(self, format=None):
1527
"""See BzrDirFormat.get_converter()."""
1529
format = BzrDirFormat.get_default_format()
1530
if not isinstance(self, format.__class__):
1531
# converting away from metadir is not implemented
1532
raise NotImplementedError(self.get_converter)
1533
return ConvertMetaToMeta(format)
1535
def get_format_string(self):
1536
"""See BzrDirFormat.get_format_string()."""
1537
return "Bazaar-NG meta directory, format 1\n"
1539
def get_format_description(self):
1540
"""See BzrDirFormat.get_format_description()."""
1541
return "Meta directory format 1"
1543
def _open(self, transport):
1544
"""See BzrDirFormat._open."""
1545
return BzrDirMeta1(transport, self)
1547
def __return_repository_format(self):
1548
"""Circular import protection."""
1549
if getattr(self, '_repository_format', None):
1550
return self._repository_format
1551
from bzrlib.repository import RepositoryFormat
1552
return RepositoryFormat.get_default_format()
1554
def __set_repository_format(self, value):
1555
"""Allow changint the repository format for metadir formats."""
1556
self._repository_format = value
1558
repository_format = property(__return_repository_format, __set_repository_format)
1560
def __get_workingtree_format(self):
1561
if self._workingtree_format is None:
1562
from bzrlib.workingtree import WorkingTreeFormat
1563
self._workingtree_format = WorkingTreeFormat.get_default_format()
1564
return self._workingtree_format
1566
def __set_workingtree_format(self, wt_format):
1567
self._workingtree_format = wt_format
1569
workingtree_format = property(__get_workingtree_format,
1570
__set_workingtree_format)
1573
BzrDirFormat.register_format(BzrDirFormat4())
1574
BzrDirFormat.register_format(BzrDirFormat5())
1575
BzrDirFormat.register_format(BzrDirFormat6())
1576
__default_format = BzrDirMetaFormat1()
1577
BzrDirFormat.register_format(__default_format)
1578
BzrDirFormat._default_format = __default_format
1581
class BzrDirTestProviderAdapter(object):
1582
"""A tool to generate a suite testing multiple bzrdir formats at once.
1584
This is done by copying the test once for each transport and injecting
1585
the transport_server, transport_readonly_server, and bzrdir_format
1586
classes into each copy. Each copy is also given a new id() to make it
1590
def __init__(self, transport_server, transport_readonly_server, formats):
1591
self._transport_server = transport_server
1592
self._transport_readonly_server = transport_readonly_server
1593
self._formats = formats
1595
def adapt(self, test):
1596
result = unittest.TestSuite()
1597
for format in self._formats:
1598
new_test = deepcopy(test)
1599
new_test.transport_server = self._transport_server
1600
new_test.transport_readonly_server = self._transport_readonly_server
1601
new_test.bzrdir_format = format
1602
def make_new_test_id():
1603
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1604
return lambda: new_id
1605
new_test.id = make_new_test_id()
1606
result.addTest(new_test)
1610
class Converter(object):
1611
"""Converts a disk format object from one format to another."""
1613
def convert(self, to_convert, pb):
1614
"""Perform the conversion of to_convert, giving feedback via pb.
1616
:param to_convert: The disk object to convert.
1617
:param pb: a progress bar to use for progress information.
1620
def step(self, message):
1621
"""Update the pb by a step."""
1623
self.pb.update(message, self.count, self.total)
1626
class ConvertBzrDir4To5(Converter):
1627
"""Converts format 4 bzr dirs to format 5."""
1630
super(ConvertBzrDir4To5, self).__init__()
1631
self.converted_revs = set()
1632
self.absent_revisions = set()
1636
def convert(self, to_convert, pb):
1637
"""See Converter.convert()."""
1638
self.bzrdir = to_convert
1640
self.pb.note('starting upgrade from format 4 to 5')
1641
if isinstance(self.bzrdir.transport, LocalTransport):
1642
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1643
self._convert_to_weaves()
1644
return BzrDir.open(self.bzrdir.root_transport.base)
1646
def _convert_to_weaves(self):
1647
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1650
stat = self.bzrdir.transport.stat('weaves')
1651
if not S_ISDIR(stat.st_mode):
1652
self.bzrdir.transport.delete('weaves')
1653
self.bzrdir.transport.mkdir('weaves')
1654
except errors.NoSuchFile:
1655
self.bzrdir.transport.mkdir('weaves')
1656
# deliberately not a WeaveFile as we want to build it up slowly.
1657
self.inv_weave = Weave('inventory')
1658
# holds in-memory weaves for all files
1659
self.text_weaves = {}
1660
self.bzrdir.transport.delete('branch-format')
1661
self.branch = self.bzrdir.open_branch()
1662
self._convert_working_inv()
1663
rev_history = self.branch.revision_history()
1664
# to_read is a stack holding the revisions we still need to process;
1665
# appending to it adds new highest-priority revisions
1666
self.known_revisions = set(rev_history)
1667
self.to_read = rev_history[-1:]
1669
rev_id = self.to_read.pop()
1670
if (rev_id not in self.revisions
1671
and rev_id not in self.absent_revisions):
1672
self._load_one_rev(rev_id)
1674
to_import = self._make_order()
1675
for i, rev_id in enumerate(to_import):
1676
self.pb.update('converting revision', i, len(to_import))
1677
self._convert_one_rev(rev_id)
1679
self._write_all_weaves()
1680
self._write_all_revs()
1681
self.pb.note('upgraded to weaves:')
1682
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1683
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1684
self.pb.note(' %6d texts', self.text_count)
1685
self._cleanup_spare_files_after_format4()
1686
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1688
def _cleanup_spare_files_after_format4(self):
1689
# FIXME working tree upgrade foo.
1690
for n in 'merged-patches', 'pending-merged-patches':
1692
## assert os.path.getsize(p) == 0
1693
self.bzrdir.transport.delete(n)
1694
except errors.NoSuchFile:
1696
self.bzrdir.transport.delete_tree('inventory-store')
1697
self.bzrdir.transport.delete_tree('text-store')
1699
def _convert_working_inv(self):
1700
inv = xml4.serializer_v4.read_inventory(
1701
self.branch.control_files.get('inventory'))
1702
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1703
# FIXME inventory is a working tree change.
1704
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1706
def _write_all_weaves(self):
1707
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1708
weave_transport = self.bzrdir.transport.clone('weaves')
1709
weaves = WeaveStore(weave_transport, prefixed=False)
1710
transaction = WriteTransaction()
1714
for file_id, file_weave in self.text_weaves.items():
1715
self.pb.update('writing weave', i, len(self.text_weaves))
1716
weaves._put_weave(file_id, file_weave, transaction)
1718
self.pb.update('inventory', 0, 1)
1719
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1720
self.pb.update('inventory', 1, 1)
1724
def _write_all_revs(self):
1725
"""Write all revisions out in new form."""
1726
self.bzrdir.transport.delete_tree('revision-store')
1727
self.bzrdir.transport.mkdir('revision-store')
1728
revision_transport = self.bzrdir.transport.clone('revision-store')
1730
_revision_store = TextRevisionStore(TextStore(revision_transport,
1734
transaction = WriteTransaction()
1735
for i, rev_id in enumerate(self.converted_revs):
1736
self.pb.update('write revision', i, len(self.converted_revs))
1737
_revision_store.add_revision(self.revisions[rev_id], transaction)
1741
def _load_one_rev(self, rev_id):
1742
"""Load a revision object into memory.
1744
Any parents not either loaded or abandoned get queued to be
1746
self.pb.update('loading revision',
1747
len(self.revisions),
1748
len(self.known_revisions))
1749
if not self.branch.repository.has_revision(rev_id):
1751
self.pb.note('revision {%s} not present in branch; '
1752
'will be converted as a ghost',
1754
self.absent_revisions.add(rev_id)
1756
rev = self.branch.repository._revision_store.get_revision(rev_id,
1757
self.branch.repository.get_transaction())
1758
for parent_id in rev.parent_ids:
1759
self.known_revisions.add(parent_id)
1760
self.to_read.append(parent_id)
1761
self.revisions[rev_id] = rev
1763
def _load_old_inventory(self, rev_id):
1764
assert rev_id not in self.converted_revs
1765
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1766
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1767
inv.revision_id = rev_id
1768
rev = self.revisions[rev_id]
1769
if rev.inventory_sha1:
1770
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1771
'inventory sha mismatch for {%s}' % rev_id
1774
def _load_updated_inventory(self, rev_id):
1775
assert rev_id in self.converted_revs
1776
inv_xml = self.inv_weave.get_text(rev_id)
1777
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1780
def _convert_one_rev(self, rev_id):
1781
"""Convert revision and all referenced objects to new format."""
1782
rev = self.revisions[rev_id]
1783
inv = self._load_old_inventory(rev_id)
1784
present_parents = [p for p in rev.parent_ids
1785
if p not in self.absent_revisions]
1786
self._convert_revision_contents(rev, inv, present_parents)
1787
self._store_new_weave(rev, inv, present_parents)
1788
self.converted_revs.add(rev_id)
1790
def _store_new_weave(self, rev, inv, present_parents):
1791
# the XML is now updated with text versions
1793
entries = inv.iter_entries()
1795
for path, ie in entries:
1796
assert getattr(ie, 'revision', None) is not None, \
1797
'no revision on {%s} in {%s}' % \
1798
(file_id, rev.revision_id)
1799
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1800
new_inv_sha1 = sha_string(new_inv_xml)
1801
self.inv_weave.add_lines(rev.revision_id,
1803
new_inv_xml.splitlines(True))
1804
rev.inventory_sha1 = new_inv_sha1
1806
def _convert_revision_contents(self, rev, inv, present_parents):
1807
"""Convert all the files within a revision.
1809
Also upgrade the inventory to refer to the text revision ids."""
1810
rev_id = rev.revision_id
1811
mutter('converting texts of revision {%s}',
1813
parent_invs = map(self._load_updated_inventory, present_parents)
1814
entries = inv.iter_entries()
1816
for path, ie in entries:
1817
self._convert_file_version(rev, ie, parent_invs)
1819
def _convert_file_version(self, rev, ie, parent_invs):
1820
"""Convert one version of one file.
1822
The file needs to be added into the weave if it is a merge
1823
of >=2 parents or if it's changed from its parent.
1825
file_id = ie.file_id
1826
rev_id = rev.revision_id
1827
w = self.text_weaves.get(file_id)
1830
self.text_weaves[file_id] = w
1831
text_changed = False
1832
previous_entries = ie.find_previous_heads(parent_invs,
1836
for old_revision in previous_entries:
1837
# if this fails, its a ghost ?
1838
assert old_revision in self.converted_revs, \
1839
"Revision {%s} not in converted_revs" % old_revision
1840
self.snapshot_ie(previous_entries, ie, w, rev_id)
1842
assert getattr(ie, 'revision', None) is not None
1844
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1845
# TODO: convert this logic, which is ~= snapshot to
1846
# a call to:. This needs the path figured out. rather than a work_tree
1847
# a v4 revision_tree can be given, or something that looks enough like
1848
# one to give the file content to the entry if it needs it.
1849
# and we need something that looks like a weave store for snapshot to
1851
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1852
if len(previous_revisions) == 1:
1853
previous_ie = previous_revisions.values()[0]
1854
if ie._unchanged(previous_ie):
1855
ie.revision = previous_ie.revision
1858
text = self.branch.repository.text_store.get(ie.text_id)
1859
file_lines = text.readlines()
1860
assert sha_strings(file_lines) == ie.text_sha1
1861
assert sum(map(len, file_lines)) == ie.text_size
1862
w.add_lines(rev_id, previous_revisions, file_lines)
1863
self.text_count += 1
1865
w.add_lines(rev_id, previous_revisions, [])
1866
ie.revision = rev_id
1868
def _make_order(self):
1869
"""Return a suitable order for importing revisions.
1871
The order must be such that an revision is imported after all
1872
its (present) parents.
1874
todo = set(self.revisions.keys())
1875
done = self.absent_revisions.copy()
1878
# scan through looking for a revision whose parents
1880
for rev_id in sorted(list(todo)):
1881
rev = self.revisions[rev_id]
1882
parent_ids = set(rev.parent_ids)
1883
if parent_ids.issubset(done):
1884
# can take this one now
1885
order.append(rev_id)
1891
class ConvertBzrDir5To6(Converter):
1892
"""Converts format 5 bzr dirs to format 6."""
1894
def convert(self, to_convert, pb):
1895
"""See Converter.convert()."""
1896
self.bzrdir = to_convert
1898
self.pb.note('starting upgrade from format 5 to 6')
1899
self._convert_to_prefixed()
1900
return BzrDir.open(self.bzrdir.root_transport.base)
1902
def _convert_to_prefixed(self):
1903
from bzrlib.store import TransportStore
1904
self.bzrdir.transport.delete('branch-format')
1905
for store_name in ["weaves", "revision-store"]:
1906
self.pb.note("adding prefixes to %s" % store_name)
1907
store_transport = self.bzrdir.transport.clone(store_name)
1908
store = TransportStore(store_transport, prefixed=True)
1909
for urlfilename in store_transport.list_dir('.'):
1910
filename = urlutils.unescape(urlfilename)
1911
if (filename.endswith(".weave") or
1912
filename.endswith(".gz") or
1913
filename.endswith(".sig")):
1914
file_id = os.path.splitext(filename)[0]
1917
prefix_dir = store.hash_prefix(file_id)
1918
# FIXME keep track of the dirs made RBC 20060121
1920
store_transport.move(filename, prefix_dir + '/' + filename)
1921
except errors.NoSuchFile: # catches missing dirs strangely enough
1922
store_transport.mkdir(prefix_dir)
1923
store_transport.move(filename, prefix_dir + '/' + filename)
1924
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1927
class ConvertBzrDir6ToMeta(Converter):
1928
"""Converts format 6 bzr dirs to metadirs."""
1930
def convert(self, to_convert, pb):
1931
"""See Converter.convert()."""
1932
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1933
from bzrlib.branch import BzrBranchFormat5
1934
self.bzrdir = to_convert
1937
self.total = 20 # the steps we know about
1938
self.garbage_inventories = []
1940
self.pb.note('starting upgrade from format 6 to metadir')
1941
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1942
# its faster to move specific files around than to open and use the apis...
1943
# first off, nuke ancestry.weave, it was never used.
1945
self.step('Removing ancestry.weave')
1946
self.bzrdir.transport.delete('ancestry.weave')
1947
except errors.NoSuchFile:
1949
# find out whats there
1950
self.step('Finding branch files')
1951
last_revision = self.bzrdir.open_branch().last_revision()
1952
bzrcontents = self.bzrdir.transport.list_dir('.')
1953
for name in bzrcontents:
1954
if name.startswith('basis-inventory.'):
1955
self.garbage_inventories.append(name)
1956
# create new directories for repository, working tree and branch
1957
self.dir_mode = self.bzrdir._control_files._dir_mode
1958
self.file_mode = self.bzrdir._control_files._file_mode
1959
repository_names = [('inventory.weave', True),
1960
('revision-store', True),
1962
self.step('Upgrading repository ')
1963
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1964
self.make_lock('repository')
1965
# we hard code the formats here because we are converting into
1966
# the meta format. The meta format upgrader can take this to a
1967
# future format within each component.
1968
self.put_format('repository', RepositoryFormat7())
1969
for entry in repository_names:
1970
self.move_entry('repository', entry)
1972
self.step('Upgrading branch ')
1973
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1974
self.make_lock('branch')
1975
self.put_format('branch', BzrBranchFormat5())
1976
branch_files = [('revision-history', True),
1977
('branch-name', True),
1979
for entry in branch_files:
1980
self.move_entry('branch', entry)
1982
checkout_files = [('pending-merges', True),
1983
('inventory', True),
1984
('stat-cache', False)]
1985
# If a mandatory checkout file is not present, the branch does not have
1986
# a functional checkout. Do not create a checkout in the converted
1988
for name, mandatory in checkout_files:
1989
if mandatory and name not in bzrcontents:
1990
has_checkout = False
1994
if not has_checkout:
1995
self.pb.note('No working tree.')
1996
# If some checkout files are there, we may as well get rid of them.
1997
for name, mandatory in checkout_files:
1998
if name in bzrcontents:
1999
self.bzrdir.transport.delete(name)
2001
from bzrlib.workingtree import WorkingTreeFormat3
2002
self.step('Upgrading working tree')
2003
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2004
self.make_lock('checkout')
2006
'checkout', WorkingTreeFormat3())
2007
self.bzrdir.transport.delete_multi(
2008
self.garbage_inventories, self.pb)
2009
for entry in checkout_files:
2010
self.move_entry('checkout', entry)
2011
if last_revision is not None:
2012
self.bzrdir._control_files.put_utf8(
2013
'checkout/last-revision', last_revision)
2014
self.bzrdir._control_files.put_utf8(
2015
'branch-format', BzrDirMetaFormat1().get_format_string())
2016
return BzrDir.open(self.bzrdir.root_transport.base)
2018
def make_lock(self, name):
2019
"""Make a lock for the new control dir name."""
2020
self.step('Make %s lock' % name)
2021
ld = lockdir.LockDir(self.bzrdir.transport,
2023
file_modebits=self.file_mode,
2024
dir_modebits=self.dir_mode)
2027
def move_entry(self, new_dir, entry):
2028
"""Move then entry name into new_dir."""
2030
mandatory = entry[1]
2031
self.step('Moving %s' % name)
2033
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2034
except errors.NoSuchFile:
2038
def put_format(self, dirname, format):
2039
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2042
class ConvertMetaToMeta(Converter):
2043
"""Converts the components of metadirs."""
2045
def __init__(self, target_format):
2046
"""Create a metadir to metadir converter.
2048
:param target_format: The final metadir format that is desired.
2050
self.target_format = target_format
2052
def convert(self, to_convert, pb):
2053
"""See Converter.convert()."""
2054
self.bzrdir = to_convert
2058
self.step('checking repository format')
2060
repo = self.bzrdir.open_repository()
2061
except errors.NoRepositoryPresent:
2064
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2065
from bzrlib.repository import CopyConverter
2066
self.pb.note('starting repository conversion')
2067
converter = CopyConverter(self.target_format.repository_format)
2068
converter.convert(repo, pb)
2070
branch = self.bzrdir.open_branch()
2071
except errors.NotBranchError:
2074
# TODO: conversions of Branch and Tree should be done by
2075
# InterXFormat lookups
2076
# Avoid circular imports
2077
from bzrlib import branch as _mod_branch
2078
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2079
self.target_format.get_branch_format().__class__ is
2080
_mod_branch.BzrBranchFormat6):
2081
branch_converter = _mod_branch.Converter5to6()
2082
branch_converter.convert(branch)
2084
tree = self.bzrdir.open_workingtree()
2085
except (errors.NoWorkingTree, errors.NotLocalUrl):
2088
# TODO: conversions of Branch and Tree should be done by
2089
# InterXFormat lookups
2090
if (isinstance(tree, workingtree.WorkingTree3) and
2091
isinstance(self.target_format.workingtree_format,
2092
workingtree_4.WorkingTreeFormat4)):
2093
workingtree_4.Converter3to4().convert(tree)
2097
class BzrDirFormatInfo(object):
2099
def __init__(self, native, deprecated):
2100
self.deprecated = deprecated
2101
self.native = native
2104
class BzrDirFormatRegistry(registry.Registry):
2105
"""Registry of user-selectable BzrDir subformats.
2107
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2108
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2111
def register_metadir(self, key,
2112
repository_format, help, native=True, deprecated=False,
2115
"""Register a metadir subformat.
2117
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2118
by the Repository format.
2120
:param repository_format: The fully-qualified repository format class
2122
:param branch_format: Fully-qualified branch format class name as
2124
:param tree_format: Fully-qualified tree format class name as
2127
# This should be expanded to support setting WorkingTree and Branch
2128
# formats, once BzrDirMetaFormat1 supports that.
2129
def _load(full_name):
2130
mod_name, factory_name = full_name.rsplit('.', 1)
2132
mod = __import__(mod_name, globals(), locals(),
2134
except ImportError, e:
2135
raise ImportError('failed to load %s: %s' % (full_name, e))
2137
factory = getattr(mod, factory_name)
2138
except AttributeError:
2139
raise AttributeError('no factory %s in module %r'
2144
bd = BzrDirMetaFormat1()
2145
if branch_format is not None:
2146
bd.set_branch_format(_load(branch_format))
2147
if tree_format is not None:
2148
bd.workingtree_format = _load(tree_format)
2149
if repository_format is not None:
2150
bd.repository_format = _load(repository_format)
2152
self.register(key, helper, help, native, deprecated)
2154
def register(self, key, factory, help, native=True, deprecated=False):
2155
"""Register a BzrDirFormat factory.
2157
The factory must be a callable that takes one parameter: the key.
2158
It must produce an instance of the BzrDirFormat when called.
2160
This function mainly exists to prevent the info object from being
2163
registry.Registry.register(self, key, factory, help,
2164
BzrDirFormatInfo(native, deprecated))
2166
def register_lazy(self, key, module_name, member_name, help, native=True,
2168
registry.Registry.register_lazy(self, key, module_name, member_name,
2169
help, BzrDirFormatInfo(native, deprecated))
2171
def set_default(self, key):
2172
"""Set the 'default' key to be a clone of the supplied key.
2174
This method must be called once and only once.
2176
registry.Registry.register(self, 'default', self.get(key),
2177
self.get_help(key), info=self.get_info(key))
2179
def set_default_repository(self, key):
2180
"""Set the FormatRegistry default and Repository default.
2182
This is a transitional method while Repository.set_default_format
2185
if 'default' in self:
2186
self.remove('default')
2187
self.set_default(key)
2188
format = self.get('default')()
2189
assert isinstance(format, BzrDirMetaFormat1)
2191
def make_bzrdir(self, key):
2192
return self.get(key)()
2194
def help_topic(self, topic):
2195
output = textwrap.dedent("""\
2196
Bazaar directory formats
2197
------------------------
2199
These formats can be used for creating branches, working trees, and
2203
default_help = self.get_help('default')
2205
for key in self.keys():
2206
if key == 'default':
2208
help = self.get_help(key)
2209
if help == default_help:
2210
default_realkey = key
2212
help_pairs.append((key, help))
2214
def wrapped(key, help, info):
2216
help = '(native) ' + help
2217
return ' %s:\n%s\n\n' % (key,
2218
textwrap.fill(help, initial_indent=' ',
2219
subsequent_indent=' '))
2220
output += wrapped('%s/default' % default_realkey, default_help,
2221
self.get_info('default'))
2222
deprecated_pairs = []
2223
for key, help in help_pairs:
2224
info = self.get_info(key)
2226
deprecated_pairs.append((key, help))
2228
output += wrapped(key, help, info)
2229
if len(deprecated_pairs) > 0:
2230
output += "Deprecated formats\n------------------\n\n"
2231
for key, help in deprecated_pairs:
2232
info = self.get_info(key)
2233
output += wrapped(key, help, info)
2238
format_registry = BzrDirFormatRegistry()
2239
format_registry.register('weave', BzrDirFormat6,
2240
'Pre-0.8 format. Slower than knit and does not'
2241
' support checkouts or shared repositories.',
2243
format_registry.register_metadir('knit',
2244
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2245
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2246
branch_format='bzrlib.branch.BzrBranchFormat5',
2247
tree_format='bzrlib.workingtree_4.WorkingTreeFormat3')
2248
format_registry.set_default('knit')
2249
format_registry.register_metadir('metaweave',
2250
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2251
'Transitional format in 0.8. Slower than knit.',
2252
branch_format='bzrlib.branch.BzrBranchFormat5',
2253
tree_format='bzrlib.workingtree_4.WorkingTreeFormat3',
2255
format_registry.register_metadir('experimental-knit2',
2256
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
2257
'Experimental successor to knit. Use at your own risk.',
2258
branch_format='bzrlib.branch.BzrBranchFormat5',
2259
tree_format='bzrlib.workingtree.WorkingTreeFormat3'
2261
format_registry.register_metadir('experimental-branch6',
2262
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2263
'Experimental successor to knit. Use at your own risk.',
2264
branch_format='bzrlib.branch.BzrBranchFormat6',
2265
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2267
format_registry.register_metadir('experimental-reference-dirstate',
2268
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2269
help='Experimental: dirstate working tree, Branch6, and reference-tree '
2270
'support. Proposed default for bzr 0.15',
2271
branch_format='bzrlib.branch.BzrBranchFormat6',
2272
tree_format='bzrlib.workingtree_4.WorkingTreeFormat4',
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
tree_format='bzrlib.workingtree_4.WorkingTreeFormat4',
2281
format_registry.register_metadir('dirstate-with-subtree',
2282
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2283
help='New in 0.15: Fast local operations and improved scaling for '
2284
'network operations. Additionally adds support for versioning nested '
2285
'bzr branches. Incompatible with bzr < 0.15.',
2286
branch_format='bzrlib.branch.BzrBranchFormat6',
2287
tree_format='bzrlib.workingtree_4.WorkingTreeFormat4',