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
436
a 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
"""Produce a metadir suitable for cloning with"""
630
def related_repository(bzrdir):
632
branch = bzrdir.open_branch()
633
return branch.repository
634
except errors.NotBranchError:
636
return bzrdir.open_repository()
637
result_format = self._format.__class__()
640
source_repository = related_repository(self)
641
except errors.NoRepositoryPresent:
644
source_repository = related_repository(self)
645
result_format.repository_format = source_repository._format
646
except errors.NoRepositoryPresent:
650
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
651
"""Create a copy of this bzrdir prepared for use as a new line of
654
If urls last component does not exist, it will be created.
656
Attributes related to the identity of the source branch like
657
branch nickname will be cleaned, a working tree is created
658
whether one existed before or not; and a local branch is always
661
if revision_id is not None, then the clone operation may tune
662
itself to download less data.
665
cloning_format = self.cloning_metadir(basis)
666
result = cloning_format.initialize(url)
667
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
669
source_branch = self.open_branch()
670
source_repository = source_branch.repository
671
except errors.NotBranchError:
674
source_repository = self.open_repository()
675
except errors.NoRepositoryPresent:
676
# copy the entire basis one if there is one
677
# but there is no repository.
678
source_repository = basis_repo
683
result_repo = result.find_repository()
684
except errors.NoRepositoryPresent:
686
if source_repository is None and result_repo is not None:
688
elif source_repository is None and result_repo is None:
689
# no repo available, make a new one
690
result.create_repository()
691
elif source_repository is not None and result_repo is None:
692
# have source, and want to make a new target repo
693
# we don't clone the repo because that preserves attributes
694
# like is_shared(), and we have not yet implemented a
695
# repository sprout().
696
result_repo = result.create_repository()
697
if result_repo is not None:
698
# fetch needed content into target.
700
# XXX FIXME RBC 20060214 need tests for this when the basis
702
result_repo.fetch(basis_repo, revision_id=revision_id)
703
if source_repository is not None:
704
result_repo.fetch(source_repository, revision_id=revision_id)
705
if source_branch is not None:
706
source_branch.sprout(result, revision_id=revision_id)
708
result.create_branch()
709
# TODO: jam 20060426 we probably need a test in here in the
710
# case that the newly sprouted branch is a remote one
711
if result_repo is None or result_repo.make_working_trees():
712
wt = result.create_workingtree()
713
if wt.inventory.root is None:
715
wt.set_root_id(self.open_workingtree.get_root_id())
716
except errors.NoWorkingTree:
721
class BzrDirPreSplitOut(BzrDir):
722
"""A common class for the all-in-one formats."""
724
def __init__(self, _transport, _format):
725
"""See BzrDir.__init__."""
726
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
727
assert self._format._lock_class == lockable_files.TransportLock
728
assert self._format._lock_file_name == 'branch-lock'
729
self._control_files = lockable_files.LockableFiles(
730
self.get_branch_transport(None),
731
self._format._lock_file_name,
732
self._format._lock_class)
734
def break_lock(self):
735
"""Pre-splitout bzrdirs do not suffer from stale locks."""
736
raise NotImplementedError(self.break_lock)
738
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
739
"""See BzrDir.clone()."""
740
from bzrlib.workingtree import WorkingTreeFormat2
742
result = self._format._initialize_for_clone(url)
743
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
744
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
745
from_branch = self.open_branch()
746
from_branch.clone(result, revision_id=revision_id)
748
self.open_workingtree().clone(result, basis=basis_tree)
749
except errors.NotLocalUrl:
750
# make a new one, this format always has to have one.
752
WorkingTreeFormat2().initialize(result)
753
except errors.NotLocalUrl:
754
# but we cannot do it for remote trees.
755
to_branch = result.open_branch()
756
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
759
def create_branch(self):
760
"""See BzrDir.create_branch."""
761
return self.open_branch()
763
def create_repository(self, shared=False):
764
"""See BzrDir.create_repository."""
766
raise errors.IncompatibleFormat('shared repository', self._format)
767
return self.open_repository()
769
def create_workingtree(self, revision_id=None):
770
"""See BzrDir.create_workingtree."""
771
# this looks buggy but is not -really-
772
# clone and sprout will have set the revision_id
773
# and that will have set it for us, its only
774
# specific uses of create_workingtree in isolation
775
# that can do wonky stuff here, and that only
776
# happens for creating checkouts, which cannot be
777
# done on this format anyway. So - acceptable wart.
778
result = self.open_workingtree()
779
if revision_id is not None:
780
if revision_id == _mod_revision.NULL_REVISION:
781
result.set_parent_ids([])
783
result.set_parent_ids([revision_id])
786
def destroy_workingtree(self):
787
"""See BzrDir.destroy_workingtree."""
788
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
790
def destroy_workingtree_metadata(self):
791
"""See BzrDir.destroy_workingtree_metadata."""
792
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
795
def get_branch_transport(self, branch_format):
796
"""See BzrDir.get_branch_transport()."""
797
if branch_format is None:
798
return self.transport
800
branch_format.get_format_string()
801
except NotImplementedError:
802
return self.transport
803
raise errors.IncompatibleFormat(branch_format, self._format)
805
def get_repository_transport(self, repository_format):
806
"""See BzrDir.get_repository_transport()."""
807
if repository_format is None:
808
return self.transport
810
repository_format.get_format_string()
811
except NotImplementedError:
812
return self.transport
813
raise errors.IncompatibleFormat(repository_format, self._format)
815
def get_workingtree_transport(self, workingtree_format):
816
"""See BzrDir.get_workingtree_transport()."""
817
if workingtree_format is None:
818
return self.transport
820
workingtree_format.get_format_string()
821
except NotImplementedError:
822
return self.transport
823
raise errors.IncompatibleFormat(workingtree_format, self._format)
825
def needs_format_conversion(self, format=None):
826
"""See BzrDir.needs_format_conversion()."""
827
# if the format is not the same as the system default,
828
# an upgrade is needed.
830
format = BzrDirFormat.get_default_format()
831
return not isinstance(self._format, format.__class__)
833
def open_branch(self, unsupported=False):
834
"""See BzrDir.open_branch."""
835
from bzrlib.branch import BzrBranchFormat4
836
format = BzrBranchFormat4()
837
self._check_supported(format, unsupported)
838
return format.open(self, _found=True)
840
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
841
"""See BzrDir.sprout()."""
842
from bzrlib.workingtree import WorkingTreeFormat2
844
result = self._format._initialize_for_clone(url)
845
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
847
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
848
except errors.NoRepositoryPresent:
851
self.open_branch().sprout(result, revision_id=revision_id)
852
except errors.NotBranchError:
854
# we always want a working tree
855
WorkingTreeFormat2().initialize(result)
859
class BzrDir4(BzrDirPreSplitOut):
860
"""A .bzr version 4 control object.
862
This is a deprecated format and may be removed after sept 2006.
865
def create_repository(self, shared=False):
866
"""See BzrDir.create_repository."""
867
return self._format.repository_format.initialize(self, shared)
869
def needs_format_conversion(self, format=None):
870
"""Format 4 dirs are always in need of conversion."""
873
def open_repository(self):
874
"""See BzrDir.open_repository."""
875
from bzrlib.repofmt.weaverepo import RepositoryFormat4
876
return RepositoryFormat4().open(self, _found=True)
879
class BzrDir5(BzrDirPreSplitOut):
880
"""A .bzr version 5 control object.
882
This is a deprecated format and may be removed after sept 2006.
885
def open_repository(self):
886
"""See BzrDir.open_repository."""
887
from bzrlib.repofmt.weaverepo import RepositoryFormat5
888
return RepositoryFormat5().open(self, _found=True)
890
def open_workingtree(self, _unsupported=False):
891
"""See BzrDir.create_workingtree."""
892
from bzrlib.workingtree import WorkingTreeFormat2
893
return WorkingTreeFormat2().open(self, _found=True)
896
class BzrDir6(BzrDirPreSplitOut):
897
"""A .bzr version 6 control object.
899
This is a deprecated format and may be removed after sept 2006.
902
def open_repository(self):
903
"""See BzrDir.open_repository."""
904
from bzrlib.repofmt.weaverepo import RepositoryFormat6
905
return RepositoryFormat6().open(self, _found=True)
907
def open_workingtree(self, _unsupported=False):
908
"""See BzrDir.create_workingtree."""
909
from bzrlib.workingtree import WorkingTreeFormat2
910
return WorkingTreeFormat2().open(self, _found=True)
913
class BzrDirMeta1(BzrDir):
914
"""A .bzr meta version 1 control object.
916
This is the first control object where the
917
individual aspects are really split out: there are separate repository,
918
workingtree and branch subdirectories and any subset of the three can be
919
present within a BzrDir.
922
def can_convert_format(self):
923
"""See BzrDir.can_convert_format()."""
926
def create_branch(self):
927
"""See BzrDir.create_branch."""
928
from bzrlib.branch import BranchFormat
929
return BranchFormat.get_default_format().initialize(self)
931
def create_repository(self, shared=False):
932
"""See BzrDir.create_repository."""
933
return self._format.repository_format.initialize(self, shared)
935
def create_workingtree(self, revision_id=None):
936
"""See BzrDir.create_workingtree."""
937
from bzrlib.workingtree import WorkingTreeFormat
938
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
940
def destroy_workingtree(self):
941
"""See BzrDir.destroy_workingtree."""
942
wt = self.open_workingtree()
943
repository = wt.branch.repository
944
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
945
wt.revert([], old_tree=empty)
946
self.destroy_workingtree_metadata()
948
def destroy_workingtree_metadata(self):
949
self.transport.delete_tree('checkout')
951
def _get_mkdir_mode(self):
952
"""Figure out the mode to use when creating a bzrdir subdir."""
953
temp_control = lockable_files.LockableFiles(self.transport, '',
954
lockable_files.TransportLock)
955
return temp_control._dir_mode
957
def get_branch_transport(self, branch_format):
958
"""See BzrDir.get_branch_transport()."""
959
if branch_format is None:
960
return self.transport.clone('branch')
962
branch_format.get_format_string()
963
except NotImplementedError:
964
raise errors.IncompatibleFormat(branch_format, self._format)
966
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
967
except errors.FileExists:
969
return self.transport.clone('branch')
971
def get_repository_transport(self, repository_format):
972
"""See BzrDir.get_repository_transport()."""
973
if repository_format is None:
974
return self.transport.clone('repository')
976
repository_format.get_format_string()
977
except NotImplementedError:
978
raise errors.IncompatibleFormat(repository_format, self._format)
980
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
981
except errors.FileExists:
983
return self.transport.clone('repository')
985
def get_workingtree_transport(self, workingtree_format):
986
"""See BzrDir.get_workingtree_transport()."""
987
if workingtree_format is None:
988
return self.transport.clone('checkout')
990
workingtree_format.get_format_string()
991
except NotImplementedError:
992
raise errors.IncompatibleFormat(workingtree_format, self._format)
994
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
995
except errors.FileExists:
997
return self.transport.clone('checkout')
999
def needs_format_conversion(self, format=None):
1000
"""See BzrDir.needs_format_conversion()."""
1002
format = BzrDirFormat.get_default_format()
1003
if not isinstance(self._format, format.__class__):
1004
# it is not a meta dir format, conversion is needed.
1006
# we might want to push this down to the repository?
1008
if not isinstance(self.open_repository()._format,
1009
format.repository_format.__class__):
1010
# the repository needs an upgrade.
1012
except errors.NoRepositoryPresent:
1014
# currently there are no other possible conversions for meta1 formats.
1017
def open_branch(self, unsupported=False):
1018
"""See BzrDir.open_branch."""
1019
from bzrlib.branch import BranchFormat
1020
format = BranchFormat.find_format(self)
1021
self._check_supported(format, unsupported)
1022
return format.open(self, _found=True)
1024
def open_repository(self, unsupported=False):
1025
"""See BzrDir.open_repository."""
1026
from bzrlib.repository import RepositoryFormat
1027
format = RepositoryFormat.find_format(self)
1028
self._check_supported(format, unsupported)
1029
return format.open(self, _found=True)
1031
def open_workingtree(self, unsupported=False):
1032
"""See BzrDir.open_workingtree."""
1033
from bzrlib.workingtree import WorkingTreeFormat
1034
format = WorkingTreeFormat.find_format(self)
1035
self._check_supported(format, unsupported)
1036
return format.open(self, _found=True)
1039
class BzrDirFormat(object):
1040
"""An encapsulation of the initialization and open routines for a format.
1042
Formats provide three things:
1043
* An initialization routine,
1047
Formats are placed in an dict by their format string for reference
1048
during bzrdir opening. These should be subclasses of BzrDirFormat
1051
Once a format is deprecated, just deprecate the initialize and open
1052
methods on the format class. Do not deprecate the object, as the
1053
object will be created every system load.
1056
_default_format = None
1057
"""The default format used for new .bzr dirs."""
1060
"""The known formats."""
1062
_control_formats = []
1063
"""The registered control formats - .bzr, ....
1065
This is a list of BzrDirFormat objects.
1068
_lock_file_name = 'branch-lock'
1070
# _lock_class must be set in subclasses to the lock type, typ.
1071
# TransportLock or LockDir
1074
def find_format(klass, transport):
1075
"""Return the format present at transport."""
1076
for format in klass._control_formats:
1078
return format.probe_transport(transport)
1079
except errors.NotBranchError:
1080
# this format does not find a control dir here.
1082
raise errors.NotBranchError(path=transport.base)
1085
def probe_transport(klass, transport):
1086
"""Return the .bzrdir style transport present at URL."""
1088
format_string = transport.get(".bzr/branch-format").read()
1089
except errors.NoSuchFile:
1090
raise errors.NotBranchError(path=transport.base)
1093
return klass._formats[format_string]
1095
raise errors.UnknownFormatError(format=format_string)
1098
def get_default_format(klass):
1099
"""Return the current default format."""
1100
return klass._default_format
1102
def get_format_string(self):
1103
"""Return the ASCII format string that identifies this format."""
1104
raise NotImplementedError(self.get_format_string)
1106
def get_format_description(self):
1107
"""Return the short description for this format."""
1108
raise NotImplementedError(self.get_format_description)
1110
def get_converter(self, format=None):
1111
"""Return the converter to use to convert bzrdirs needing converts.
1113
This returns a bzrlib.bzrdir.Converter object.
1115
This should return the best upgrader to step this format towards the
1116
current default format. In the case of plugins we can/should provide
1117
some means for them to extend the range of returnable converters.
1119
:param format: Optional format to override the default format of the
1122
raise NotImplementedError(self.get_converter)
1124
def initialize(self, url):
1125
"""Create a bzr control dir at this url and return an opened copy.
1127
Subclasses should typically override initialize_on_transport
1128
instead of this method.
1130
return self.initialize_on_transport(get_transport(url))
1132
def initialize_on_transport(self, transport):
1133
"""Initialize a new bzrdir in the base directory of a Transport."""
1134
# Since we don't have a .bzr directory, inherit the
1135
# mode from the root directory
1136
temp_control = lockable_files.LockableFiles(transport,
1137
'', lockable_files.TransportLock)
1138
temp_control._transport.mkdir('.bzr',
1139
# FIXME: RBC 20060121 don't peek under
1141
mode=temp_control._dir_mode)
1142
file_mode = temp_control._file_mode
1144
mutter('created control directory in ' + transport.base)
1145
control = transport.clone('.bzr')
1146
utf8_files = [('README',
1147
"This is a Bazaar-NG control directory.\n"
1148
"Do not change any files in this directory.\n"),
1149
('branch-format', self.get_format_string()),
1151
# NB: no need to escape relative paths that are url safe.
1152
control_files = lockable_files.LockableFiles(control,
1153
self._lock_file_name, self._lock_class)
1154
control_files.create_lock()
1155
control_files.lock_write()
1157
for file, content in utf8_files:
1158
control_files.put_utf8(file, content)
1160
control_files.unlock()
1161
return self.open(transport, _found=True)
1163
def is_supported(self):
1164
"""Is this format supported?
1166
Supported formats must be initializable and openable.
1167
Unsupported formats may not support initialization or committing or
1168
some other features depending on the reason for not being supported.
1172
def same_model(self, target_format):
1173
return (self.repository_format.rich_root_data ==
1174
target_format.rich_root_data)
1177
def known_formats(klass):
1178
"""Return all the known formats.
1180
Concrete formats should override _known_formats.
1182
# There is double indirection here to make sure that control
1183
# formats used by more than one dir format will only be probed
1184
# once. This can otherwise be quite expensive for remote connections.
1186
for format in klass._control_formats:
1187
result.update(format._known_formats())
1191
def _known_formats(klass):
1192
"""Return the known format instances for this control format."""
1193
return set(klass._formats.values())
1195
def open(self, transport, _found=False):
1196
"""Return an instance of this format for the dir transport points at.
1198
_found is a private parameter, do not use it.
1201
found_format = BzrDirFormat.find_format(transport)
1202
if not isinstance(found_format, self.__class__):
1203
raise AssertionError("%s was asked to open %s, but it seems to need "
1205
% (self, transport, found_format))
1206
return self._open(transport)
1208
def _open(self, transport):
1209
"""Template method helper for opening BzrDirectories.
1211
This performs the actual open and any additional logic or parameter
1214
raise NotImplementedError(self._open)
1217
def register_format(klass, format):
1218
klass._formats[format.get_format_string()] = format
1221
def register_control_format(klass, format):
1222
"""Register a format that does not use '.bzrdir' for its control dir.
1224
TODO: This should be pulled up into a 'ControlDirFormat' base class
1225
which BzrDirFormat can inherit from, and renamed to register_format
1226
there. It has been done without that for now for simplicity of
1229
klass._control_formats.append(format)
1232
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1233
def set_default_format(klass, format):
1234
klass._set_default_format(format)
1237
def _set_default_format(klass, format):
1238
"""Set default format (for testing behavior of defaults only)"""
1239
klass._default_format = format
1242
return self.get_format_string()[:-1]
1245
def unregister_format(klass, format):
1246
assert klass._formats[format.get_format_string()] is format
1247
del klass._formats[format.get_format_string()]
1250
def unregister_control_format(klass, format):
1251
klass._control_formats.remove(format)
1254
# register BzrDirFormat as a control format
1255
BzrDirFormat.register_control_format(BzrDirFormat)
1258
class BzrDirFormat4(BzrDirFormat):
1259
"""Bzr dir format 4.
1261
This format is a combined format for working tree, branch and repository.
1263
- Format 1 working trees [always]
1264
- Format 4 branches [always]
1265
- Format 4 repositories [always]
1267
This format is deprecated: it indexes texts using a text it which is
1268
removed in format 5; write support for this format has been removed.
1271
_lock_class = lockable_files.TransportLock
1273
def get_format_string(self):
1274
"""See BzrDirFormat.get_format_string()."""
1275
return "Bazaar-NG branch, format 0.0.4\n"
1277
def get_format_description(self):
1278
"""See BzrDirFormat.get_format_description()."""
1279
return "All-in-one format 4"
1281
def get_converter(self, format=None):
1282
"""See BzrDirFormat.get_converter()."""
1283
# there is one and only one upgrade path here.
1284
return ConvertBzrDir4To5()
1286
def initialize_on_transport(self, transport):
1287
"""Format 4 branches cannot be created."""
1288
raise errors.UninitializableFormat(self)
1290
def is_supported(self):
1291
"""Format 4 is not supported.
1293
It is not supported because the model changed from 4 to 5 and the
1294
conversion logic is expensive - so doing it on the fly was not
1299
def _open(self, transport):
1300
"""See BzrDirFormat._open."""
1301
return BzrDir4(transport, self)
1303
def __return_repository_format(self):
1304
"""Circular import protection."""
1305
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1306
return RepositoryFormat4()
1307
repository_format = property(__return_repository_format)
1310
class BzrDirFormat5(BzrDirFormat):
1311
"""Bzr control format 5.
1313
This format is a combined format for working tree, branch and repository.
1315
- Format 2 working trees [always]
1316
- Format 4 branches [always]
1317
- Format 5 repositories [always]
1318
Unhashed stores in the repository.
1321
_lock_class = lockable_files.TransportLock
1323
def get_format_string(self):
1324
"""See BzrDirFormat.get_format_string()."""
1325
return "Bazaar-NG branch, format 5\n"
1327
def get_format_description(self):
1328
"""See BzrDirFormat.get_format_description()."""
1329
return "All-in-one format 5"
1331
def get_converter(self, format=None):
1332
"""See BzrDirFormat.get_converter()."""
1333
# there is one and only one upgrade path here.
1334
return ConvertBzrDir5To6()
1336
def _initialize_for_clone(self, url):
1337
return self.initialize_on_transport(get_transport(url), _cloning=True)
1339
def initialize_on_transport(self, transport, _cloning=False):
1340
"""Format 5 dirs always have working tree, branch and repository.
1342
Except when they are being cloned.
1344
from bzrlib.branch import BzrBranchFormat4
1345
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1346
from bzrlib.workingtree import WorkingTreeFormat2
1347
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1348
RepositoryFormat5().initialize(result, _internal=True)
1350
branch = BzrBranchFormat4().initialize(result)
1352
WorkingTreeFormat2().initialize(result)
1353
except errors.NotLocalUrl:
1354
# Even though we can't access the working tree, we need to
1355
# create its control files.
1356
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1359
def _open(self, transport):
1360
"""See BzrDirFormat._open."""
1361
return BzrDir5(transport, self)
1363
def __return_repository_format(self):
1364
"""Circular import protection."""
1365
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1366
return RepositoryFormat5()
1367
repository_format = property(__return_repository_format)
1370
class BzrDirFormat6(BzrDirFormat):
1371
"""Bzr control format 6.
1373
This format is a combined format for working tree, branch and repository.
1375
- Format 2 working trees [always]
1376
- Format 4 branches [always]
1377
- Format 6 repositories [always]
1380
_lock_class = lockable_files.TransportLock
1382
def get_format_string(self):
1383
"""See BzrDirFormat.get_format_string()."""
1384
return "Bazaar-NG branch, format 6\n"
1386
def get_format_description(self):
1387
"""See BzrDirFormat.get_format_description()."""
1388
return "All-in-one format 6"
1390
def get_converter(self, format=None):
1391
"""See BzrDirFormat.get_converter()."""
1392
# there is one and only one upgrade path here.
1393
return ConvertBzrDir6ToMeta()
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 6 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 RepositoryFormat6
1405
from bzrlib.workingtree import WorkingTreeFormat2
1406
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1407
RepositoryFormat6().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 BzrDir6(transport, self)
1422
def __return_repository_format(self):
1423
"""Circular import protection."""
1424
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1425
return RepositoryFormat6()
1426
repository_format = property(__return_repository_format)
1429
class BzrDirMetaFormat1(BzrDirFormat):
1430
"""Bzr meta control format 1
1432
This is the first format with split out working tree, branch and repository
1435
- Format 3 working trees [optional]
1436
- Format 5 branches [optional]
1437
- Format 7 repositories [optional]
1440
_lock_class = lockdir.LockDir
1442
def get_converter(self, format=None):
1443
"""See BzrDirFormat.get_converter()."""
1445
format = BzrDirFormat.get_default_format()
1446
if not isinstance(self, format.__class__):
1447
# converting away from metadir is not implemented
1448
raise NotImplementedError(self.get_converter)
1449
return ConvertMetaToMeta(format)
1451
def get_format_string(self):
1452
"""See BzrDirFormat.get_format_string()."""
1453
return "Bazaar-NG meta directory, format 1\n"
1455
def get_format_description(self):
1456
"""See BzrDirFormat.get_format_description()."""
1457
return "Meta directory format 1"
1459
def _open(self, transport):
1460
"""See BzrDirFormat._open."""
1461
return BzrDirMeta1(transport, self)
1463
def __return_repository_format(self):
1464
"""Circular import protection."""
1465
if getattr(self, '_repository_format', None):
1466
return self._repository_format
1467
from bzrlib.repository import RepositoryFormat
1468
return RepositoryFormat.get_default_format()
1470
def __set_repository_format(self, value):
1471
"""Allow changint the repository format for metadir formats."""
1472
self._repository_format = value
1474
repository_format = property(__return_repository_format, __set_repository_format)
1477
BzrDirFormat.register_format(BzrDirFormat4())
1478
BzrDirFormat.register_format(BzrDirFormat5())
1479
BzrDirFormat.register_format(BzrDirFormat6())
1480
__default_format = BzrDirMetaFormat1()
1481
BzrDirFormat.register_format(__default_format)
1482
BzrDirFormat._default_format = __default_format
1485
class BzrDirTestProviderAdapter(object):
1486
"""A tool to generate a suite testing multiple bzrdir formats at once.
1488
This is done by copying the test once for each transport and injecting
1489
the transport_server, transport_readonly_server, and bzrdir_format
1490
classes into each copy. Each copy is also given a new id() to make it
1494
def __init__(self, transport_server, transport_readonly_server, formats):
1495
self._transport_server = transport_server
1496
self._transport_readonly_server = transport_readonly_server
1497
self._formats = formats
1499
def adapt(self, test):
1500
result = unittest.TestSuite()
1501
for format in self._formats:
1502
new_test = deepcopy(test)
1503
new_test.transport_server = self._transport_server
1504
new_test.transport_readonly_server = self._transport_readonly_server
1505
new_test.bzrdir_format = format
1506
def make_new_test_id():
1507
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1508
return lambda: new_id
1509
new_test.id = make_new_test_id()
1510
result.addTest(new_test)
1514
class Converter(object):
1515
"""Converts a disk format object from one format to another."""
1517
def convert(self, to_convert, pb):
1518
"""Perform the conversion of to_convert, giving feedback via pb.
1520
:param to_convert: The disk object to convert.
1521
:param pb: a progress bar to use for progress information.
1524
def step(self, message):
1525
"""Update the pb by a step."""
1527
self.pb.update(message, self.count, self.total)
1530
class ConvertBzrDir4To5(Converter):
1531
"""Converts format 4 bzr dirs to format 5."""
1534
super(ConvertBzrDir4To5, self).__init__()
1535
self.converted_revs = set()
1536
self.absent_revisions = set()
1540
def convert(self, to_convert, pb):
1541
"""See Converter.convert()."""
1542
self.bzrdir = to_convert
1544
self.pb.note('starting upgrade from format 4 to 5')
1545
if isinstance(self.bzrdir.transport, LocalTransport):
1546
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1547
self._convert_to_weaves()
1548
return BzrDir.open(self.bzrdir.root_transport.base)
1550
def _convert_to_weaves(self):
1551
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1554
stat = self.bzrdir.transport.stat('weaves')
1555
if not S_ISDIR(stat.st_mode):
1556
self.bzrdir.transport.delete('weaves')
1557
self.bzrdir.transport.mkdir('weaves')
1558
except errors.NoSuchFile:
1559
self.bzrdir.transport.mkdir('weaves')
1560
# deliberately not a WeaveFile as we want to build it up slowly.
1561
self.inv_weave = Weave('inventory')
1562
# holds in-memory weaves for all files
1563
self.text_weaves = {}
1564
self.bzrdir.transport.delete('branch-format')
1565
self.branch = self.bzrdir.open_branch()
1566
self._convert_working_inv()
1567
rev_history = self.branch.revision_history()
1568
# to_read is a stack holding the revisions we still need to process;
1569
# appending to it adds new highest-priority revisions
1570
self.known_revisions = set(rev_history)
1571
self.to_read = rev_history[-1:]
1573
rev_id = self.to_read.pop()
1574
if (rev_id not in self.revisions
1575
and rev_id not in self.absent_revisions):
1576
self._load_one_rev(rev_id)
1578
to_import = self._make_order()
1579
for i, rev_id in enumerate(to_import):
1580
self.pb.update('converting revision', i, len(to_import))
1581
self._convert_one_rev(rev_id)
1583
self._write_all_weaves()
1584
self._write_all_revs()
1585
self.pb.note('upgraded to weaves:')
1586
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1587
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1588
self.pb.note(' %6d texts', self.text_count)
1589
self._cleanup_spare_files_after_format4()
1590
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1592
def _cleanup_spare_files_after_format4(self):
1593
# FIXME working tree upgrade foo.
1594
for n in 'merged-patches', 'pending-merged-patches':
1596
## assert os.path.getsize(p) == 0
1597
self.bzrdir.transport.delete(n)
1598
except errors.NoSuchFile:
1600
self.bzrdir.transport.delete_tree('inventory-store')
1601
self.bzrdir.transport.delete_tree('text-store')
1603
def _convert_working_inv(self):
1604
inv = xml4.serializer_v4.read_inventory(
1605
self.branch.control_files.get('inventory'))
1606
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1607
# FIXME inventory is a working tree change.
1608
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1610
def _write_all_weaves(self):
1611
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1612
weave_transport = self.bzrdir.transport.clone('weaves')
1613
weaves = WeaveStore(weave_transport, prefixed=False)
1614
transaction = WriteTransaction()
1618
for file_id, file_weave in self.text_weaves.items():
1619
self.pb.update('writing weave', i, len(self.text_weaves))
1620
weaves._put_weave(file_id, file_weave, transaction)
1622
self.pb.update('inventory', 0, 1)
1623
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1624
self.pb.update('inventory', 1, 1)
1628
def _write_all_revs(self):
1629
"""Write all revisions out in new form."""
1630
self.bzrdir.transport.delete_tree('revision-store')
1631
self.bzrdir.transport.mkdir('revision-store')
1632
revision_transport = self.bzrdir.transport.clone('revision-store')
1634
_revision_store = TextRevisionStore(TextStore(revision_transport,
1638
transaction = WriteTransaction()
1639
for i, rev_id in enumerate(self.converted_revs):
1640
self.pb.update('write revision', i, len(self.converted_revs))
1641
_revision_store.add_revision(self.revisions[rev_id], transaction)
1645
def _load_one_rev(self, rev_id):
1646
"""Load a revision object into memory.
1648
Any parents not either loaded or abandoned get queued to be
1650
self.pb.update('loading revision',
1651
len(self.revisions),
1652
len(self.known_revisions))
1653
if not self.branch.repository.has_revision(rev_id):
1655
self.pb.note('revision {%s} not present in branch; '
1656
'will be converted as a ghost',
1658
self.absent_revisions.add(rev_id)
1660
rev = self.branch.repository._revision_store.get_revision(rev_id,
1661
self.branch.repository.get_transaction())
1662
for parent_id in rev.parent_ids:
1663
self.known_revisions.add(parent_id)
1664
self.to_read.append(parent_id)
1665
self.revisions[rev_id] = rev
1667
def _load_old_inventory(self, rev_id):
1668
assert rev_id not in self.converted_revs
1669
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1670
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1671
inv.revision_id = rev_id
1672
rev = self.revisions[rev_id]
1673
if rev.inventory_sha1:
1674
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1675
'inventory sha mismatch for {%s}' % rev_id
1678
def _load_updated_inventory(self, rev_id):
1679
assert rev_id in self.converted_revs
1680
inv_xml = self.inv_weave.get_text(rev_id)
1681
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1684
def _convert_one_rev(self, rev_id):
1685
"""Convert revision and all referenced objects to new format."""
1686
rev = self.revisions[rev_id]
1687
inv = self._load_old_inventory(rev_id)
1688
present_parents = [p for p in rev.parent_ids
1689
if p not in self.absent_revisions]
1690
self._convert_revision_contents(rev, inv, present_parents)
1691
self._store_new_weave(rev, inv, present_parents)
1692
self.converted_revs.add(rev_id)
1694
def _store_new_weave(self, rev, inv, present_parents):
1695
# the XML is now updated with text versions
1697
entries = inv.iter_entries()
1699
for path, ie in entries:
1700
assert getattr(ie, 'revision', None) is not None, \
1701
'no revision on {%s} in {%s}' % \
1702
(file_id, rev.revision_id)
1703
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1704
new_inv_sha1 = sha_string(new_inv_xml)
1705
self.inv_weave.add_lines(rev.revision_id,
1707
new_inv_xml.splitlines(True))
1708
rev.inventory_sha1 = new_inv_sha1
1710
def _convert_revision_contents(self, rev, inv, present_parents):
1711
"""Convert all the files within a revision.
1713
Also upgrade the inventory to refer to the text revision ids."""
1714
rev_id = rev.revision_id
1715
mutter('converting texts of revision {%s}',
1717
parent_invs = map(self._load_updated_inventory, present_parents)
1718
entries = inv.iter_entries()
1720
for path, ie in entries:
1721
self._convert_file_version(rev, ie, parent_invs)
1723
def _convert_file_version(self, rev, ie, parent_invs):
1724
"""Convert one version of one file.
1726
The file needs to be added into the weave if it is a merge
1727
of >=2 parents or if it's changed from its parent.
1729
file_id = ie.file_id
1730
rev_id = rev.revision_id
1731
w = self.text_weaves.get(file_id)
1734
self.text_weaves[file_id] = w
1735
text_changed = False
1736
previous_entries = ie.find_previous_heads(parent_invs,
1740
for old_revision in previous_entries:
1741
# if this fails, its a ghost ?
1742
assert old_revision in self.converted_revs, \
1743
"Revision {%s} not in converted_revs" % old_revision
1744
self.snapshot_ie(previous_entries, ie, w, rev_id)
1746
assert getattr(ie, 'revision', None) is not None
1748
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1749
# TODO: convert this logic, which is ~= snapshot to
1750
# a call to:. This needs the path figured out. rather than a work_tree
1751
# a v4 revision_tree can be given, or something that looks enough like
1752
# one to give the file content to the entry if it needs it.
1753
# and we need something that looks like a weave store for snapshot to
1755
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1756
if len(previous_revisions) == 1:
1757
previous_ie = previous_revisions.values()[0]
1758
if ie._unchanged(previous_ie):
1759
ie.revision = previous_ie.revision
1762
text = self.branch.repository.text_store.get(ie.text_id)
1763
file_lines = text.readlines()
1764
assert sha_strings(file_lines) == ie.text_sha1
1765
assert sum(map(len, file_lines)) == ie.text_size
1766
w.add_lines(rev_id, previous_revisions, file_lines)
1767
self.text_count += 1
1769
w.add_lines(rev_id, previous_revisions, [])
1770
ie.revision = rev_id
1772
def _make_order(self):
1773
"""Return a suitable order for importing revisions.
1775
The order must be such that an revision is imported after all
1776
its (present) parents.
1778
todo = set(self.revisions.keys())
1779
done = self.absent_revisions.copy()
1782
# scan through looking for a revision whose parents
1784
for rev_id in sorted(list(todo)):
1785
rev = self.revisions[rev_id]
1786
parent_ids = set(rev.parent_ids)
1787
if parent_ids.issubset(done):
1788
# can take this one now
1789
order.append(rev_id)
1795
class ConvertBzrDir5To6(Converter):
1796
"""Converts format 5 bzr dirs to format 6."""
1798
def convert(self, to_convert, pb):
1799
"""See Converter.convert()."""
1800
self.bzrdir = to_convert
1802
self.pb.note('starting upgrade from format 5 to 6')
1803
self._convert_to_prefixed()
1804
return BzrDir.open(self.bzrdir.root_transport.base)
1806
def _convert_to_prefixed(self):
1807
from bzrlib.store import TransportStore
1808
self.bzrdir.transport.delete('branch-format')
1809
for store_name in ["weaves", "revision-store"]:
1810
self.pb.note("adding prefixes to %s" % store_name)
1811
store_transport = self.bzrdir.transport.clone(store_name)
1812
store = TransportStore(store_transport, prefixed=True)
1813
for urlfilename in store_transport.list_dir('.'):
1814
filename = urlutils.unescape(urlfilename)
1815
if (filename.endswith(".weave") or
1816
filename.endswith(".gz") or
1817
filename.endswith(".sig")):
1818
file_id = os.path.splitext(filename)[0]
1821
prefix_dir = store.hash_prefix(file_id)
1822
# FIXME keep track of the dirs made RBC 20060121
1824
store_transport.move(filename, prefix_dir + '/' + filename)
1825
except errors.NoSuchFile: # catches missing dirs strangely enough
1826
store_transport.mkdir(prefix_dir)
1827
store_transport.move(filename, prefix_dir + '/' + filename)
1828
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1831
class ConvertBzrDir6ToMeta(Converter):
1832
"""Converts format 6 bzr dirs to metadirs."""
1834
def convert(self, to_convert, pb):
1835
"""See Converter.convert()."""
1836
from bzrlib.repofmt.weaverepo import RepositoryFormat7_instance
1837
from bzrlib.branch import BzrBranchFormat5
1838
self.bzrdir = to_convert
1841
self.total = 20 # the steps we know about
1842
self.garbage_inventories = []
1844
self.pb.note('starting upgrade from format 6 to metadir')
1845
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1846
# its faster to move specific files around than to open and use the apis...
1847
# first off, nuke ancestry.weave, it was never used.
1849
self.step('Removing ancestry.weave')
1850
self.bzrdir.transport.delete('ancestry.weave')
1851
except errors.NoSuchFile:
1853
# find out whats there
1854
self.step('Finding branch files')
1855
last_revision = self.bzrdir.open_branch().last_revision()
1856
bzrcontents = self.bzrdir.transport.list_dir('.')
1857
for name in bzrcontents:
1858
if name.startswith('basis-inventory.'):
1859
self.garbage_inventories.append(name)
1860
# create new directories for repository, working tree and branch
1861
self.dir_mode = self.bzrdir._control_files._dir_mode
1862
self.file_mode = self.bzrdir._control_files._file_mode
1863
repository_names = [('inventory.weave', True),
1864
('revision-store', True),
1866
self.step('Upgrading repository ')
1867
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1868
self.make_lock('repository')
1869
# we hard code the formats here because we are converting into
1870
# the meta format. The meta format upgrader can take this to a
1871
# future format within each component.
1872
self.put_format('repository', RepositoryFormat7_instance)
1873
for entry in repository_names:
1874
self.move_entry('repository', entry)
1876
self.step('Upgrading branch ')
1877
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1878
self.make_lock('branch')
1879
self.put_format('branch', BzrBranchFormat5())
1880
branch_files = [('revision-history', True),
1881
('branch-name', True),
1883
for entry in branch_files:
1884
self.move_entry('branch', entry)
1886
checkout_files = [('pending-merges', True),
1887
('inventory', True),
1888
('stat-cache', False)]
1889
# If a mandatory checkout file is not present, the branch does not have
1890
# a functional checkout. Do not create a checkout in the converted
1892
for name, mandatory in checkout_files:
1893
if mandatory and name not in bzrcontents:
1894
has_checkout = False
1898
if not has_checkout:
1899
self.pb.note('No working tree.')
1900
# If some checkout files are there, we may as well get rid of them.
1901
for name, mandatory in checkout_files:
1902
if name in bzrcontents:
1903
self.bzrdir.transport.delete(name)
1905
from bzrlib.workingtree import WorkingTreeFormat3
1906
self.step('Upgrading working tree')
1907
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1908
self.make_lock('checkout')
1910
'checkout', WorkingTreeFormat3())
1911
self.bzrdir.transport.delete_multi(
1912
self.garbage_inventories, self.pb)
1913
for entry in checkout_files:
1914
self.move_entry('checkout', entry)
1915
if last_revision is not None:
1916
self.bzrdir._control_files.put_utf8(
1917
'checkout/last-revision', last_revision)
1918
self.bzrdir._control_files.put_utf8(
1919
'branch-format', BzrDirMetaFormat1().get_format_string())
1920
return BzrDir.open(self.bzrdir.root_transport.base)
1922
def make_lock(self, name):
1923
"""Make a lock for the new control dir name."""
1924
self.step('Make %s lock' % name)
1925
ld = lockdir.LockDir(self.bzrdir.transport,
1927
file_modebits=self.file_mode,
1928
dir_modebits=self.dir_mode)
1931
def move_entry(self, new_dir, entry):
1932
"""Move then entry name into new_dir."""
1934
mandatory = entry[1]
1935
self.step('Moving %s' % name)
1937
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1938
except errors.NoSuchFile:
1942
def put_format(self, dirname, format):
1943
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1946
class ConvertMetaToMeta(Converter):
1947
"""Converts the components of metadirs."""
1949
def __init__(self, target_format):
1950
"""Create a metadir to metadir converter.
1952
:param target_format: The final metadir format that is desired.
1954
self.target_format = target_format
1956
def convert(self, to_convert, pb):
1957
"""See Converter.convert()."""
1958
self.bzrdir = to_convert
1962
self.step('checking repository format')
1964
repo = self.bzrdir.open_repository()
1965
except errors.NoRepositoryPresent:
1968
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1969
from bzrlib.repository import CopyConverter
1970
self.pb.note('starting repository conversion')
1971
converter = CopyConverter(self.target_format.repository_format)
1972
converter.convert(repo, pb)
1976
class BzrDirFormatInfo(object):
1978
def __init__(self, native, deprecated):
1979
self.deprecated = deprecated
1980
self.native = native
1983
class BzrDirFormatRegistry(registry.Registry):
1984
"""Registry of user-selectable BzrDir subformats.
1986
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
1987
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
1990
def register_metadir(self, key, repo, help, native=True, deprecated=False,
1991
repo_module='bzrlib.repository'):
1992
"""Register a metadir subformat.
1994
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
1995
by the Repository format.
1997
:param repo: The repository format class name as a string.
1999
:param repo_module: The module from which the repository class
2000
should be lazily loaded. By default this is bzrlib.repository.
2002
# This should be expanded to support setting WorkingTree and Branch
2003
# formats, once BzrDirMetaFormat1 supports that.
2005
mod = __import__(repo_module, globals(), locals(), [repo])
2007
repo_format_class = getattr(mod, repo)
2008
except AttributeError:
2009
raise AttributeError('no repository format %r in module %r'
2011
bd = BzrDirMetaFormat1()
2012
bd.repository_format = repo_format_class()
2014
self.register(key, helper, help, native, deprecated)
2016
def register(self, key, factory, help, native=True, deprecated=False):
2017
"""Register a BzrDirFormat factory.
2019
The factory must be a callable that takes one parameter: the key.
2020
It must produce an instance of the BzrDirFormat when called.
2022
This function mainly exists to prevent the info object from being
2025
registry.Registry.register(self, key, factory, help,
2026
BzrDirFormatInfo(native, deprecated))
2028
def register_lazy(self, key, module_name, member_name, help, native=True,
2030
registry.Registry.register_lazy(self, key, module_name, member_name,
2031
help, BzrDirFormatInfo(native, deprecated))
2033
def set_default(self, key):
2034
"""Set the 'default' key to be a clone of the supplied key.
2036
This method must be called once and only once.
2038
registry.Registry.register(self, 'default', self.get(key),
2039
self.get_help(key), info=self.get_info(key))
2041
def set_default_repository(self, key):
2042
"""Set the FormatRegistry default and Repository default.
2044
This is a transitional method while Repository.set_default_format
2047
if 'default' in self:
2048
self.remove('default')
2049
self.set_default(key)
2050
format = self.get('default')()
2051
assert isinstance(format, BzrDirMetaFormat1)
2052
from bzrlib import repository
2053
repository.RepositoryFormat._set_default_format(
2054
format.repository_format)
2056
def make_bzrdir(self, key):
2057
return self.get(key)()
2059
def help_topic(self, topic):
2060
output = textwrap.dedent("""\
2061
Bazaar directory formats
2062
------------------------
2064
These formats can be used for creating branches, working trees, and
2068
default_help = self.get_help('default')
2070
for key in self.keys():
2071
if key == 'default':
2073
help = self.get_help(key)
2074
if help == default_help:
2075
default_realkey = key
2077
help_pairs.append((key, help))
2079
def wrapped(key, help, info):
2081
help = '(native) ' + help
2082
return ' %s:\n%s\n\n' % (key,
2083
textwrap.fill(help, initial_indent=' ',
2084
subsequent_indent=' '))
2085
output += wrapped('%s/default' % default_realkey, default_help,
2086
self.get_info('default'))
2087
deprecated_pairs = []
2088
for key, help in help_pairs:
2089
info = self.get_info(key)
2091
deprecated_pairs.append((key, help))
2093
output += wrapped(key, help, info)
2094
if len(deprecated_pairs) > 0:
2095
output += "Deprecated formats\n------------------\n\n"
2096
for key, help in deprecated_pairs:
2097
info = self.get_info(key)
2098
output += wrapped(key, help, info)
2103
format_registry = BzrDirFormatRegistry()
2104
format_registry.register('weave', BzrDirFormat6,
2105
'Pre-0.8 format. Slower than knit and does not'
2106
' support checkouts or shared repositories.',
2108
format_registry.register_metadir('knit',
2109
'RepositoryFormatKnit1',
2110
'Format using knits. Recommended.',
2111
repo_module='bzrlib.repofmt.knitrepo')
2112
format_registry.set_default('knit')
2113
format_registry.register_metadir('metaweave', 'RepositoryFormat7',
2114
'Transitional format in 0.8. Slower than knit.',
2116
repo_module='bzrlib.repofmt.weaverepo')
2117
format_registry.register_metadir('experimental-knit2',
2118
'RepositoryFormatKnit2',
2119
'Experimental successor to knit. Use at your own risk.',
2120
repo_module='bzrlib.repofmt.knitrepo')