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
return self._format.branch_format.initialize(self)
930
def create_repository(self, shared=False):
931
"""See BzrDir.create_repository."""
932
return self._format.repository_format.initialize(self, shared)
934
def create_workingtree(self, revision_id=None):
935
"""See BzrDir.create_workingtree."""
936
from bzrlib.workingtree import WorkingTreeFormat
937
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
939
def destroy_workingtree(self):
940
"""See BzrDir.destroy_workingtree."""
941
wt = self.open_workingtree()
942
repository = wt.branch.repository
943
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
944
wt.revert([], old_tree=empty)
945
self.destroy_workingtree_metadata()
947
def destroy_workingtree_metadata(self):
948
self.transport.delete_tree('checkout')
950
def _get_mkdir_mode(self):
951
"""Figure out the mode to use when creating a bzrdir subdir."""
952
temp_control = lockable_files.LockableFiles(self.transport, '',
953
lockable_files.TransportLock)
954
return temp_control._dir_mode
956
def get_branch_transport(self, branch_format):
957
"""See BzrDir.get_branch_transport()."""
958
if branch_format is None:
959
return self.transport.clone('branch')
961
branch_format.get_format_string()
962
except NotImplementedError:
963
raise errors.IncompatibleFormat(branch_format, self._format)
965
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
966
except errors.FileExists:
968
return self.transport.clone('branch')
970
def get_repository_transport(self, repository_format):
971
"""See BzrDir.get_repository_transport()."""
972
if repository_format is None:
973
return self.transport.clone('repository')
975
repository_format.get_format_string()
976
except NotImplementedError:
977
raise errors.IncompatibleFormat(repository_format, self._format)
979
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
980
except errors.FileExists:
982
return self.transport.clone('repository')
984
def get_workingtree_transport(self, workingtree_format):
985
"""See BzrDir.get_workingtree_transport()."""
986
if workingtree_format is None:
987
return self.transport.clone('checkout')
989
workingtree_format.get_format_string()
990
except NotImplementedError:
991
raise errors.IncompatibleFormat(workingtree_format, self._format)
993
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
994
except errors.FileExists:
996
return self.transport.clone('checkout')
998
def needs_format_conversion(self, format=None):
999
"""See BzrDir.needs_format_conversion()."""
1001
format = BzrDirFormat.get_default_format()
1002
if not isinstance(self._format, format.__class__):
1003
# it is not a meta dir format, conversion is needed.
1005
# we might want to push this down to the repository?
1007
if not isinstance(self.open_repository()._format,
1008
format.repository_format.__class__):
1009
# the repository needs an upgrade.
1011
except errors.NoRepositoryPresent:
1014
if not isinstance(self.open_branch()._format,
1015
format.branch_format.__class__):
1016
# the repository needs an upgrade.
1018
except errors.NotBranchError:
1020
# currently there are no other possible conversions for meta1 formats.
1023
def open_branch(self, unsupported=False):
1024
"""See BzrDir.open_branch."""
1025
from bzrlib.branch import BranchFormat
1026
format = BranchFormat.find_format(self)
1027
self._check_supported(format, unsupported)
1028
return format.open(self, _found=True)
1030
def open_repository(self, unsupported=False):
1031
"""See BzrDir.open_repository."""
1032
from bzrlib.repository import RepositoryFormat
1033
format = RepositoryFormat.find_format(self)
1034
self._check_supported(format, unsupported)
1035
return format.open(self, _found=True)
1037
def open_workingtree(self, unsupported=False):
1038
"""See BzrDir.open_workingtree."""
1039
from bzrlib.workingtree import WorkingTreeFormat
1040
format = WorkingTreeFormat.find_format(self)
1041
self._check_supported(format, unsupported)
1042
return format.open(self, _found=True)
1045
class BzrDirFormat(object):
1046
"""An encapsulation of the initialization and open routines for a format.
1048
Formats provide three things:
1049
* An initialization routine,
1053
Formats are placed in an dict by their format string for reference
1054
during bzrdir opening. These should be subclasses of BzrDirFormat
1057
Once a format is deprecated, just deprecate the initialize and open
1058
methods on the format class. Do not deprecate the object, as the
1059
object will be created every system load.
1062
_default_format = None
1063
"""The default format used for new .bzr dirs."""
1066
"""The known formats."""
1068
_control_formats = []
1069
"""The registered control formats - .bzr, ....
1071
This is a list of BzrDirFormat objects.
1074
_lock_file_name = 'branch-lock'
1076
# _lock_class must be set in subclasses to the lock type, typ.
1077
# TransportLock or LockDir
1080
def find_format(klass, transport):
1081
"""Return the format present at transport."""
1082
for format in klass._control_formats:
1084
return format.probe_transport(transport)
1085
except errors.NotBranchError:
1086
# this format does not find a control dir here.
1088
raise errors.NotBranchError(path=transport.base)
1091
def probe_transport(klass, transport):
1092
"""Return the .bzrdir style transport present at URL."""
1094
format_string = transport.get(".bzr/branch-format").read()
1095
except errors.NoSuchFile:
1096
raise errors.NotBranchError(path=transport.base)
1099
return klass._formats[format_string]
1101
raise errors.UnknownFormatError(format=format_string)
1104
def get_default_format(klass):
1105
"""Return the current default format."""
1106
return klass._default_format
1108
def get_format_string(self):
1109
"""Return the ASCII format string that identifies this format."""
1110
raise NotImplementedError(self.get_format_string)
1112
def get_format_description(self):
1113
"""Return the short description for this format."""
1114
raise NotImplementedError(self.get_format_description)
1116
def get_converter(self, format=None):
1117
"""Return the converter to use to convert bzrdirs needing converts.
1119
This returns a bzrlib.bzrdir.Converter object.
1121
This should return the best upgrader to step this format towards the
1122
current default format. In the case of plugins we can/should provide
1123
some means for them to extend the range of returnable converters.
1125
:param format: Optional format to override the default format of the
1128
raise NotImplementedError(self.get_converter)
1130
def initialize(self, url):
1131
"""Create a bzr control dir at this url and return an opened copy.
1133
Subclasses should typically override initialize_on_transport
1134
instead of this method.
1136
return self.initialize_on_transport(get_transport(url))
1138
def initialize_on_transport(self, transport):
1139
"""Initialize a new bzrdir in the base directory of a Transport."""
1140
# Since we don't have a .bzr directory, inherit the
1141
# mode from the root directory
1142
temp_control = lockable_files.LockableFiles(transport,
1143
'', lockable_files.TransportLock)
1144
temp_control._transport.mkdir('.bzr',
1145
# FIXME: RBC 20060121 don't peek under
1147
mode=temp_control._dir_mode)
1148
file_mode = temp_control._file_mode
1150
mutter('created control directory in ' + transport.base)
1151
control = transport.clone('.bzr')
1152
utf8_files = [('README',
1153
"This is a Bazaar-NG control directory.\n"
1154
"Do not change any files in this directory.\n"),
1155
('branch-format', self.get_format_string()),
1157
# NB: no need to escape relative paths that are url safe.
1158
control_files = lockable_files.LockableFiles(control,
1159
self._lock_file_name, self._lock_class)
1160
control_files.create_lock()
1161
control_files.lock_write()
1163
for file, content in utf8_files:
1164
control_files.put_utf8(file, content)
1166
control_files.unlock()
1167
return self.open(transport, _found=True)
1169
def is_supported(self):
1170
"""Is this format supported?
1172
Supported formats must be initializable and openable.
1173
Unsupported formats may not support initialization or committing or
1174
some other features depending on the reason for not being supported.
1178
def same_model(self, target_format):
1179
return (self.repository_format.rich_root_data ==
1180
target_format.rich_root_data)
1183
def known_formats(klass):
1184
"""Return all the known formats.
1186
Concrete formats should override _known_formats.
1188
# There is double indirection here to make sure that control
1189
# formats used by more than one dir format will only be probed
1190
# once. This can otherwise be quite expensive for remote connections.
1192
for format in klass._control_formats:
1193
result.update(format._known_formats())
1197
def _known_formats(klass):
1198
"""Return the known format instances for this control format."""
1199
return set(klass._formats.values())
1201
def open(self, transport, _found=False):
1202
"""Return an instance of this format for the dir transport points at.
1204
_found is a private parameter, do not use it.
1207
found_format = BzrDirFormat.find_format(transport)
1208
if not isinstance(found_format, self.__class__):
1209
raise AssertionError("%s was asked to open %s, but it seems to need "
1211
% (self, transport, found_format))
1212
return self._open(transport)
1214
def _open(self, transport):
1215
"""Template method helper for opening BzrDirectories.
1217
This performs the actual open and any additional logic or parameter
1220
raise NotImplementedError(self._open)
1223
def register_format(klass, format):
1224
klass._formats[format.get_format_string()] = format
1227
def register_control_format(klass, format):
1228
"""Register a format that does not use '.bzrdir' for its control dir.
1230
TODO: This should be pulled up into a 'ControlDirFormat' base class
1231
which BzrDirFormat can inherit from, and renamed to register_format
1232
there. It has been done without that for now for simplicity of
1235
klass._control_formats.append(format)
1238
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1239
def set_default_format(klass, format):
1240
klass._set_default_format(format)
1243
def _set_default_format(klass, format):
1244
"""Set default format (for testing behavior of defaults only)"""
1245
klass._default_format = format
1248
return self.get_format_string()[:-1]
1251
def unregister_format(klass, format):
1252
assert klass._formats[format.get_format_string()] is format
1253
del klass._formats[format.get_format_string()]
1256
def unregister_control_format(klass, format):
1257
klass._control_formats.remove(format)
1260
# register BzrDirFormat as a control format
1261
BzrDirFormat.register_control_format(BzrDirFormat)
1264
class BzrDirFormat4(BzrDirFormat):
1265
"""Bzr dir format 4.
1267
This format is a combined format for working tree, branch and repository.
1269
- Format 1 working trees [always]
1270
- Format 4 branches [always]
1271
- Format 4 repositories [always]
1273
This format is deprecated: it indexes texts using a text it which is
1274
removed in format 5; write support for this format has been removed.
1277
_lock_class = lockable_files.TransportLock
1279
def get_format_string(self):
1280
"""See BzrDirFormat.get_format_string()."""
1281
return "Bazaar-NG branch, format 0.0.4\n"
1283
def get_format_description(self):
1284
"""See BzrDirFormat.get_format_description()."""
1285
return "All-in-one format 4"
1287
def get_converter(self, format=None):
1288
"""See BzrDirFormat.get_converter()."""
1289
# there is one and only one upgrade path here.
1290
return ConvertBzrDir4To5()
1292
def initialize_on_transport(self, transport):
1293
"""Format 4 branches cannot be created."""
1294
raise errors.UninitializableFormat(self)
1296
def is_supported(self):
1297
"""Format 4 is not supported.
1299
It is not supported because the model changed from 4 to 5 and the
1300
conversion logic is expensive - so doing it on the fly was not
1305
def _open(self, transport):
1306
"""See BzrDirFormat._open."""
1307
return BzrDir4(transport, self)
1309
def __return_repository_format(self):
1310
"""Circular import protection."""
1311
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1312
return RepositoryFormat4()
1313
repository_format = property(__return_repository_format)
1316
class BzrDirFormat5(BzrDirFormat):
1317
"""Bzr control format 5.
1319
This format is a combined format for working tree, branch and repository.
1321
- Format 2 working trees [always]
1322
- Format 4 branches [always]
1323
- Format 5 repositories [always]
1324
Unhashed stores in the repository.
1327
_lock_class = lockable_files.TransportLock
1329
def get_format_string(self):
1330
"""See BzrDirFormat.get_format_string()."""
1331
return "Bazaar-NG branch, format 5\n"
1333
def get_format_description(self):
1334
"""See BzrDirFormat.get_format_description()."""
1335
return "All-in-one format 5"
1337
def get_converter(self, format=None):
1338
"""See BzrDirFormat.get_converter()."""
1339
# there is one and only one upgrade path here.
1340
return ConvertBzrDir5To6()
1342
def _initialize_for_clone(self, url):
1343
return self.initialize_on_transport(get_transport(url), _cloning=True)
1345
def initialize_on_transport(self, transport, _cloning=False):
1346
"""Format 5 dirs always have working tree, branch and repository.
1348
Except when they are being cloned.
1350
from bzrlib.branch import BzrBranchFormat4
1351
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1352
from bzrlib.workingtree import WorkingTreeFormat2
1353
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1354
RepositoryFormat5().initialize(result, _internal=True)
1356
branch = BzrBranchFormat4().initialize(result)
1358
WorkingTreeFormat2().initialize(result)
1359
except errors.NotLocalUrl:
1360
# Even though we can't access the working tree, we need to
1361
# create its control files.
1362
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1365
def _open(self, transport):
1366
"""See BzrDirFormat._open."""
1367
return BzrDir5(transport, self)
1369
def __return_repository_format(self):
1370
"""Circular import protection."""
1371
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1372
return RepositoryFormat5()
1373
repository_format = property(__return_repository_format)
1376
class BzrDirFormat6(BzrDirFormat):
1377
"""Bzr control format 6.
1379
This format is a combined format for working tree, branch and repository.
1381
- Format 2 working trees [always]
1382
- Format 4 branches [always]
1383
- Format 6 repositories [always]
1386
_lock_class = lockable_files.TransportLock
1388
def get_format_string(self):
1389
"""See BzrDirFormat.get_format_string()."""
1390
return "Bazaar-NG branch, format 6\n"
1392
def get_format_description(self):
1393
"""See BzrDirFormat.get_format_description()."""
1394
return "All-in-one format 6"
1396
def get_converter(self, format=None):
1397
"""See BzrDirFormat.get_converter()."""
1398
# there is one and only one upgrade path here.
1399
return ConvertBzrDir6ToMeta()
1401
def _initialize_for_clone(self, url):
1402
return self.initialize_on_transport(get_transport(url), _cloning=True)
1404
def initialize_on_transport(self, transport, _cloning=False):
1405
"""Format 6 dirs always have working tree, branch and repository.
1407
Except when they are being cloned.
1409
from bzrlib.branch import BzrBranchFormat4
1410
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1411
from bzrlib.workingtree import WorkingTreeFormat2
1412
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1413
RepositoryFormat6().initialize(result, _internal=True)
1415
branch = BzrBranchFormat4().initialize(result)
1417
WorkingTreeFormat2().initialize(result)
1418
except errors.NotLocalUrl:
1419
# Even though we can't access the working tree, we need to
1420
# create its control files.
1421
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1424
def _open(self, transport):
1425
"""See BzrDirFormat._open."""
1426
return BzrDir6(transport, self)
1428
def __return_repository_format(self):
1429
"""Circular import protection."""
1430
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1431
return RepositoryFormat6()
1432
repository_format = property(__return_repository_format)
1435
class BzrDirMetaFormat1(BzrDirFormat):
1436
"""Bzr meta control format 1
1438
This is the first format with split out working tree, branch and repository
1441
- Format 3 working trees [optional]
1442
- Format 5 branches [optional]
1443
- Format 7 repositories [optional]
1446
_lock_class = lockdir.LockDir
1449
self._branch_format = None
1451
def _get_branch_format(self):
1452
if self._branch_format is None:
1453
from bzrlib.branch import BranchFormat
1454
self._branch_format = BranchFormat.get_default_format()
1455
return self._branch_format
1457
def _set_branch_format(self, format):
1458
self._branch_format = format
1460
branch_format = property(_get_branch_format, _set_branch_format)
1462
def get_converter(self, format=None):
1463
"""See BzrDirFormat.get_converter()."""
1465
format = BzrDirFormat.get_default_format()
1466
if not isinstance(self, format.__class__):
1467
# converting away from metadir is not implemented
1468
raise NotImplementedError(self.get_converter)
1469
return ConvertMetaToMeta(format)
1471
def get_format_string(self):
1472
"""See BzrDirFormat.get_format_string()."""
1473
return "Bazaar-NG meta directory, format 1\n"
1475
def get_format_description(self):
1476
"""See BzrDirFormat.get_format_description()."""
1477
return "Meta directory format 1"
1479
def _open(self, transport):
1480
"""See BzrDirFormat._open."""
1481
from bzrlib.repository import RepositoryFormat
1482
from bzrlib.branch import BranchFormat
1483
return BzrDirMeta1(transport, self)
1485
def __return_repository_format(self):
1486
"""Circular import protection."""
1487
if getattr(self, '_repository_format', None):
1488
return self._repository_format
1489
from bzrlib.repository import RepositoryFormat
1490
return RepositoryFormat.get_default_format()
1492
def __set_repository_format(self, value):
1493
"""Allow changint the repository format for metadir formats."""
1494
self._repository_format = value
1496
repository_format = property(__return_repository_format, __set_repository_format)
1499
BzrDirFormat.register_format(BzrDirFormat4())
1500
BzrDirFormat.register_format(BzrDirFormat5())
1501
BzrDirFormat.register_format(BzrDirFormat6())
1502
__default_format = BzrDirMetaFormat1()
1503
BzrDirFormat.register_format(__default_format)
1504
BzrDirFormat._default_format = __default_format
1507
class BzrDirTestProviderAdapter(object):
1508
"""A tool to generate a suite testing multiple bzrdir formats at once.
1510
This is done by copying the test once for each transport and injecting
1511
the transport_server, transport_readonly_server, and bzrdir_format
1512
classes into each copy. Each copy is also given a new id() to make it
1516
def __init__(self, transport_server, transport_readonly_server, formats):
1517
self._transport_server = transport_server
1518
self._transport_readonly_server = transport_readonly_server
1519
self._formats = formats
1521
def adapt(self, test):
1522
result = unittest.TestSuite()
1523
for format in self._formats:
1524
new_test = deepcopy(test)
1525
new_test.transport_server = self._transport_server
1526
new_test.transport_readonly_server = self._transport_readonly_server
1527
new_test.bzrdir_format = format
1528
def make_new_test_id():
1529
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1530
return lambda: new_id
1531
new_test.id = make_new_test_id()
1532
result.addTest(new_test)
1536
class Converter(object):
1537
"""Converts a disk format object from one format to another."""
1539
def convert(self, to_convert, pb):
1540
"""Perform the conversion of to_convert, giving feedback via pb.
1542
:param to_convert: The disk object to convert.
1543
:param pb: a progress bar to use for progress information.
1546
def step(self, message):
1547
"""Update the pb by a step."""
1549
self.pb.update(message, self.count, self.total)
1552
class ConvertBzrDir4To5(Converter):
1553
"""Converts format 4 bzr dirs to format 5."""
1556
super(ConvertBzrDir4To5, self).__init__()
1557
self.converted_revs = set()
1558
self.absent_revisions = set()
1562
def convert(self, to_convert, pb):
1563
"""See Converter.convert()."""
1564
self.bzrdir = to_convert
1566
self.pb.note('starting upgrade from format 4 to 5')
1567
if isinstance(self.bzrdir.transport, LocalTransport):
1568
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1569
self._convert_to_weaves()
1570
return BzrDir.open(self.bzrdir.root_transport.base)
1572
def _convert_to_weaves(self):
1573
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1576
stat = self.bzrdir.transport.stat('weaves')
1577
if not S_ISDIR(stat.st_mode):
1578
self.bzrdir.transport.delete('weaves')
1579
self.bzrdir.transport.mkdir('weaves')
1580
except errors.NoSuchFile:
1581
self.bzrdir.transport.mkdir('weaves')
1582
# deliberately not a WeaveFile as we want to build it up slowly.
1583
self.inv_weave = Weave('inventory')
1584
# holds in-memory weaves for all files
1585
self.text_weaves = {}
1586
self.bzrdir.transport.delete('branch-format')
1587
self.branch = self.bzrdir.open_branch()
1588
self._convert_working_inv()
1589
rev_history = self.branch.revision_history()
1590
# to_read is a stack holding the revisions we still need to process;
1591
# appending to it adds new highest-priority revisions
1592
self.known_revisions = set(rev_history)
1593
self.to_read = rev_history[-1:]
1595
rev_id = self.to_read.pop()
1596
if (rev_id not in self.revisions
1597
and rev_id not in self.absent_revisions):
1598
self._load_one_rev(rev_id)
1600
to_import = self._make_order()
1601
for i, rev_id in enumerate(to_import):
1602
self.pb.update('converting revision', i, len(to_import))
1603
self._convert_one_rev(rev_id)
1605
self._write_all_weaves()
1606
self._write_all_revs()
1607
self.pb.note('upgraded to weaves:')
1608
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1609
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1610
self.pb.note(' %6d texts', self.text_count)
1611
self._cleanup_spare_files_after_format4()
1612
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1614
def _cleanup_spare_files_after_format4(self):
1615
# FIXME working tree upgrade foo.
1616
for n in 'merged-patches', 'pending-merged-patches':
1618
## assert os.path.getsize(p) == 0
1619
self.bzrdir.transport.delete(n)
1620
except errors.NoSuchFile:
1622
self.bzrdir.transport.delete_tree('inventory-store')
1623
self.bzrdir.transport.delete_tree('text-store')
1625
def _convert_working_inv(self):
1626
inv = xml4.serializer_v4.read_inventory(
1627
self.branch.control_files.get('inventory'))
1628
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1629
# FIXME inventory is a working tree change.
1630
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1632
def _write_all_weaves(self):
1633
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1634
weave_transport = self.bzrdir.transport.clone('weaves')
1635
weaves = WeaveStore(weave_transport, prefixed=False)
1636
transaction = WriteTransaction()
1640
for file_id, file_weave in self.text_weaves.items():
1641
self.pb.update('writing weave', i, len(self.text_weaves))
1642
weaves._put_weave(file_id, file_weave, transaction)
1644
self.pb.update('inventory', 0, 1)
1645
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1646
self.pb.update('inventory', 1, 1)
1650
def _write_all_revs(self):
1651
"""Write all revisions out in new form."""
1652
self.bzrdir.transport.delete_tree('revision-store')
1653
self.bzrdir.transport.mkdir('revision-store')
1654
revision_transport = self.bzrdir.transport.clone('revision-store')
1656
_revision_store = TextRevisionStore(TextStore(revision_transport,
1660
transaction = WriteTransaction()
1661
for i, rev_id in enumerate(self.converted_revs):
1662
self.pb.update('write revision', i, len(self.converted_revs))
1663
_revision_store.add_revision(self.revisions[rev_id], transaction)
1667
def _load_one_rev(self, rev_id):
1668
"""Load a revision object into memory.
1670
Any parents not either loaded or abandoned get queued to be
1672
self.pb.update('loading revision',
1673
len(self.revisions),
1674
len(self.known_revisions))
1675
if not self.branch.repository.has_revision(rev_id):
1677
self.pb.note('revision {%s} not present in branch; '
1678
'will be converted as a ghost',
1680
self.absent_revisions.add(rev_id)
1682
rev = self.branch.repository._revision_store.get_revision(rev_id,
1683
self.branch.repository.get_transaction())
1684
for parent_id in rev.parent_ids:
1685
self.known_revisions.add(parent_id)
1686
self.to_read.append(parent_id)
1687
self.revisions[rev_id] = rev
1689
def _load_old_inventory(self, rev_id):
1690
assert rev_id not in self.converted_revs
1691
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1692
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1693
inv.revision_id = rev_id
1694
rev = self.revisions[rev_id]
1695
if rev.inventory_sha1:
1696
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1697
'inventory sha mismatch for {%s}' % rev_id
1700
def _load_updated_inventory(self, rev_id):
1701
assert rev_id in self.converted_revs
1702
inv_xml = self.inv_weave.get_text(rev_id)
1703
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1706
def _convert_one_rev(self, rev_id):
1707
"""Convert revision and all referenced objects to new format."""
1708
rev = self.revisions[rev_id]
1709
inv = self._load_old_inventory(rev_id)
1710
present_parents = [p for p in rev.parent_ids
1711
if p not in self.absent_revisions]
1712
self._convert_revision_contents(rev, inv, present_parents)
1713
self._store_new_weave(rev, inv, present_parents)
1714
self.converted_revs.add(rev_id)
1716
def _store_new_weave(self, rev, inv, present_parents):
1717
# the XML is now updated with text versions
1719
entries = inv.iter_entries()
1721
for path, ie in entries:
1722
assert getattr(ie, 'revision', None) is not None, \
1723
'no revision on {%s} in {%s}' % \
1724
(file_id, rev.revision_id)
1725
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1726
new_inv_sha1 = sha_string(new_inv_xml)
1727
self.inv_weave.add_lines(rev.revision_id,
1729
new_inv_xml.splitlines(True))
1730
rev.inventory_sha1 = new_inv_sha1
1732
def _convert_revision_contents(self, rev, inv, present_parents):
1733
"""Convert all the files within a revision.
1735
Also upgrade the inventory to refer to the text revision ids."""
1736
rev_id = rev.revision_id
1737
mutter('converting texts of revision {%s}',
1739
parent_invs = map(self._load_updated_inventory, present_parents)
1740
entries = inv.iter_entries()
1742
for path, ie in entries:
1743
self._convert_file_version(rev, ie, parent_invs)
1745
def _convert_file_version(self, rev, ie, parent_invs):
1746
"""Convert one version of one file.
1748
The file needs to be added into the weave if it is a merge
1749
of >=2 parents or if it's changed from its parent.
1751
file_id = ie.file_id
1752
rev_id = rev.revision_id
1753
w = self.text_weaves.get(file_id)
1756
self.text_weaves[file_id] = w
1757
text_changed = False
1758
previous_entries = ie.find_previous_heads(parent_invs,
1762
for old_revision in previous_entries:
1763
# if this fails, its a ghost ?
1764
assert old_revision in self.converted_revs, \
1765
"Revision {%s} not in converted_revs" % old_revision
1766
self.snapshot_ie(previous_entries, ie, w, rev_id)
1768
assert getattr(ie, 'revision', None) is not None
1770
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1771
# TODO: convert this logic, which is ~= snapshot to
1772
# a call to:. This needs the path figured out. rather than a work_tree
1773
# a v4 revision_tree can be given, or something that looks enough like
1774
# one to give the file content to the entry if it needs it.
1775
# and we need something that looks like a weave store for snapshot to
1777
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1778
if len(previous_revisions) == 1:
1779
previous_ie = previous_revisions.values()[0]
1780
if ie._unchanged(previous_ie):
1781
ie.revision = previous_ie.revision
1784
text = self.branch.repository.text_store.get(ie.text_id)
1785
file_lines = text.readlines()
1786
assert sha_strings(file_lines) == ie.text_sha1
1787
assert sum(map(len, file_lines)) == ie.text_size
1788
w.add_lines(rev_id, previous_revisions, file_lines)
1789
self.text_count += 1
1791
w.add_lines(rev_id, previous_revisions, [])
1792
ie.revision = rev_id
1794
def _make_order(self):
1795
"""Return a suitable order for importing revisions.
1797
The order must be such that an revision is imported after all
1798
its (present) parents.
1800
todo = set(self.revisions.keys())
1801
done = self.absent_revisions.copy()
1804
# scan through looking for a revision whose parents
1806
for rev_id in sorted(list(todo)):
1807
rev = self.revisions[rev_id]
1808
parent_ids = set(rev.parent_ids)
1809
if parent_ids.issubset(done):
1810
# can take this one now
1811
order.append(rev_id)
1817
class ConvertBzrDir5To6(Converter):
1818
"""Converts format 5 bzr dirs to format 6."""
1820
def convert(self, to_convert, pb):
1821
"""See Converter.convert()."""
1822
self.bzrdir = to_convert
1824
self.pb.note('starting upgrade from format 5 to 6')
1825
self._convert_to_prefixed()
1826
return BzrDir.open(self.bzrdir.root_transport.base)
1828
def _convert_to_prefixed(self):
1829
from bzrlib.store import TransportStore
1830
self.bzrdir.transport.delete('branch-format')
1831
for store_name in ["weaves", "revision-store"]:
1832
self.pb.note("adding prefixes to %s" % store_name)
1833
store_transport = self.bzrdir.transport.clone(store_name)
1834
store = TransportStore(store_transport, prefixed=True)
1835
for urlfilename in store_transport.list_dir('.'):
1836
filename = urlutils.unescape(urlfilename)
1837
if (filename.endswith(".weave") or
1838
filename.endswith(".gz") or
1839
filename.endswith(".sig")):
1840
file_id = os.path.splitext(filename)[0]
1843
prefix_dir = store.hash_prefix(file_id)
1844
# FIXME keep track of the dirs made RBC 20060121
1846
store_transport.move(filename, prefix_dir + '/' + filename)
1847
except errors.NoSuchFile: # catches missing dirs strangely enough
1848
store_transport.mkdir(prefix_dir)
1849
store_transport.move(filename, prefix_dir + '/' + filename)
1850
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1853
class ConvertBzrDir6ToMeta(Converter):
1854
"""Converts format 6 bzr dirs to metadirs."""
1856
def convert(self, to_convert, pb):
1857
"""See Converter.convert()."""
1858
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1859
from bzrlib.branch import BzrBranchFormat5
1860
self.bzrdir = to_convert
1863
self.total = 20 # the steps we know about
1864
self.garbage_inventories = []
1866
self.pb.note('starting upgrade from format 6 to metadir')
1867
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1868
# its faster to move specific files around than to open and use the apis...
1869
# first off, nuke ancestry.weave, it was never used.
1871
self.step('Removing ancestry.weave')
1872
self.bzrdir.transport.delete('ancestry.weave')
1873
except errors.NoSuchFile:
1875
# find out whats there
1876
self.step('Finding branch files')
1877
last_revision = self.bzrdir.open_branch().last_revision()
1878
bzrcontents = self.bzrdir.transport.list_dir('.')
1879
for name in bzrcontents:
1880
if name.startswith('basis-inventory.'):
1881
self.garbage_inventories.append(name)
1882
# create new directories for repository, working tree and branch
1883
self.dir_mode = self.bzrdir._control_files._dir_mode
1884
self.file_mode = self.bzrdir._control_files._file_mode
1885
repository_names = [('inventory.weave', True),
1886
('revision-store', True),
1888
self.step('Upgrading repository ')
1889
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1890
self.make_lock('repository')
1891
# we hard code the formats here because we are converting into
1892
# the meta format. The meta format upgrader can take this to a
1893
# future format within each component.
1894
self.put_format('repository', RepositoryFormat7())
1895
for entry in repository_names:
1896
self.move_entry('repository', entry)
1898
self.step('Upgrading branch ')
1899
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1900
self.make_lock('branch')
1901
self.put_format('branch', BzrBranchFormat5())
1902
branch_files = [('revision-history', True),
1903
('branch-name', True),
1905
for entry in branch_files:
1906
self.move_entry('branch', entry)
1908
checkout_files = [('pending-merges', True),
1909
('inventory', True),
1910
('stat-cache', False)]
1911
# If a mandatory checkout file is not present, the branch does not have
1912
# a functional checkout. Do not create a checkout in the converted
1914
for name, mandatory in checkout_files:
1915
if mandatory and name not in bzrcontents:
1916
has_checkout = False
1920
if not has_checkout:
1921
self.pb.note('No working tree.')
1922
# If some checkout files are there, we may as well get rid of them.
1923
for name, mandatory in checkout_files:
1924
if name in bzrcontents:
1925
self.bzrdir.transport.delete(name)
1927
from bzrlib.workingtree import WorkingTreeFormat3
1928
self.step('Upgrading working tree')
1929
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1930
self.make_lock('checkout')
1932
'checkout', WorkingTreeFormat3())
1933
self.bzrdir.transport.delete_multi(
1934
self.garbage_inventories, self.pb)
1935
for entry in checkout_files:
1936
self.move_entry('checkout', entry)
1937
if last_revision is not None:
1938
self.bzrdir._control_files.put_utf8(
1939
'checkout/last-revision', last_revision)
1940
self.bzrdir._control_files.put_utf8(
1941
'branch-format', BzrDirMetaFormat1().get_format_string())
1942
return BzrDir.open(self.bzrdir.root_transport.base)
1944
def make_lock(self, name):
1945
"""Make a lock for the new control dir name."""
1946
self.step('Make %s lock' % name)
1947
ld = lockdir.LockDir(self.bzrdir.transport,
1949
file_modebits=self.file_mode,
1950
dir_modebits=self.dir_mode)
1953
def move_entry(self, new_dir, entry):
1954
"""Move then entry name into new_dir."""
1956
mandatory = entry[1]
1957
self.step('Moving %s' % name)
1959
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1960
except errors.NoSuchFile:
1964
def put_format(self, dirname, format):
1965
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1968
class ConvertMetaToMeta(Converter):
1969
"""Converts the components of metadirs."""
1971
def __init__(self, target_format):
1972
"""Create a metadir to metadir converter.
1974
:param target_format: The final metadir format that is desired.
1976
self.target_format = target_format
1978
def convert(self, to_convert, pb):
1979
"""See Converter.convert()."""
1980
self.bzrdir = to_convert
1984
self.step('checking repository format')
1986
repo = self.bzrdir.open_repository()
1987
except errors.NoRepositoryPresent:
1990
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1991
from bzrlib.repository import CopyConverter
1992
self.pb.note('starting repository conversion')
1993
converter = CopyConverter(self.target_format.repository_format)
1994
converter.convert(repo, pb)
1996
branch = self.bzrdir.open_branch()
1997
except errors.NotBranchError:
2000
# Avoid circular imports
2001
from bzrlib import branch as _mod_branch
2002
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2003
self.target_format.branch_format.__class__ is
2004
_mod_branch.BzrBranchFormat6):
2005
branch_converter = _mod_branch.Converter5to6()
2006
branch_converter.convert(branch)
2010
class BzrDirFormatInfo(object):
2012
def __init__(self, native, deprecated):
2013
self.deprecated = deprecated
2014
self.native = native
2017
class BzrDirFormatRegistry(registry.Registry):
2018
"""Registry of user-selectable BzrDir subformats.
2020
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2021
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2024
def register_metadir(self, key, repo, help, native=True, deprecated=False,
2025
repo_module='bzrlib.repository', branch_format=None):
2026
"""Register a metadir subformat.
2028
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2029
by the Repository format.
2031
:param repo: The repository format class name as a string.
2033
:param repo_module: The module from which the repository class
2034
should be lazily loaded. By default this is bzrlib.repository.
2036
# This should be expanded to support setting WorkingTree and Branch
2037
# formats, once BzrDirMetaFormat1 supports that.
2039
import bzrlib.branch
2040
mod = __import__(repo_module, globals(), locals(), [repo])
2042
repo_format_class = getattr(mod, repo)
2043
except AttributeError:
2044
raise AttributeError('no repository format %r in module %r'
2046
bd = BzrDirMetaFormat1()
2047
bd.repository_format = repo_format_class()
2048
if branch_format is not None:
2049
bd.branch_format = getattr(bzrlib.branch, branch_format)()
2051
self.register(key, helper, help, native, deprecated)
2053
def register(self, key, factory, help, native=True, deprecated=False):
2054
"""Register a BzrDirFormat factory.
2056
The factory must be a callable that takes one parameter: the key.
2057
It must produce an instance of the BzrDirFormat when called.
2059
This function mainly exists to prevent the info object from being
2062
registry.Registry.register(self, key, factory, help,
2063
BzrDirFormatInfo(native, deprecated))
2065
def register_lazy(self, key, module_name, member_name, help, native=True,
2067
registry.Registry.register_lazy(self, key, module_name, member_name,
2068
help, BzrDirFormatInfo(native, deprecated))
2070
def set_default(self, key):
2071
"""Set the 'default' key to be a clone of the supplied key.
2073
This method must be called once and only once.
2075
registry.Registry.register(self, 'default', self.get(key),
2076
self.get_help(key), info=self.get_info(key))
2078
def set_default_repository(self, key):
2079
"""Set the FormatRegistry default and Repository default.
2081
This is a transitional method while Repository.set_default_format
2084
if 'default' in self:
2085
self.remove('default')
2086
self.set_default(key)
2087
format = self.get('default')()
2088
assert isinstance(format, BzrDirMetaFormat1)
2090
def make_bzrdir(self, key):
2091
return self.get(key)()
2093
def help_topic(self, topic):
2094
output = textwrap.dedent("""\
2095
Bazaar directory formats
2096
------------------------
2098
These formats can be used for creating branches, working trees, and
2102
default_help = self.get_help('default')
2104
for key in self.keys():
2105
if key == 'default':
2107
help = self.get_help(key)
2108
if help == default_help:
2109
default_realkey = key
2111
help_pairs.append((key, help))
2113
def wrapped(key, help, info):
2115
help = '(native) ' + help
2116
return ' %s:\n%s\n\n' % (key,
2117
textwrap.fill(help, initial_indent=' ',
2118
subsequent_indent=' '))
2119
output += wrapped('%s/default' % default_realkey, default_help,
2120
self.get_info('default'))
2121
deprecated_pairs = []
2122
for key, help in help_pairs:
2123
info = self.get_info(key)
2125
deprecated_pairs.append((key, help))
2127
output += wrapped(key, help, info)
2128
if len(deprecated_pairs) > 0:
2129
output += "Deprecated formats\n------------------\n\n"
2130
for key, help in deprecated_pairs:
2131
info = self.get_info(key)
2132
output += wrapped(key, help, info)
2137
format_registry = BzrDirFormatRegistry()
2138
format_registry.register('weave', BzrDirFormat6,
2139
'Pre-0.8 format. Slower than knit and does not'
2140
' support checkouts or shared repositories.',
2142
format_registry.register_metadir('knit',
2143
'RepositoryFormatKnit1',
2144
'Format using knits. Recommended.',
2145
repo_module='bzrlib.repofmt.knitrepo', branch_format='BzrBranchFormat5')
2146
format_registry.set_default('knit')
2147
format_registry.register_metadir('metaweave', 'RepositoryFormat7',
2148
'Transitional format in 0.8. Slower than knit.',
2150
repo_module='bzrlib.repofmt.weaverepo')
2151
format_registry.register_metadir('experimental-knit2', 'RepositoryFormatKnit2',
2152
'Experimental successor to knit. Use at your own risk.',
2153
repo_module='bzrlib.repofmt.knitrepo',
2154
branch_format='BzrBranchFormat5')
2156
format_registry.register_metadir('experimental-branch6',
2157
'RepositoryFormatKnit1',
2158
'Experimental successor to knit. Use at your own risk.',
2159
repo_module='bzrlib.repofmt.knitrepo',
2160
branch_format='BzrBranchFormat6')