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,
53
from bzrlib.osutils import (
58
from bzrlib.store.revision.text import TextRevisionStore
59
from bzrlib.store.text import TextStore
60
from bzrlib.store.versioned import WeaveStore
61
from bzrlib.transactions import WriteTransaction
62
from bzrlib.transport import get_transport
63
from bzrlib.weave import Weave
66
from bzrlib.trace import mutter
67
from bzrlib.transport.local import LocalTransport
71
"""A .bzr control diretory.
73
BzrDir instances let you create or open any of the things that can be
74
found within .bzr - checkouts, branches and repositories.
77
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
79
a transport connected to the directory this bzr was opened from.
83
"""Invoke break_lock on the first object in the bzrdir.
85
If there is a tree, the tree is opened and break_lock() called.
86
Otherwise, branch is tried, and finally repository.
89
thing_to_unlock = self.open_workingtree()
90
except (errors.NotLocalUrl, errors.NoWorkingTree):
92
thing_to_unlock = self.open_branch()
93
except errors.NotBranchError:
95
thing_to_unlock = self.open_repository()
96
except errors.NoRepositoryPresent:
98
thing_to_unlock.break_lock()
100
def can_convert_format(self):
101
"""Return true if this bzrdir is one whose format we can convert from."""
104
def check_conversion_target(self, target_format):
105
target_repo_format = target_format.repository_format
106
source_repo_format = self._format.repository_format
107
source_repo_format.check_conversion_target(target_repo_format)
110
def _check_supported(format, allow_unsupported):
111
"""Check whether format is a supported format.
113
If allow_unsupported is True, this is a no-op.
115
if not allow_unsupported and not format.is_supported():
116
# see open_downlevel to open legacy branches.
117
raise errors.UnsupportedFormatError(format=format)
119
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
120
"""Clone this bzrdir and its contents to url verbatim.
122
If urls last component does not exist, it will be created.
124
if revision_id is not None, then the clone operation may tune
125
itself to download less data.
126
:param force_new_repo: Do not use a shared repository for the target
127
even if one is available.
130
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
131
result = self._format.initialize(url)
133
local_repo = self.find_repository()
134
except errors.NoRepositoryPresent:
137
# may need to copy content in
139
result_repo = local_repo.clone(
141
revision_id=revision_id,
143
result_repo.set_make_working_trees(local_repo.make_working_trees())
146
result_repo = result.find_repository()
147
# fetch content this dir needs.
149
# XXX FIXME RBC 20060214 need tests for this when the basis
151
result_repo.fetch(basis_repo, revision_id=revision_id)
152
result_repo.fetch(local_repo, revision_id=revision_id)
153
except errors.NoRepositoryPresent:
154
# needed to make one anyway.
155
result_repo = local_repo.clone(
157
revision_id=revision_id,
159
result_repo.set_make_working_trees(local_repo.make_working_trees())
160
# 1 if there is a branch present
161
# make sure its content is available in the target repository
164
self.open_branch().clone(result, revision_id=revision_id)
165
except errors.NotBranchError:
168
self.open_workingtree().clone(result, basis=basis_tree)
169
except (errors.NoWorkingTree, errors.NotLocalUrl):
173
def _get_basis_components(self, basis):
174
"""Retrieve the basis components that are available at basis."""
176
return None, None, None
178
basis_tree = basis.open_workingtree()
179
basis_branch = basis_tree.branch
180
basis_repo = basis_branch.repository
181
except (errors.NoWorkingTree, errors.NotLocalUrl):
184
basis_branch = basis.open_branch()
185
basis_repo = basis_branch.repository
186
except errors.NotBranchError:
189
basis_repo = basis.open_repository()
190
except errors.NoRepositoryPresent:
192
return basis_repo, basis_branch, basis_tree
194
# TODO: This should be given a Transport, and should chdir up; otherwise
195
# this will open a new connection.
196
def _make_tail(self, url):
197
head, tail = urlutils.split(url)
198
if tail and tail != '.':
199
t = get_transport(head)
202
except errors.FileExists:
205
# TODO: Should take a Transport
207
def create(cls, base, format=None):
208
"""Create a new BzrDir at the url 'base'.
210
This will call the current default formats initialize with base
211
as the only parameter.
213
:param format: If supplied, the format of branch to create. If not
214
supplied, the default is used.
216
if cls is not BzrDir:
217
raise AssertionError("BzrDir.create always creates the default"
218
" format, not one of %r" % cls)
219
head, tail = urlutils.split(base)
220
if tail and tail != '.':
221
t = get_transport(head)
224
except errors.FileExists:
227
format = BzrDirFormat.get_default_format()
228
return format.initialize(safe_unicode(base))
230
def create_branch(self):
231
"""Create a branch in this BzrDir.
233
The bzrdirs format will control what branch format is created.
234
For more control see BranchFormatXX.create(a_bzrdir).
236
raise NotImplementedError(self.create_branch)
239
def create_branch_and_repo(base, force_new_repo=False, format=None):
240
"""Create a new BzrDir, Branch and Repository at the url 'base'.
242
This will use the current default BzrDirFormat, and use whatever
243
repository format that that uses via bzrdir.create_branch and
244
create_repository. If a shared repository is available that is used
247
The created Branch object is returned.
249
:param base: The URL to create the branch at.
250
:param force_new_repo: If True a new repository is always created.
252
bzrdir = BzrDir.create(base, format)
253
bzrdir._find_or_create_repository(force_new_repo)
254
return bzrdir.create_branch()
256
def _find_or_create_repository(self, force_new_repo):
257
"""Create a new repository if needed, returning the repository."""
259
return self.create_repository()
261
return self.find_repository()
262
except errors.NoRepositoryPresent:
263
return self.create_repository()
266
def create_branch_convenience(base, force_new_repo=False,
267
force_new_tree=None, format=None):
268
"""Create a new BzrDir, Branch and Repository at the url 'base'.
270
This is a convenience function - it will use an existing repository
271
if possible, can be told explicitly whether to create a working tree or
274
This will use the current default BzrDirFormat, and use whatever
275
repository format that that uses via bzrdir.create_branch and
276
create_repository. If a shared repository is available that is used
277
preferentially. Whatever repository is used, its tree creation policy
280
The created Branch object is returned.
281
If a working tree cannot be made due to base not being a file:// url,
282
no error is raised unless force_new_tree is True, in which case no
283
data is created on disk and NotLocalUrl is raised.
285
:param base: The URL to create the branch at.
286
:param force_new_repo: If True a new repository is always created.
287
:param force_new_tree: If True or False force creation of a tree or
288
prevent such creation respectively.
289
:param format: Override for the for the bzrdir format to create
292
# check for non local urls
293
t = get_transport(safe_unicode(base))
294
if not isinstance(t, LocalTransport):
295
raise errors.NotLocalUrl(base)
296
bzrdir = BzrDir.create(base, format)
297
repo = bzrdir._find_or_create_repository(force_new_repo)
298
result = bzrdir.create_branch()
299
if force_new_tree or (repo.make_working_trees() and
300
force_new_tree is None):
302
bzrdir.create_workingtree()
303
except errors.NotLocalUrl:
308
def create_repository(base, shared=False, format=None):
309
"""Create a new BzrDir and Repository at the url 'base'.
311
If no format is supplied, this will default to the current default
312
BzrDirFormat by default, and use whatever repository format that that
313
uses for bzrdirformat.create_repository.
315
:param shared: Create a shared repository rather than a standalone
317
The Repository object is returned.
319
This must be overridden as an instance method in child classes, where
320
it should take no parameters and construct whatever repository format
321
that child class desires.
323
bzrdir = BzrDir.create(base, format)
324
return bzrdir.create_repository(shared)
327
def create_standalone_workingtree(base, format=None):
328
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
330
'base' must be a local path or a file:// url.
332
This will use the current default BzrDirFormat, and use whatever
333
repository format that that uses for bzrdirformat.create_workingtree,
334
create_branch and create_repository.
336
:return: The WorkingTree object.
338
t = get_transport(safe_unicode(base))
339
if not isinstance(t, LocalTransport):
340
raise errors.NotLocalUrl(base)
341
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
343
format=format).bzrdir
344
return bzrdir.create_workingtree()
346
def create_workingtree(self, revision_id=None):
347
"""Create a working tree at this BzrDir.
349
revision_id: create it as of this revision id.
351
raise NotImplementedError(self.create_workingtree)
353
def destroy_workingtree(self):
354
"""Destroy the working tree at this BzrDir.
356
Formats that do not support this may raise UnsupportedOperation.
358
raise NotImplementedError(self.destroy_workingtree)
360
def destroy_workingtree_metadata(self):
361
"""Destroy the control files for the working tree at this BzrDir.
363
The contents of working tree files are not affected.
364
Formats that do not support this may raise UnsupportedOperation.
366
raise NotImplementedError(self.destroy_workingtree_metadata)
368
def find_repository(self):
369
"""Find the repository that should be used for a_bzrdir.
371
This does not require a branch as we use it to find the repo for
372
new branches as well as to hook existing branches up to their
376
return self.open_repository()
377
except errors.NoRepositoryPresent:
379
next_transport = self.root_transport.clone('..')
381
# find the next containing bzrdir
383
found_bzrdir = BzrDir.open_containing_from_transport(
385
except errors.NotBranchError:
387
raise errors.NoRepositoryPresent(self)
388
# does it have a repository ?
390
repository = found_bzrdir.open_repository()
391
except errors.NoRepositoryPresent:
392
next_transport = found_bzrdir.root_transport.clone('..')
393
if (found_bzrdir.root_transport.base == next_transport.base):
394
# top of the file system
398
if ((found_bzrdir.root_transport.base ==
399
self.root_transport.base) or repository.is_shared()):
402
raise errors.NoRepositoryPresent(self)
403
raise errors.NoRepositoryPresent(self)
405
def get_branch_transport(self, branch_format):
406
"""Get the transport for use by branch format in this BzrDir.
408
Note that bzr dirs that do not support format strings will raise
409
IncompatibleFormat if the branch format they are given has
410
a format string, and vice versa.
412
If branch_format is None, the transport is returned with no
413
checking. if it is not None, then the returned transport is
414
guaranteed to point to an existing directory ready for use.
416
raise NotImplementedError(self.get_branch_transport)
418
def get_repository_transport(self, repository_format):
419
"""Get the transport for use by repository format in this BzrDir.
421
Note that bzr dirs that do not support format strings will raise
422
IncompatibleFormat if the repository format they are given has
423
a format string, and vice versa.
425
If repository_format is None, the transport is returned with no
426
checking. if it is not None, then the returned transport is
427
guaranteed to point to an existing directory ready for use.
429
raise NotImplementedError(self.get_repository_transport)
431
def get_workingtree_transport(self, tree_format):
432
"""Get the transport for use by workingtree format in this BzrDir.
434
Note that bzr dirs that do not support format strings will raise
435
IncompatibleFormat if the workingtree format they are given has a
436
format string, and vice versa.
438
If workingtree_format is None, the transport is returned with no
439
checking. if it is not None, then the returned transport is
440
guaranteed to point to an existing directory ready for use.
442
raise NotImplementedError(self.get_workingtree_transport)
444
def __init__(self, _transport, _format):
445
"""Initialize a Bzr control dir object.
447
Only really common logic should reside here, concrete classes should be
448
made with varying behaviours.
450
:param _format: the format that is creating this BzrDir instance.
451
:param _transport: the transport this dir is based at.
453
self._format = _format
454
self.transport = _transport.clone('.bzr')
455
self.root_transport = _transport
457
def is_control_filename(self, filename):
458
"""True if filename is the name of a path which is reserved for bzrdir's.
460
:param filename: A filename within the root transport of this bzrdir.
462
This is true IF and ONLY IF the filename is part of the namespace reserved
463
for bzr control dirs. Currently this is the '.bzr' directory in the root
464
of the root_transport. it is expected that plugins will need to extend
465
this in the future - for instance to make bzr talk with svn working
468
# this might be better on the BzrDirFormat class because it refers to
469
# all the possible bzrdir disk formats.
470
# This method is tested via the workingtree is_control_filename tests-
471
# it was extracted from WorkingTree.is_control_filename. If the methods
472
# contract is extended beyond the current trivial implementation please
473
# add new tests for it to the appropriate place.
474
return filename == '.bzr' or filename.startswith('.bzr/')
476
def needs_format_conversion(self, format=None):
477
"""Return true if this bzrdir needs convert_format run on it.
479
For instance, if the repository format is out of date but the
480
branch and working tree are not, this should return True.
482
:param format: Optional parameter indicating a specific desired
483
format we plan to arrive at.
485
raise NotImplementedError(self.needs_format_conversion)
488
def open_unsupported(base):
489
"""Open a branch which is not supported."""
490
return BzrDir.open(base, _unsupported=True)
493
def open(base, _unsupported=False):
494
"""Open an existing bzrdir, rooted at 'base' (url)
496
_unsupported is a private parameter to the BzrDir class.
498
t = get_transport(base)
499
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
502
def open_from_transport(transport, _unsupported=False):
503
"""Open a bzrdir within a particular directory.
505
:param transport: Transport containing the bzrdir.
506
:param _unsupported: private.
508
format = BzrDirFormat.find_format(transport)
509
BzrDir._check_supported(format, _unsupported)
510
return format.open(transport, _found=True)
512
def open_branch(self, unsupported=False):
513
"""Open the branch object at this BzrDir if one is present.
515
If unsupported is True, then no longer supported branch formats can
518
TODO: static convenience version of this?
520
raise NotImplementedError(self.open_branch)
523
def open_containing(url):
524
"""Open an existing branch which contains url.
526
:param url: url to search from.
527
See open_containing_from_transport for more detail.
529
return BzrDir.open_containing_from_transport(get_transport(url))
532
def open_containing_from_transport(a_transport):
533
"""Open an existing branch which contains a_transport.base
535
This probes for a branch at a_transport, and searches upwards from there.
537
Basically we keep looking up until we find the control directory or
538
run into the root. If there isn't one, raises NotBranchError.
539
If there is one and it is either an unrecognised format or an unsupported
540
format, UnknownFormatError or UnsupportedFormatError are raised.
541
If there is one, it is returned, along with the unused portion of url.
543
:return: The BzrDir that contains the path, and a Unicode path
544
for the rest of the URL.
546
# this gets the normalised url back. I.e. '.' -> the full path.
547
url = a_transport.base
550
result = BzrDir.open_from_transport(a_transport)
551
return result, urlutils.unescape(a_transport.relpath(url))
552
except errors.NotBranchError, e:
554
new_t = a_transport.clone('..')
555
if new_t.base == a_transport.base:
556
# reached the root, whatever that may be
557
raise errors.NotBranchError(path=url)
561
def open_containing_tree_or_branch(klass, location):
562
"""Return the branch and working tree contained by a location.
564
Returns (tree, branch, relpath).
565
If there is no tree at containing the location, tree will be None.
566
If there is no branch containing the location, an exception will be
568
relpath is the portion of the path that is contained by the branch.
570
bzrdir, relpath = klass.open_containing(location)
572
tree = bzrdir.open_workingtree()
573
except (errors.NoWorkingTree, errors.NotLocalUrl):
575
branch = bzrdir.open_branch()
578
return tree, branch, relpath
580
def open_repository(self, _unsupported=False):
581
"""Open the repository object at this BzrDir if one is present.
583
This will not follow the Branch object pointer - its strictly a direct
584
open facility. Most client code should use open_branch().repository to
587
_unsupported is a private parameter, not part of the api.
588
TODO: static convenience version of this?
590
raise NotImplementedError(self.open_repository)
592
def open_workingtree(self, _unsupported=False):
593
"""Open the workingtree object at this BzrDir if one is present.
595
TODO: static convenience version of this?
597
raise NotImplementedError(self.open_workingtree)
599
def has_branch(self):
600
"""Tell if this bzrdir contains a branch.
602
Note: if you're going to open the branch, you should just go ahead
603
and try, and not ask permission first. (This method just opens the
604
branch and discards it, and that's somewhat expensive.)
609
except errors.NotBranchError:
612
def has_workingtree(self):
613
"""Tell if this bzrdir contains a working tree.
615
This will still raise an exception if the bzrdir has a workingtree that
616
is remote & inaccessible.
618
Note: if you're going to open the working tree, you should just go ahead
619
and try, and not ask permission first. (This method just opens the
620
workingtree and discards it, and that's somewhat expensive.)
623
self.open_workingtree()
625
except errors.NoWorkingTree:
628
def _cloning_metadir(self, basis=None):
629
def related_repository(bzrdir):
631
branch = bzrdir.open_branch()
632
return branch.repository
633
except errors.NotBranchError:
635
return bzrdir.open_repository()
636
result_format = self._format.__class__()
639
source_repository = related_repository(self)
640
except errors.NoRepositoryPresent:
643
source_repository = related_repository(self)
644
result_format.repository_format = source_repository._format
645
except errors.NoRepositoryPresent:
646
source_repository = None
648
tree = self.open_workingtree()
649
except (errors.NoWorkingTree, errors.NotLocalUrl):
650
result_format.workingtree_format = None
652
result_format.workingtree_format = tree._format.__class__()
653
return result_format, source_repository
655
def cloning_metadir(self, basis=None):
656
"""Produce a metadir suitable for cloning or sprouting with.
658
These operations may produce workingtrees (yes, even though they're
659
"cloning" something that doesn't have a tree, so a viable workingtree
660
format must be selected.
662
format, repository = self._cloning_metadir()
663
if format._workingtree_format is None:
664
if repository is None:
666
tree_format = repository._format._matchingbzrdir.workingtree_format
667
format.workingtree_format = tree_format.__class__()
670
def checkout_metadir(self):
671
return self.cloning_metadir()
673
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False,
675
"""Create a copy of this bzrdir prepared for use as a new line of
678
If urls last component does not exist, it will be created.
680
Attributes related to the identity of the source branch like
681
branch nickname will be cleaned, a working tree is created
682
whether one existed before or not; and a local branch is always
685
if revision_id is not None, then the clone operation may tune
686
itself to download less data.
689
cloning_format = self.cloning_metadir(basis)
690
result = cloning_format.initialize(url)
691
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
693
source_branch = self.open_branch()
694
source_repository = source_branch.repository
695
except errors.NotBranchError:
698
source_repository = self.open_repository()
699
except errors.NoRepositoryPresent:
700
# copy the entire basis one if there is one
701
# but there is no repository.
702
source_repository = basis_repo
707
result_repo = result.find_repository()
708
except errors.NoRepositoryPresent:
710
if source_repository is None and result_repo is not None:
712
elif source_repository is None and result_repo is None:
713
# no repo available, make a new one
714
result.create_repository()
715
elif source_repository is not None and result_repo is None:
716
# have source, and want to make a new target repo
717
# we don't clone the repo because that preserves attributes
718
# like is_shared(), and we have not yet implemented a
719
# repository sprout().
720
result_repo = result.create_repository()
721
if result_repo is not None:
722
# fetch needed content into target.
724
# XXX FIXME RBC 20060214 need tests for this when the basis
726
result_repo.fetch(basis_repo, revision_id=revision_id)
727
if source_repository is not None:
728
result_repo.fetch(source_repository, revision_id=revision_id)
729
if source_branch is not None:
730
source_branch.sprout(result, revision_id=revision_id)
732
result.create_branch()
733
# TODO: jam 20060426 we probably need a test in here in the
734
# case that the newly sprouted branch is a remote one
735
if result_repo is None or result_repo.make_working_trees():
736
wt = result.create_workingtree()
739
if wt.path2id('') is None:
741
wt.set_root_id(self.open_workingtree.get_root_id())
742
except errors.NoWorkingTree:
748
if recurse == 'down':
750
entries = wt.iter_reference_entries()
751
recurse_branch = wt.branch
752
elif source_branch is not None:
753
entries = source_branch.basis_tree().iter_reference_entries()
754
recurse_branch = source_branch
757
for path, entry in entries:
758
target = urlutils.join(url, urlutils.escape(path))
759
sublocation = source_branch.reference_parent(entry.file_id,
761
sublocation.bzrdir.sprout(target, entry.reference_revision,
762
force_new_repo=force_new_repo, recurse=recurse)
766
class BzrDirPreSplitOut(BzrDir):
767
"""A common class for the all-in-one formats."""
769
def __init__(self, _transport, _format):
770
"""See BzrDir.__init__."""
771
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
772
assert self._format._lock_class == lockable_files.TransportLock
773
assert self._format._lock_file_name == 'branch-lock'
774
self._control_files = lockable_files.LockableFiles(
775
self.get_branch_transport(None),
776
self._format._lock_file_name,
777
self._format._lock_class)
779
def break_lock(self):
780
"""Pre-splitout bzrdirs do not suffer from stale locks."""
781
raise NotImplementedError(self.break_lock)
783
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
784
"""See BzrDir.clone()."""
785
from bzrlib.workingtree import WorkingTreeFormat2
787
result = self._format._initialize_for_clone(url)
788
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
789
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
790
from_branch = self.open_branch()
791
from_branch.clone(result, revision_id=revision_id)
793
self.open_workingtree().clone(result, basis=basis_tree)
794
except errors.NotLocalUrl:
795
# make a new one, this format always has to have one.
797
WorkingTreeFormat2().initialize(result)
798
except errors.NotLocalUrl:
799
# but we cannot do it for remote trees.
800
to_branch = result.open_branch()
801
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
804
def create_branch(self):
805
"""See BzrDir.create_branch."""
806
return self.open_branch()
808
def create_repository(self, shared=False):
809
"""See BzrDir.create_repository."""
811
raise errors.IncompatibleFormat('shared repository', self._format)
812
return self.open_repository()
814
def create_workingtree(self, revision_id=None):
815
"""See BzrDir.create_workingtree."""
816
# this looks buggy but is not -really-
817
# clone and sprout will have set the revision_id
818
# and that will have set it for us, its only
819
# specific uses of create_workingtree in isolation
820
# that can do wonky stuff here, and that only
821
# happens for creating checkouts, which cannot be
822
# done on this format anyway. So - acceptable wart.
823
result = self.open_workingtree()
824
if revision_id is not None:
825
if revision_id == _mod_revision.NULL_REVISION:
826
result.set_parent_ids([])
828
result.set_parent_ids([revision_id])
831
def destroy_workingtree(self):
832
"""See BzrDir.destroy_workingtree."""
833
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
835
def destroy_workingtree_metadata(self):
836
"""See BzrDir.destroy_workingtree_metadata."""
837
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
840
def get_branch_transport(self, branch_format):
841
"""See BzrDir.get_branch_transport()."""
842
if branch_format is None:
843
return self.transport
845
branch_format.get_format_string()
846
except NotImplementedError:
847
return self.transport
848
raise errors.IncompatibleFormat(branch_format, self._format)
850
def get_repository_transport(self, repository_format):
851
"""See BzrDir.get_repository_transport()."""
852
if repository_format is None:
853
return self.transport
855
repository_format.get_format_string()
856
except NotImplementedError:
857
return self.transport
858
raise errors.IncompatibleFormat(repository_format, self._format)
860
def get_workingtree_transport(self, workingtree_format):
861
"""See BzrDir.get_workingtree_transport()."""
862
if workingtree_format is None:
863
return self.transport
865
workingtree_format.get_format_string()
866
except NotImplementedError:
867
return self.transport
868
raise errors.IncompatibleFormat(workingtree_format, self._format)
870
def needs_format_conversion(self, format=None):
871
"""See BzrDir.needs_format_conversion()."""
872
# if the format is not the same as the system default,
873
# an upgrade is needed.
875
format = BzrDirFormat.get_default_format()
876
return not isinstance(self._format, format.__class__)
878
def open_branch(self, unsupported=False):
879
"""See BzrDir.open_branch."""
880
from bzrlib.branch import BzrBranchFormat4
881
format = BzrBranchFormat4()
882
self._check_supported(format, unsupported)
883
return format.open(self, _found=True)
885
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
886
"""See BzrDir.sprout()."""
887
from bzrlib.workingtree import WorkingTreeFormat2
889
result = self._format._initialize_for_clone(url)
890
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
892
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
893
except errors.NoRepositoryPresent:
896
self.open_branch().sprout(result, revision_id=revision_id)
897
except errors.NotBranchError:
899
# we always want a working tree
900
WorkingTreeFormat2().initialize(result)
904
class BzrDir4(BzrDirPreSplitOut):
905
"""A .bzr version 4 control object.
907
This is a deprecated format and may be removed after sept 2006.
910
def create_repository(self, shared=False):
911
"""See BzrDir.create_repository."""
912
return self._format.repository_format.initialize(self, shared)
914
def needs_format_conversion(self, format=None):
915
"""Format 4 dirs are always in need of conversion."""
918
def open_repository(self):
919
"""See BzrDir.open_repository."""
920
from bzrlib.repofmt.weaverepo import RepositoryFormat4
921
return RepositoryFormat4().open(self, _found=True)
924
class BzrDir5(BzrDirPreSplitOut):
925
"""A .bzr version 5 control object.
927
This is a deprecated format and may be removed after sept 2006.
930
def open_repository(self):
931
"""See BzrDir.open_repository."""
932
from bzrlib.repofmt.weaverepo import RepositoryFormat5
933
return RepositoryFormat5().open(self, _found=True)
935
def open_workingtree(self, _unsupported=False):
936
"""See BzrDir.create_workingtree."""
937
from bzrlib.workingtree import WorkingTreeFormat2
938
return WorkingTreeFormat2().open(self, _found=True)
941
class BzrDir6(BzrDirPreSplitOut):
942
"""A .bzr version 6 control object.
944
This is a deprecated format and may be removed after sept 2006.
947
def open_repository(self):
948
"""See BzrDir.open_repository."""
949
from bzrlib.repofmt.weaverepo import RepositoryFormat6
950
return RepositoryFormat6().open(self, _found=True)
952
def open_workingtree(self, _unsupported=False):
953
"""See BzrDir.create_workingtree."""
954
from bzrlib.workingtree import WorkingTreeFormat2
955
return WorkingTreeFormat2().open(self, _found=True)
958
class BzrDirMeta1(BzrDir):
959
"""A .bzr meta version 1 control object.
961
This is the first control object where the
962
individual aspects are really split out: there are separate repository,
963
workingtree and branch subdirectories and any subset of the three can be
964
present within a BzrDir.
967
def can_convert_format(self):
968
"""See BzrDir.can_convert_format()."""
971
def create_branch(self):
972
"""See BzrDir.create_branch."""
973
return self._format.get_branch_format().initialize(self)
975
def create_repository(self, shared=False):
976
"""See BzrDir.create_repository."""
977
return self._format.repository_format.initialize(self, shared)
979
def create_workingtree(self, revision_id=None):
980
"""See BzrDir.create_workingtree."""
981
from bzrlib.workingtree import WorkingTreeFormat
982
return self._format.workingtree_format.initialize(self, revision_id)
984
def destroy_workingtree(self):
985
"""See BzrDir.destroy_workingtree."""
986
wt = self.open_workingtree()
987
repository = wt.branch.repository
988
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
989
wt.revert([], old_tree=empty)
990
self.destroy_workingtree_metadata()
992
def destroy_workingtree_metadata(self):
993
self.transport.delete_tree('checkout')
995
def _get_mkdir_mode(self):
996
"""Figure out the mode to use when creating a bzrdir subdir."""
997
temp_control = lockable_files.LockableFiles(self.transport, '',
998
lockable_files.TransportLock)
999
return temp_control._dir_mode
1001
def get_branch_transport(self, branch_format):
1002
"""See BzrDir.get_branch_transport()."""
1003
if branch_format is None:
1004
return self.transport.clone('branch')
1006
branch_format.get_format_string()
1007
except NotImplementedError:
1008
raise errors.IncompatibleFormat(branch_format, self._format)
1010
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1011
except errors.FileExists:
1013
return self.transport.clone('branch')
1015
def get_repository_transport(self, repository_format):
1016
"""See BzrDir.get_repository_transport()."""
1017
if repository_format is None:
1018
return self.transport.clone('repository')
1020
repository_format.get_format_string()
1021
except NotImplementedError:
1022
raise errors.IncompatibleFormat(repository_format, self._format)
1024
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1025
except errors.FileExists:
1027
return self.transport.clone('repository')
1029
def get_workingtree_transport(self, workingtree_format):
1030
"""See BzrDir.get_workingtree_transport()."""
1031
if workingtree_format is None:
1032
return self.transport.clone('checkout')
1034
workingtree_format.get_format_string()
1035
except NotImplementedError:
1036
raise errors.IncompatibleFormat(workingtree_format, self._format)
1038
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1039
except errors.FileExists:
1041
return self.transport.clone('checkout')
1043
def needs_format_conversion(self, format=None):
1044
"""See BzrDir.needs_format_conversion()."""
1046
format = BzrDirFormat.get_default_format()
1047
if not isinstance(self._format, format.__class__):
1048
# it is not a meta dir format, conversion is needed.
1050
# we might want to push this down to the repository?
1052
if not isinstance(self.open_repository()._format,
1053
format.repository_format.__class__):
1054
# the repository needs an upgrade.
1056
except errors.NoRepositoryPresent:
1059
if not isinstance(self.open_branch()._format,
1060
format.get_branch_format().__class__):
1061
# the repository needs an upgrade.
1063
except errors.NotBranchError:
1065
# currently there are no other possible conversions for meta1 formats.
1068
def open_branch(self, unsupported=False):
1069
"""See BzrDir.open_branch."""
1070
from bzrlib.branch import BranchFormat
1071
format = BranchFormat.find_format(self)
1072
self._check_supported(format, unsupported)
1073
return format.open(self, _found=True)
1075
def open_repository(self, unsupported=False):
1076
"""See BzrDir.open_repository."""
1077
from bzrlib.repository import RepositoryFormat
1078
format = RepositoryFormat.find_format(self)
1079
self._check_supported(format, unsupported)
1080
return format.open(self, _found=True)
1082
def open_workingtree(self, unsupported=False):
1083
"""See BzrDir.open_workingtree."""
1084
from bzrlib.workingtree import WorkingTreeFormat
1085
format = WorkingTreeFormat.find_format(self)
1086
self._check_supported(format, unsupported)
1087
return format.open(self, _found=True)
1090
class BzrDirFormat(object):
1091
"""An encapsulation of the initialization and open routines for a format.
1093
Formats provide three things:
1094
* An initialization routine,
1098
Formats are placed in an dict by their format string for reference
1099
during bzrdir opening. These should be subclasses of BzrDirFormat
1102
Once a format is deprecated, just deprecate the initialize and open
1103
methods on the format class. Do not deprecate the object, as the
1104
object will be created every system load.
1107
_default_format = None
1108
"""The default format used for new .bzr dirs."""
1111
"""The known formats."""
1113
_control_formats = []
1114
"""The registered control formats - .bzr, ....
1116
This is a list of BzrDirFormat objects.
1119
_lock_file_name = 'branch-lock'
1121
# _lock_class must be set in subclasses to the lock type, typ.
1122
# TransportLock or LockDir
1125
def find_format(klass, transport):
1126
"""Return the format present at transport."""
1127
for format in klass._control_formats:
1129
return format.probe_transport(transport)
1130
except errors.NotBranchError:
1131
# this format does not find a control dir here.
1133
raise errors.NotBranchError(path=transport.base)
1136
def probe_transport(klass, transport):
1137
"""Return the .bzrdir style transport present at URL."""
1139
format_string = transport.get(".bzr/branch-format").read()
1140
except errors.NoSuchFile:
1141
raise errors.NotBranchError(path=transport.base)
1144
return klass._formats[format_string]
1146
raise errors.UnknownFormatError(format=format_string)
1149
def get_default_format(klass):
1150
"""Return the current default format."""
1151
return klass._default_format
1153
def get_format_string(self):
1154
"""Return the ASCII format string that identifies this format."""
1155
raise NotImplementedError(self.get_format_string)
1157
def get_format_description(self):
1158
"""Return the short description for this format."""
1159
raise NotImplementedError(self.get_format_description)
1161
def get_converter(self, format=None):
1162
"""Return the converter to use to convert bzrdirs needing converts.
1164
This returns a bzrlib.bzrdir.Converter object.
1166
This should return the best upgrader to step this format towards the
1167
current default format. In the case of plugins we can/should provide
1168
some means for them to extend the range of returnable converters.
1170
:param format: Optional format to override the default format of the
1173
raise NotImplementedError(self.get_converter)
1175
def initialize(self, url):
1176
"""Create a bzr control dir at this url and return an opened copy.
1178
Subclasses should typically override initialize_on_transport
1179
instead of this method.
1181
return self.initialize_on_transport(get_transport(url))
1183
def initialize_on_transport(self, transport):
1184
"""Initialize a new bzrdir in the base directory of a Transport."""
1185
# Since we don't have a .bzr directory, inherit the
1186
# mode from the root directory
1187
temp_control = lockable_files.LockableFiles(transport,
1188
'', lockable_files.TransportLock)
1189
temp_control._transport.mkdir('.bzr',
1190
# FIXME: RBC 20060121 don't peek under
1192
mode=temp_control._dir_mode)
1193
file_mode = temp_control._file_mode
1195
mutter('created control directory in ' + transport.base)
1196
control = transport.clone('.bzr')
1197
utf8_files = [('README',
1198
"This is a Bazaar-NG control directory.\n"
1199
"Do not change any files in this directory.\n"),
1200
('branch-format', self.get_format_string()),
1202
# NB: no need to escape relative paths that are url safe.
1203
control_files = lockable_files.LockableFiles(control,
1204
self._lock_file_name, self._lock_class)
1205
control_files.create_lock()
1206
control_files.lock_write()
1208
for file, content in utf8_files:
1209
control_files.put_utf8(file, content)
1211
control_files.unlock()
1212
return self.open(transport, _found=True)
1214
def is_supported(self):
1215
"""Is this format supported?
1217
Supported formats must be initializable and openable.
1218
Unsupported formats may not support initialization or committing or
1219
some other features depending on the reason for not being supported.
1223
def same_model(self, target_format):
1224
return (self.repository_format.rich_root_data ==
1225
target_format.rich_root_data)
1228
def known_formats(klass):
1229
"""Return all the known formats.
1231
Concrete formats should override _known_formats.
1233
# There is double indirection here to make sure that control
1234
# formats used by more than one dir format will only be probed
1235
# once. This can otherwise be quite expensive for remote connections.
1237
for format in klass._control_formats:
1238
result.update(format._known_formats())
1242
def _known_formats(klass):
1243
"""Return the known format instances for this control format."""
1244
return set(klass._formats.values())
1246
def open(self, transport, _found=False):
1247
"""Return an instance of this format for the dir transport points at.
1249
_found is a private parameter, do not use it.
1252
found_format = BzrDirFormat.find_format(transport)
1253
if not isinstance(found_format, self.__class__):
1254
raise AssertionError("%s was asked to open %s, but it seems to need "
1256
% (self, transport, found_format))
1257
return self._open(transport)
1259
def _open(self, transport):
1260
"""Template method helper for opening BzrDirectories.
1262
This performs the actual open and any additional logic or parameter
1265
raise NotImplementedError(self._open)
1268
def register_format(klass, format):
1269
klass._formats[format.get_format_string()] = format
1272
def register_control_format(klass, format):
1273
"""Register a format that does not use '.bzrdir' for its control dir.
1275
TODO: This should be pulled up into a 'ControlDirFormat' base class
1276
which BzrDirFormat can inherit from, and renamed to register_format
1277
there. It has been done without that for now for simplicity of
1280
klass._control_formats.append(format)
1283
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1284
def set_default_format(klass, format):
1285
klass._set_default_format(format)
1288
def _set_default_format(klass, format):
1289
"""Set default format (for testing behavior of defaults only)"""
1290
klass._default_format = format
1293
return self.get_format_string()[:-1]
1296
def unregister_format(klass, format):
1297
assert klass._formats[format.get_format_string()] is format
1298
del klass._formats[format.get_format_string()]
1301
def unregister_control_format(klass, format):
1302
klass._control_formats.remove(format)
1305
# register BzrDirFormat as a control format
1306
BzrDirFormat.register_control_format(BzrDirFormat)
1309
class BzrDirFormat4(BzrDirFormat):
1310
"""Bzr dir format 4.
1312
This format is a combined format for working tree, branch and repository.
1314
- Format 1 working trees [always]
1315
- Format 4 branches [always]
1316
- Format 4 repositories [always]
1318
This format is deprecated: it indexes texts using a text it which is
1319
removed in format 5; write support for this format has been removed.
1322
_lock_class = lockable_files.TransportLock
1324
def get_format_string(self):
1325
"""See BzrDirFormat.get_format_string()."""
1326
return "Bazaar-NG branch, format 0.0.4\n"
1328
def get_format_description(self):
1329
"""See BzrDirFormat.get_format_description()."""
1330
return "All-in-one format 4"
1332
def get_converter(self, format=None):
1333
"""See BzrDirFormat.get_converter()."""
1334
# there is one and only one upgrade path here.
1335
return ConvertBzrDir4To5()
1337
def initialize_on_transport(self, transport):
1338
"""Format 4 branches cannot be created."""
1339
raise errors.UninitializableFormat(self)
1341
def is_supported(self):
1342
"""Format 4 is not supported.
1344
It is not supported because the model changed from 4 to 5 and the
1345
conversion logic is expensive - so doing it on the fly was not
1350
def _open(self, transport):
1351
"""See BzrDirFormat._open."""
1352
return BzrDir4(transport, self)
1354
def __return_repository_format(self):
1355
"""Circular import protection."""
1356
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1357
return RepositoryFormat4()
1358
repository_format = property(__return_repository_format)
1361
class BzrDirFormat5(BzrDirFormat):
1362
"""Bzr control format 5.
1364
This format is a combined format for working tree, branch and repository.
1366
- Format 2 working trees [always]
1367
- Format 4 branches [always]
1368
- Format 5 repositories [always]
1369
Unhashed stores in the repository.
1372
_lock_class = lockable_files.TransportLock
1374
def get_format_string(self):
1375
"""See BzrDirFormat.get_format_string()."""
1376
return "Bazaar-NG branch, format 5\n"
1378
def get_format_description(self):
1379
"""See BzrDirFormat.get_format_description()."""
1380
return "All-in-one format 5"
1382
def get_converter(self, format=None):
1383
"""See BzrDirFormat.get_converter()."""
1384
# there is one and only one upgrade path here.
1385
return ConvertBzrDir5To6()
1387
def _initialize_for_clone(self, url):
1388
return self.initialize_on_transport(get_transport(url), _cloning=True)
1390
def initialize_on_transport(self, transport, _cloning=False):
1391
"""Format 5 dirs always have working tree, branch and repository.
1393
Except when they are being cloned.
1395
from bzrlib.branch import BzrBranchFormat4
1396
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1397
from bzrlib.workingtree import WorkingTreeFormat2
1398
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1399
RepositoryFormat5().initialize(result, _internal=True)
1401
branch = BzrBranchFormat4().initialize(result)
1403
WorkingTreeFormat2().initialize(result)
1404
except errors.NotLocalUrl:
1405
# Even though we can't access the working tree, we need to
1406
# create its control files.
1407
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1410
def _open(self, transport):
1411
"""See BzrDirFormat._open."""
1412
return BzrDir5(transport, self)
1414
def __return_repository_format(self):
1415
"""Circular import protection."""
1416
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1417
return RepositoryFormat5()
1418
repository_format = property(__return_repository_format)
1421
class BzrDirFormat6(BzrDirFormat):
1422
"""Bzr control format 6.
1424
This format is a combined format for working tree, branch and repository.
1426
- Format 2 working trees [always]
1427
- Format 4 branches [always]
1428
- Format 6 repositories [always]
1431
_lock_class = lockable_files.TransportLock
1433
def get_format_string(self):
1434
"""See BzrDirFormat.get_format_string()."""
1435
return "Bazaar-NG branch, format 6\n"
1437
def get_format_description(self):
1438
"""See BzrDirFormat.get_format_description()."""
1439
return "All-in-one format 6"
1441
def get_converter(self, format=None):
1442
"""See BzrDirFormat.get_converter()."""
1443
# there is one and only one upgrade path here.
1444
return ConvertBzrDir6ToMeta()
1446
def _initialize_for_clone(self, url):
1447
return self.initialize_on_transport(get_transport(url), _cloning=True)
1449
def initialize_on_transport(self, transport, _cloning=False):
1450
"""Format 6 dirs always have working tree, branch and repository.
1452
Except when they are being cloned.
1454
from bzrlib.branch import BzrBranchFormat4
1455
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1456
from bzrlib.workingtree import WorkingTreeFormat2
1457
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1458
RepositoryFormat6().initialize(result, _internal=True)
1460
branch = BzrBranchFormat4().initialize(result)
1462
WorkingTreeFormat2().initialize(result)
1463
except errors.NotLocalUrl:
1464
# Even though we can't access the working tree, we need to
1465
# create its control files.
1466
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1469
def _open(self, transport):
1470
"""See BzrDirFormat._open."""
1471
return BzrDir6(transport, self)
1473
def __return_repository_format(self):
1474
"""Circular import protection."""
1475
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1476
return RepositoryFormat6()
1477
repository_format = property(__return_repository_format)
1480
class BzrDirMetaFormat1(BzrDirFormat):
1481
"""Bzr meta control format 1
1483
This is the first format with split out working tree, branch and repository
1486
- Format 3 working trees [optional]
1487
- Format 5 branches [optional]
1488
- Format 7 repositories [optional]
1491
_lock_class = lockdir.LockDir
1494
self._workingtree_format = None
1495
self._branch_format = None
1497
def __eq__(self, other):
1498
if other.__class__ is not self.__class__:
1500
if other.repository_format != self.repository_format:
1502
if other.workingtree_format != self.workingtree_format:
1506
def __ne__(self, other):
1507
return not self == other
1509
def get_branch_format(self):
1510
if self._branch_format is None:
1511
from bzrlib.branch import BranchFormat
1512
self._branch_format = BranchFormat.get_default_format()
1513
return self._branch_format
1515
def set_branch_format(self, format):
1516
self._branch_format = format
1518
def get_converter(self, format=None):
1519
"""See BzrDirFormat.get_converter()."""
1521
format = BzrDirFormat.get_default_format()
1522
if not isinstance(self, format.__class__):
1523
# converting away from metadir is not implemented
1524
raise NotImplementedError(self.get_converter)
1525
return ConvertMetaToMeta(format)
1527
def get_format_string(self):
1528
"""See BzrDirFormat.get_format_string()."""
1529
return "Bazaar-NG meta directory, format 1\n"
1531
def get_format_description(self):
1532
"""See BzrDirFormat.get_format_description()."""
1533
return "Meta directory format 1"
1535
def _open(self, transport):
1536
"""See BzrDirFormat._open."""
1537
return BzrDirMeta1(transport, self)
1539
def __return_repository_format(self):
1540
"""Circular import protection."""
1541
if getattr(self, '_repository_format', None):
1542
return self._repository_format
1543
from bzrlib.repository import RepositoryFormat
1544
return RepositoryFormat.get_default_format()
1546
def __set_repository_format(self, value):
1547
"""Allow changint the repository format for metadir formats."""
1548
self._repository_format = value
1550
repository_format = property(__return_repository_format, __set_repository_format)
1552
def __get_workingtree_format(self):
1553
if self._workingtree_format is None:
1554
from bzrlib.workingtree import WorkingTreeFormat
1555
self._workingtree_format = WorkingTreeFormat.get_default_format()
1556
return self._workingtree_format
1558
def __set_workingtree_format(self, wt_format):
1559
self._workingtree_format = wt_format
1561
workingtree_format = property(__get_workingtree_format,
1562
__set_workingtree_format)
1565
BzrDirFormat.register_format(BzrDirFormat4())
1566
BzrDirFormat.register_format(BzrDirFormat5())
1567
BzrDirFormat.register_format(BzrDirFormat6())
1568
__default_format = BzrDirMetaFormat1()
1569
BzrDirFormat.register_format(__default_format)
1570
BzrDirFormat._default_format = __default_format
1573
class BzrDirTestProviderAdapter(object):
1574
"""A tool to generate a suite testing multiple bzrdir formats at once.
1576
This is done by copying the test once for each transport and injecting
1577
the transport_server, transport_readonly_server, and bzrdir_format
1578
classes into each copy. Each copy is also given a new id() to make it
1582
def __init__(self, transport_server, transport_readonly_server, formats):
1583
self._transport_server = transport_server
1584
self._transport_readonly_server = transport_readonly_server
1585
self._formats = formats
1587
def adapt(self, test):
1588
result = unittest.TestSuite()
1589
for format in self._formats:
1590
new_test = deepcopy(test)
1591
new_test.transport_server = self._transport_server
1592
new_test.transport_readonly_server = self._transport_readonly_server
1593
new_test.bzrdir_format = format
1594
def make_new_test_id():
1595
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1596
return lambda: new_id
1597
new_test.id = make_new_test_id()
1598
result.addTest(new_test)
1602
class Converter(object):
1603
"""Converts a disk format object from one format to another."""
1605
def convert(self, to_convert, pb):
1606
"""Perform the conversion of to_convert, giving feedback via pb.
1608
:param to_convert: The disk object to convert.
1609
:param pb: a progress bar to use for progress information.
1612
def step(self, message):
1613
"""Update the pb by a step."""
1615
self.pb.update(message, self.count, self.total)
1618
class ConvertBzrDir4To5(Converter):
1619
"""Converts format 4 bzr dirs to format 5."""
1622
super(ConvertBzrDir4To5, self).__init__()
1623
self.converted_revs = set()
1624
self.absent_revisions = set()
1628
def convert(self, to_convert, pb):
1629
"""See Converter.convert()."""
1630
self.bzrdir = to_convert
1632
self.pb.note('starting upgrade from format 4 to 5')
1633
if isinstance(self.bzrdir.transport, LocalTransport):
1634
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1635
self._convert_to_weaves()
1636
return BzrDir.open(self.bzrdir.root_transport.base)
1638
def _convert_to_weaves(self):
1639
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1642
stat = self.bzrdir.transport.stat('weaves')
1643
if not S_ISDIR(stat.st_mode):
1644
self.bzrdir.transport.delete('weaves')
1645
self.bzrdir.transport.mkdir('weaves')
1646
except errors.NoSuchFile:
1647
self.bzrdir.transport.mkdir('weaves')
1648
# deliberately not a WeaveFile as we want to build it up slowly.
1649
self.inv_weave = Weave('inventory')
1650
# holds in-memory weaves for all files
1651
self.text_weaves = {}
1652
self.bzrdir.transport.delete('branch-format')
1653
self.branch = self.bzrdir.open_branch()
1654
self._convert_working_inv()
1655
rev_history = self.branch.revision_history()
1656
# to_read is a stack holding the revisions we still need to process;
1657
# appending to it adds new highest-priority revisions
1658
self.known_revisions = set(rev_history)
1659
self.to_read = rev_history[-1:]
1661
rev_id = self.to_read.pop()
1662
if (rev_id not in self.revisions
1663
and rev_id not in self.absent_revisions):
1664
self._load_one_rev(rev_id)
1666
to_import = self._make_order()
1667
for i, rev_id in enumerate(to_import):
1668
self.pb.update('converting revision', i, len(to_import))
1669
self._convert_one_rev(rev_id)
1671
self._write_all_weaves()
1672
self._write_all_revs()
1673
self.pb.note('upgraded to weaves:')
1674
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1675
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1676
self.pb.note(' %6d texts', self.text_count)
1677
self._cleanup_spare_files_after_format4()
1678
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1680
def _cleanup_spare_files_after_format4(self):
1681
# FIXME working tree upgrade foo.
1682
for n in 'merged-patches', 'pending-merged-patches':
1684
## assert os.path.getsize(p) == 0
1685
self.bzrdir.transport.delete(n)
1686
except errors.NoSuchFile:
1688
self.bzrdir.transport.delete_tree('inventory-store')
1689
self.bzrdir.transport.delete_tree('text-store')
1691
def _convert_working_inv(self):
1692
inv = xml4.serializer_v4.read_inventory(
1693
self.branch.control_files.get('inventory'))
1694
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1695
# FIXME inventory is a working tree change.
1696
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1698
def _write_all_weaves(self):
1699
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1700
weave_transport = self.bzrdir.transport.clone('weaves')
1701
weaves = WeaveStore(weave_transport, prefixed=False)
1702
transaction = WriteTransaction()
1706
for file_id, file_weave in self.text_weaves.items():
1707
self.pb.update('writing weave', i, len(self.text_weaves))
1708
weaves._put_weave(file_id, file_weave, transaction)
1710
self.pb.update('inventory', 0, 1)
1711
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1712
self.pb.update('inventory', 1, 1)
1716
def _write_all_revs(self):
1717
"""Write all revisions out in new form."""
1718
self.bzrdir.transport.delete_tree('revision-store')
1719
self.bzrdir.transport.mkdir('revision-store')
1720
revision_transport = self.bzrdir.transport.clone('revision-store')
1722
_revision_store = TextRevisionStore(TextStore(revision_transport,
1726
transaction = WriteTransaction()
1727
for i, rev_id in enumerate(self.converted_revs):
1728
self.pb.update('write revision', i, len(self.converted_revs))
1729
_revision_store.add_revision(self.revisions[rev_id], transaction)
1733
def _load_one_rev(self, rev_id):
1734
"""Load a revision object into memory.
1736
Any parents not either loaded or abandoned get queued to be
1738
self.pb.update('loading revision',
1739
len(self.revisions),
1740
len(self.known_revisions))
1741
if not self.branch.repository.has_revision(rev_id):
1743
self.pb.note('revision {%s} not present in branch; '
1744
'will be converted as a ghost',
1746
self.absent_revisions.add(rev_id)
1748
rev = self.branch.repository._revision_store.get_revision(rev_id,
1749
self.branch.repository.get_transaction())
1750
for parent_id in rev.parent_ids:
1751
self.known_revisions.add(parent_id)
1752
self.to_read.append(parent_id)
1753
self.revisions[rev_id] = rev
1755
def _load_old_inventory(self, rev_id):
1756
assert rev_id not in self.converted_revs
1757
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1758
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1759
inv.revision_id = rev_id
1760
rev = self.revisions[rev_id]
1761
if rev.inventory_sha1:
1762
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1763
'inventory sha mismatch for {%s}' % rev_id
1766
def _load_updated_inventory(self, rev_id):
1767
assert rev_id in self.converted_revs
1768
inv_xml = self.inv_weave.get_text(rev_id)
1769
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1772
def _convert_one_rev(self, rev_id):
1773
"""Convert revision and all referenced objects to new format."""
1774
rev = self.revisions[rev_id]
1775
inv = self._load_old_inventory(rev_id)
1776
present_parents = [p for p in rev.parent_ids
1777
if p not in self.absent_revisions]
1778
self._convert_revision_contents(rev, inv, present_parents)
1779
self._store_new_weave(rev, inv, present_parents)
1780
self.converted_revs.add(rev_id)
1782
def _store_new_weave(self, rev, inv, present_parents):
1783
# the XML is now updated with text versions
1785
entries = inv.iter_entries()
1787
for path, ie in entries:
1788
assert getattr(ie, 'revision', None) is not None, \
1789
'no revision on {%s} in {%s}' % \
1790
(file_id, rev.revision_id)
1791
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1792
new_inv_sha1 = sha_string(new_inv_xml)
1793
self.inv_weave.add_lines(rev.revision_id,
1795
new_inv_xml.splitlines(True))
1796
rev.inventory_sha1 = new_inv_sha1
1798
def _convert_revision_contents(self, rev, inv, present_parents):
1799
"""Convert all the files within a revision.
1801
Also upgrade the inventory to refer to the text revision ids."""
1802
rev_id = rev.revision_id
1803
mutter('converting texts of revision {%s}',
1805
parent_invs = map(self._load_updated_inventory, present_parents)
1806
entries = inv.iter_entries()
1808
for path, ie in entries:
1809
self._convert_file_version(rev, ie, parent_invs)
1811
def _convert_file_version(self, rev, ie, parent_invs):
1812
"""Convert one version of one file.
1814
The file needs to be added into the weave if it is a merge
1815
of >=2 parents or if it's changed from its parent.
1817
file_id = ie.file_id
1818
rev_id = rev.revision_id
1819
w = self.text_weaves.get(file_id)
1822
self.text_weaves[file_id] = w
1823
text_changed = False
1824
previous_entries = ie.find_previous_heads(parent_invs,
1828
for old_revision in previous_entries:
1829
# if this fails, its a ghost ?
1830
assert old_revision in self.converted_revs, \
1831
"Revision {%s} not in converted_revs" % old_revision
1832
self.snapshot_ie(previous_entries, ie, w, rev_id)
1834
assert getattr(ie, 'revision', None) is not None
1836
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1837
# TODO: convert this logic, which is ~= snapshot to
1838
# a call to:. This needs the path figured out. rather than a work_tree
1839
# a v4 revision_tree can be given, or something that looks enough like
1840
# one to give the file content to the entry if it needs it.
1841
# and we need something that looks like a weave store for snapshot to
1843
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1844
if len(previous_revisions) == 1:
1845
previous_ie = previous_revisions.values()[0]
1846
if ie._unchanged(previous_ie):
1847
ie.revision = previous_ie.revision
1850
text = self.branch.repository.text_store.get(ie.text_id)
1851
file_lines = text.readlines()
1852
assert sha_strings(file_lines) == ie.text_sha1
1853
assert sum(map(len, file_lines)) == ie.text_size
1854
w.add_lines(rev_id, previous_revisions, file_lines)
1855
self.text_count += 1
1857
w.add_lines(rev_id, previous_revisions, [])
1858
ie.revision = rev_id
1860
def _make_order(self):
1861
"""Return a suitable order for importing revisions.
1863
The order must be such that an revision is imported after all
1864
its (present) parents.
1866
todo = set(self.revisions.keys())
1867
done = self.absent_revisions.copy()
1870
# scan through looking for a revision whose parents
1872
for rev_id in sorted(list(todo)):
1873
rev = self.revisions[rev_id]
1874
parent_ids = set(rev.parent_ids)
1875
if parent_ids.issubset(done):
1876
# can take this one now
1877
order.append(rev_id)
1883
class ConvertBzrDir5To6(Converter):
1884
"""Converts format 5 bzr dirs to format 6."""
1886
def convert(self, to_convert, pb):
1887
"""See Converter.convert()."""
1888
self.bzrdir = to_convert
1890
self.pb.note('starting upgrade from format 5 to 6')
1891
self._convert_to_prefixed()
1892
return BzrDir.open(self.bzrdir.root_transport.base)
1894
def _convert_to_prefixed(self):
1895
from bzrlib.store import TransportStore
1896
self.bzrdir.transport.delete('branch-format')
1897
for store_name in ["weaves", "revision-store"]:
1898
self.pb.note("adding prefixes to %s" % store_name)
1899
store_transport = self.bzrdir.transport.clone(store_name)
1900
store = TransportStore(store_transport, prefixed=True)
1901
for urlfilename in store_transport.list_dir('.'):
1902
filename = urlutils.unescape(urlfilename)
1903
if (filename.endswith(".weave") or
1904
filename.endswith(".gz") or
1905
filename.endswith(".sig")):
1906
file_id = os.path.splitext(filename)[0]
1909
prefix_dir = store.hash_prefix(file_id)
1910
# FIXME keep track of the dirs made RBC 20060121
1912
store_transport.move(filename, prefix_dir + '/' + filename)
1913
except errors.NoSuchFile: # catches missing dirs strangely enough
1914
store_transport.mkdir(prefix_dir)
1915
store_transport.move(filename, prefix_dir + '/' + filename)
1916
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1919
class ConvertBzrDir6ToMeta(Converter):
1920
"""Converts format 6 bzr dirs to metadirs."""
1922
def convert(self, to_convert, pb):
1923
"""See Converter.convert()."""
1924
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1925
from bzrlib.branch import BzrBranchFormat5
1926
self.bzrdir = to_convert
1929
self.total = 20 # the steps we know about
1930
self.garbage_inventories = []
1932
self.pb.note('starting upgrade from format 6 to metadir')
1933
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1934
# its faster to move specific files around than to open and use the apis...
1935
# first off, nuke ancestry.weave, it was never used.
1937
self.step('Removing ancestry.weave')
1938
self.bzrdir.transport.delete('ancestry.weave')
1939
except errors.NoSuchFile:
1941
# find out whats there
1942
self.step('Finding branch files')
1943
last_revision = self.bzrdir.open_branch().last_revision()
1944
bzrcontents = self.bzrdir.transport.list_dir('.')
1945
for name in bzrcontents:
1946
if name.startswith('basis-inventory.'):
1947
self.garbage_inventories.append(name)
1948
# create new directories for repository, working tree and branch
1949
self.dir_mode = self.bzrdir._control_files._dir_mode
1950
self.file_mode = self.bzrdir._control_files._file_mode
1951
repository_names = [('inventory.weave', True),
1952
('revision-store', True),
1954
self.step('Upgrading repository ')
1955
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1956
self.make_lock('repository')
1957
# we hard code the formats here because we are converting into
1958
# the meta format. The meta format upgrader can take this to a
1959
# future format within each component.
1960
self.put_format('repository', RepositoryFormat7())
1961
for entry in repository_names:
1962
self.move_entry('repository', entry)
1964
self.step('Upgrading branch ')
1965
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1966
self.make_lock('branch')
1967
self.put_format('branch', BzrBranchFormat5())
1968
branch_files = [('revision-history', True),
1969
('branch-name', True),
1971
for entry in branch_files:
1972
self.move_entry('branch', entry)
1974
checkout_files = [('pending-merges', True),
1975
('inventory', True),
1976
('stat-cache', False)]
1977
# If a mandatory checkout file is not present, the branch does not have
1978
# a functional checkout. Do not create a checkout in the converted
1980
for name, mandatory in checkout_files:
1981
if mandatory and name not in bzrcontents:
1982
has_checkout = False
1986
if not has_checkout:
1987
self.pb.note('No working tree.')
1988
# If some checkout files are there, we may as well get rid of them.
1989
for name, mandatory in checkout_files:
1990
if name in bzrcontents:
1991
self.bzrdir.transport.delete(name)
1993
from bzrlib.workingtree import WorkingTreeFormat3
1994
self.step('Upgrading working tree')
1995
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1996
self.make_lock('checkout')
1998
'checkout', WorkingTreeFormat3())
1999
self.bzrdir.transport.delete_multi(
2000
self.garbage_inventories, self.pb)
2001
for entry in checkout_files:
2002
self.move_entry('checkout', entry)
2003
if last_revision is not None:
2004
self.bzrdir._control_files.put_utf8(
2005
'checkout/last-revision', last_revision)
2006
self.bzrdir._control_files.put_utf8(
2007
'branch-format', BzrDirMetaFormat1().get_format_string())
2008
return BzrDir.open(self.bzrdir.root_transport.base)
2010
def make_lock(self, name):
2011
"""Make a lock for the new control dir name."""
2012
self.step('Make %s lock' % name)
2013
ld = lockdir.LockDir(self.bzrdir.transport,
2015
file_modebits=self.file_mode,
2016
dir_modebits=self.dir_mode)
2019
def move_entry(self, new_dir, entry):
2020
"""Move then entry name into new_dir."""
2022
mandatory = entry[1]
2023
self.step('Moving %s' % name)
2025
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2026
except errors.NoSuchFile:
2030
def put_format(self, dirname, format):
2031
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2034
class ConvertMetaToMeta(Converter):
2035
"""Converts the components of metadirs."""
2037
def __init__(self, target_format):
2038
"""Create a metadir to metadir converter.
2040
:param target_format: The final metadir format that is desired.
2042
self.target_format = target_format
2044
def convert(self, to_convert, pb):
2045
"""See Converter.convert()."""
2046
self.bzrdir = to_convert
2050
self.step('checking repository format')
2052
repo = self.bzrdir.open_repository()
2053
except errors.NoRepositoryPresent:
2056
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2057
from bzrlib.repository import CopyConverter
2058
self.pb.note('starting repository conversion')
2059
converter = CopyConverter(self.target_format.repository_format)
2060
converter.convert(repo, pb)
2062
branch = self.bzrdir.open_branch()
2063
except errors.NotBranchError:
2066
# Avoid circular imports
2067
from bzrlib import branch as _mod_branch
2068
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2069
self.target_format.get_branch_format().__class__ is
2070
_mod_branch.BzrBranchFormat6):
2071
branch_converter = _mod_branch.Converter5to6()
2072
branch_converter.convert(branch)
2076
class BzrDirFormatInfo(object):
2078
def __init__(self, native, deprecated):
2079
self.deprecated = deprecated
2080
self.native = native
2083
class BzrDirFormatRegistry(registry.Registry):
2084
"""Registry of user-selectable BzrDir subformats.
2086
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2087
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2090
def register_metadir(self, key,
2091
repository_format, help, native=True, deprecated=False,
2094
"""Register a metadir subformat.
2096
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2097
by the Repository format.
2099
:param repository_format: The fully-qualified repository format class
2101
:param branch_format: Fully-qualified branch format class name as
2103
:param tree_format: Fully-qualified tree format class name as
2106
# This should be expanded to support setting WorkingTree and Branch
2107
# formats, once BzrDirMetaFormat1 supports that.
2108
def _load(full_name):
2109
mod_name, factory_name = full_name.rsplit('.', 1)
2111
mod = __import__(mod_name, globals(), locals(),
2113
except ImportError, e:
2114
raise ImportError('failed to load %s: %s' % (full_name, e))
2116
factory = getattr(mod, factory_name)
2117
except AttributeError:
2118
raise AttributeError('no factory %s in module %r'
2123
bd = BzrDirMetaFormat1()
2124
if branch_format is not None:
2125
bd.set_branch_format(_load(branch_format))
2126
if tree_format is not None:
2127
bd.workingtree_format = _load(tree_format)
2128
if repository_format is not None:
2129
bd.repository_format = _load(repository_format)
2131
self.register(key, helper, help, native, deprecated)
2133
def register(self, key, factory, help, native=True, deprecated=False):
2134
"""Register a BzrDirFormat factory.
2136
The factory must be a callable that takes one parameter: the key.
2137
It must produce an instance of the BzrDirFormat when called.
2139
This function mainly exists to prevent the info object from being
2142
registry.Registry.register(self, key, factory, help,
2143
BzrDirFormatInfo(native, deprecated))
2145
def register_lazy(self, key, module_name, member_name, help, native=True,
2147
registry.Registry.register_lazy(self, key, module_name, member_name,
2148
help, BzrDirFormatInfo(native, deprecated))
2150
def set_default(self, key):
2151
"""Set the 'default' key to be a clone of the supplied key.
2153
This method must be called once and only once.
2155
registry.Registry.register(self, 'default', self.get(key),
2156
self.get_help(key), info=self.get_info(key))
2158
def set_default_repository(self, key):
2159
"""Set the FormatRegistry default and Repository default.
2161
This is a transitional method while Repository.set_default_format
2164
if 'default' in self:
2165
self.remove('default')
2166
self.set_default(key)
2167
format = self.get('default')()
2168
assert isinstance(format, BzrDirMetaFormat1)
2170
def make_bzrdir(self, key):
2171
return self.get(key)()
2173
def help_topic(self, topic):
2174
output = textwrap.dedent("""\
2175
Bazaar directory formats
2176
------------------------
2178
These formats can be used for creating branches, working trees, and
2182
default_help = self.get_help('default')
2184
for key in self.keys():
2185
if key == 'default':
2187
help = self.get_help(key)
2188
if help == default_help:
2189
default_realkey = key
2191
help_pairs.append((key, help))
2193
def wrapped(key, help, info):
2195
help = '(native) ' + help
2196
return ' %s:\n%s\n\n' % (key,
2197
textwrap.fill(help, initial_indent=' ',
2198
subsequent_indent=' '))
2199
output += wrapped('%s/default' % default_realkey, default_help,
2200
self.get_info('default'))
2201
deprecated_pairs = []
2202
for key, help in help_pairs:
2203
info = self.get_info(key)
2205
deprecated_pairs.append((key, help))
2207
output += wrapped(key, help, info)
2208
if len(deprecated_pairs) > 0:
2209
output += "Deprecated formats\n------------------\n\n"
2210
for key, help in deprecated_pairs:
2211
info = self.get_info(key)
2212
output += wrapped(key, help, info)
2217
format_registry = BzrDirFormatRegistry()
2218
format_registry.register('weave', BzrDirFormat6,
2219
'Pre-0.8 format. Slower than knit and does not'
2220
' support checkouts or shared repositories.',
2222
format_registry.register_metadir('knit',
2223
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2224
'Format using knits. Recommended.',
2225
branch_format='bzrlib.branch.BzrBranchFormat5')
2226
format_registry.set_default('knit')
2227
format_registry.register_metadir('metaweave',
2228
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2229
'Transitional format in 0.8. Slower than knit.',
2231
format_registry.register_metadir('experimental-knit2',
2232
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
2233
'Experimental successor to knit. Use at your own risk.',
2234
branch_format='bzrlib.branch.BzrBranchFormat5',
2235
tree_format='bzrlib.workingtree.WorkingTreeFormat3'
2237
format_registry.register_metadir('experimental-branch6',
2238
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2239
'Experimental successor to knit. Use at your own risk.',
2240
branch_format='bzrlib.branch.BzrBranchFormat6',
2241
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2243
format_registry.register_metadir('experimental-reference-dirstate',
2244
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2245
help='Experimental: dirstate working tree, Branch6, and reference-tree '
2246
'support. Proposed default for bzr 0.15',
2247
branch_format='bzrlib.branch.BzrBranchFormat6',
2248
tree_format='bzrlib.workingtree_4.WorkingTreeFormat4',