1
# Copyright (C) 2005, 2006 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,
48
repository as _mod_repository,
54
from bzrlib.osutils import (
59
from bzrlib.store.revision.text import TextRevisionStore
60
from bzrlib.store.text import TextStore
61
from bzrlib.store.versioned import WeaveStore
62
from bzrlib.transactions import WriteTransaction
63
from bzrlib.transport import get_transport
64
from bzrlib.weave import Weave
67
from bzrlib.trace import mutter
68
from bzrlib.transport.local import LocalTransport
72
"""A .bzr control diretory.
74
BzrDir instances let you create or open any of the things that can be
75
found within .bzr - checkouts, branches and repositories.
78
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
80
a transport connected to the directory this bzr was opened from.
84
"""Invoke break_lock on the first object in the bzrdir.
86
If there is a tree, the tree is opened and break_lock() called.
87
Otherwise, branch is tried, and finally repository.
90
thing_to_unlock = self.open_workingtree()
91
except (errors.NotLocalUrl, errors.NoWorkingTree):
93
thing_to_unlock = self.open_branch()
94
except errors.NotBranchError:
96
thing_to_unlock = self.open_repository()
97
except errors.NoRepositoryPresent:
99
thing_to_unlock.break_lock()
101
def can_convert_format(self):
102
"""Return true if this bzrdir is one whose format we can convert from."""
105
def check_conversion_target(self, target_format):
106
target_repo_format = target_format.repository_format
107
source_repo_format = self._format.repository_format
108
source_repo_format.check_conversion_target(target_repo_format)
111
def _check_supported(format, allow_unsupported):
112
"""Check whether format is a supported format.
114
If allow_unsupported is True, this is a no-op.
116
if not allow_unsupported and not format.is_supported():
117
# see open_downlevel to open legacy branches.
118
raise errors.UnsupportedFormatError(format=format)
120
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
121
"""Clone this bzrdir and its contents to url verbatim.
123
If urls last component does not exist, it will be created.
125
if revision_id is not None, then the clone operation may tune
126
itself to download less data.
127
:param force_new_repo: Do not use a shared repository for the target
128
even if one is available.
131
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
132
result = self._format.initialize(url)
134
local_repo = self.find_repository()
135
except errors.NoRepositoryPresent:
138
# may need to copy content in
140
result_repo = local_repo.clone(
142
revision_id=revision_id,
144
result_repo.set_make_working_trees(local_repo.make_working_trees())
147
result_repo = result.find_repository()
148
# fetch content this dir needs.
150
# XXX FIXME RBC 20060214 need tests for this when the basis
152
result_repo.fetch(basis_repo, revision_id=revision_id)
153
result_repo.fetch(local_repo, revision_id=revision_id)
154
except errors.NoRepositoryPresent:
155
# needed to make one anyway.
156
result_repo = local_repo.clone(
158
revision_id=revision_id,
160
result_repo.set_make_working_trees(local_repo.make_working_trees())
161
# 1 if there is a branch present
162
# make sure its content is available in the target repository
165
self.open_branch().clone(result, revision_id=revision_id)
166
except errors.NotBranchError:
169
self.open_workingtree().clone(result, basis=basis_tree)
170
except (errors.NoWorkingTree, errors.NotLocalUrl):
174
def _get_basis_components(self, basis):
175
"""Retrieve the basis components that are available at basis."""
177
return None, None, None
179
basis_tree = basis.open_workingtree()
180
basis_branch = basis_tree.branch
181
basis_repo = basis_branch.repository
182
except (errors.NoWorkingTree, errors.NotLocalUrl):
185
basis_branch = basis.open_branch()
186
basis_repo = basis_branch.repository
187
except errors.NotBranchError:
190
basis_repo = basis.open_repository()
191
except errors.NoRepositoryPresent:
193
return basis_repo, basis_branch, basis_tree
195
# TODO: This should be given a Transport, and should chdir up; otherwise
196
# this will open a new connection.
197
def _make_tail(self, url):
198
head, tail = urlutils.split(url)
199
if tail and tail != '.':
200
t = get_transport(head)
203
except errors.FileExists:
206
# TODO: Should take a Transport
208
def create(cls, base, format=None):
209
"""Create a new BzrDir at the url 'base'.
211
This will call the current default formats initialize with base
212
as the only parameter.
214
:param format: If supplied, the format of branch to create. If not
215
supplied, the default is used.
217
if cls is not BzrDir:
218
raise AssertionError("BzrDir.create always creates the default"
219
" format, not one of %r" % cls)
220
head, tail = urlutils.split(base)
221
if tail and tail != '.':
222
t = get_transport(head)
225
except errors.FileExists:
228
format = BzrDirFormat.get_default_format()
229
return format.initialize(safe_unicode(base))
231
def create_branch(self):
232
"""Create a branch in this BzrDir.
234
The bzrdirs format will control what branch format is created.
235
For more control see BranchFormatXX.create(a_bzrdir).
237
raise NotImplementedError(self.create_branch)
240
def create_branch_and_repo(base, force_new_repo=False, format=None):
241
"""Create a new BzrDir, Branch and Repository at the url 'base'.
243
This will use the current default BzrDirFormat, and use whatever
244
repository format that that uses via bzrdir.create_branch and
245
create_repository. If a shared repository is available that is used
248
The created Branch object is returned.
250
:param base: The URL to create the branch at.
251
:param force_new_repo: If True a new repository is always created.
253
bzrdir = BzrDir.create(base, format)
254
bzrdir._find_or_create_repository(force_new_repo)
255
return bzrdir.create_branch()
257
def _find_or_create_repository(self, force_new_repo):
258
"""Create a new repository if needed, returning the repository."""
260
return self.create_repository()
262
return self.find_repository()
263
except errors.NoRepositoryPresent:
264
return self.create_repository()
267
def create_branch_convenience(base, force_new_repo=False,
268
force_new_tree=None, format=None):
269
"""Create a new BzrDir, Branch and Repository at the url 'base'.
271
This is a convenience function - it will use an existing repository
272
if possible, can be told explicitly whether to create a working tree or
275
This will use the current default BzrDirFormat, and use whatever
276
repository format that that uses via bzrdir.create_branch and
277
create_repository. If a shared repository is available that is used
278
preferentially. Whatever repository is used, its tree creation policy
281
The created Branch object is returned.
282
If a working tree cannot be made due to base not being a file:// url,
283
no error is raised unless force_new_tree is True, in which case no
284
data is created on disk and NotLocalUrl is raised.
286
:param base: The URL to create the branch at.
287
:param force_new_repo: If True a new repository is always created.
288
:param force_new_tree: If True or False force creation of a tree or
289
prevent such creation respectively.
290
:param format: Override for the for the bzrdir format to create
293
# check for non local urls
294
t = get_transport(safe_unicode(base))
295
if not isinstance(t, LocalTransport):
296
raise errors.NotLocalUrl(base)
297
bzrdir = BzrDir.create(base, format)
298
repo = bzrdir._find_or_create_repository(force_new_repo)
299
result = bzrdir.create_branch()
300
if force_new_tree or (repo.make_working_trees() and
301
force_new_tree is None):
303
bzrdir.create_workingtree()
304
except errors.NotLocalUrl:
309
def create_repository(base, shared=False, format=None):
310
"""Create a new BzrDir and Repository at the url 'base'.
312
If no format is supplied, this will default to the current default
313
BzrDirFormat by default, and use whatever repository format that that
314
uses for bzrdirformat.create_repository.
316
:param shared: Create a shared repository rather than a standalone
318
The Repository object is returned.
320
This must be overridden as an instance method in child classes, where
321
it should take no parameters and construct whatever repository format
322
that child class desires.
324
bzrdir = BzrDir.create(base, format)
325
return bzrdir.create_repository(shared)
328
def create_standalone_workingtree(base, format=None):
329
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
331
'base' must be a local path or a file:// url.
333
This will use the current default BzrDirFormat, and use whatever
334
repository format that that uses for bzrdirformat.create_workingtree,
335
create_branch and create_repository.
337
:return: The WorkingTree object.
339
t = get_transport(safe_unicode(base))
340
if not isinstance(t, LocalTransport):
341
raise errors.NotLocalUrl(base)
342
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
344
format=format).bzrdir
345
return bzrdir.create_workingtree()
347
def create_workingtree(self, revision_id=None):
348
"""Create a working tree at this BzrDir.
350
revision_id: create it as of this revision id.
352
raise NotImplementedError(self.create_workingtree)
354
def destroy_workingtree(self):
355
"""Destroy the working tree at this BzrDir.
357
Formats that do not support this may raise UnsupportedOperation.
359
raise NotImplementedError(self.destroy_workingtree)
361
def destroy_workingtree_metadata(self):
362
"""Destroy the control files for the working tree at this BzrDir.
364
The contents of working tree files are not affected.
365
Formats that do not support this may raise UnsupportedOperation.
367
raise NotImplementedError(self.destroy_workingtree_metadata)
369
def find_repository(self):
370
"""Find the repository that should be used for a_bzrdir.
372
This does not require a branch as we use it to find the repo for
373
new branches as well as to hook existing branches up to their
377
return self.open_repository()
378
except errors.NoRepositoryPresent:
380
next_transport = self.root_transport.clone('..')
382
# find the next containing bzrdir
384
found_bzrdir = BzrDir.open_containing_from_transport(
386
except errors.NotBranchError:
388
raise errors.NoRepositoryPresent(self)
389
# does it have a repository ?
391
repository = found_bzrdir.open_repository()
392
except errors.NoRepositoryPresent:
393
next_transport = found_bzrdir.root_transport.clone('..')
394
if (found_bzrdir.root_transport.base == next_transport.base):
395
# top of the file system
399
if ((found_bzrdir.root_transport.base ==
400
self.root_transport.base) or repository.is_shared()):
403
raise errors.NoRepositoryPresent(self)
404
raise errors.NoRepositoryPresent(self)
406
def get_branch_transport(self, branch_format):
407
"""Get the transport for use by branch format in this BzrDir.
409
Note that bzr dirs that do not support format strings will raise
410
IncompatibleFormat if the branch format they are given has
411
a format string, and vice versa.
413
If branch_format is None, the transport is returned with no
414
checking. if it is not None, then the returned transport is
415
guaranteed to point to an existing directory ready for use.
417
raise NotImplementedError(self.get_branch_transport)
419
def get_repository_transport(self, repository_format):
420
"""Get the transport for use by repository format in this BzrDir.
422
Note that bzr dirs that do not support format strings will raise
423
IncompatibleFormat if the repository format they are given has
424
a format string, and vice versa.
426
If repository_format is None, the transport is returned with no
427
checking. if it is not None, then the returned transport is
428
guaranteed to point to an existing directory ready for use.
430
raise NotImplementedError(self.get_repository_transport)
432
def get_workingtree_transport(self, tree_format):
433
"""Get the transport for use by workingtree format in this BzrDir.
435
Note that bzr dirs that do not support format strings will raise
436
IncompatibleFormat if the workingtree format they are given has
437
a format string, and vice versa.
439
If workingtree_format is None, the transport is returned with no
440
checking. if it is not None, then the returned transport is
441
guaranteed to point to an existing directory ready for use.
443
raise NotImplementedError(self.get_workingtree_transport)
445
def __init__(self, _transport, _format):
446
"""Initialize a Bzr control dir object.
448
Only really common logic should reside here, concrete classes should be
449
made with varying behaviours.
451
:param _format: the format that is creating this BzrDir instance.
452
:param _transport: the transport this dir is based at.
454
self._format = _format
455
self.transport = _transport.clone('.bzr')
456
self.root_transport = _transport
458
def is_control_filename(self, filename):
459
"""True if filename is the name of a path which is reserved for bzrdir's.
461
:param filename: A filename within the root transport of this bzrdir.
463
This is true IF and ONLY IF the filename is part of the namespace reserved
464
for bzr control dirs. Currently this is the '.bzr' directory in the root
465
of the root_transport. it is expected that plugins will need to extend
466
this in the future - for instance to make bzr talk with svn working
469
# this might be better on the BzrDirFormat class because it refers to
470
# all the possible bzrdir disk formats.
471
# This method is tested via the workingtree is_control_filename tests-
472
# it was extracted from WorkingTree.is_control_filename. If the methods
473
# contract is extended beyond the current trivial implementation please
474
# add new tests for it to the appropriate place.
475
return filename == '.bzr' or filename.startswith('.bzr/')
477
def needs_format_conversion(self, format=None):
478
"""Return true if this bzrdir needs convert_format run on it.
480
For instance, if the repository format is out of date but the
481
branch and working tree are not, this should return True.
483
:param format: Optional parameter indicating a specific desired
484
format we plan to arrive at.
486
raise NotImplementedError(self.needs_format_conversion)
489
def open_unsupported(base):
490
"""Open a branch which is not supported."""
491
return BzrDir.open(base, _unsupported=True)
494
def open(base, _unsupported=False):
495
"""Open an existing bzrdir, rooted at 'base' (url)
497
_unsupported is a private parameter to the BzrDir class.
499
t = get_transport(base)
500
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
503
def open_from_transport(transport, _unsupported=False):
504
"""Open a bzrdir within a particular directory.
506
:param transport: Transport containing the bzrdir.
507
:param _unsupported: private.
509
format = BzrDirFormat.find_format(transport)
510
BzrDir._check_supported(format, _unsupported)
511
return format.open(transport, _found=True)
513
def open_branch(self, unsupported=False):
514
"""Open the branch object at this BzrDir if one is present.
516
If unsupported is True, then no longer supported branch formats can
519
TODO: static convenience version of this?
521
raise NotImplementedError(self.open_branch)
524
def open_containing(url):
525
"""Open an existing branch which contains url.
527
:param url: url to search from.
528
See open_containing_from_transport for more detail.
530
return BzrDir.open_containing_from_transport(get_transport(url))
533
def open_containing_from_transport(a_transport):
534
"""Open an existing branch which contains a_transport.base
536
This probes for a branch at a_transport, and searches upwards from there.
538
Basically we keep looking up until we find the control directory or
539
run into the root. If there isn't one, raises NotBranchError.
540
If there is one and it is either an unrecognised format or an unsupported
541
format, UnknownFormatError or UnsupportedFormatError are raised.
542
If there is one, it is returned, along with the unused portion of url.
544
:return: The BzrDir that contains the path, and a Unicode path
545
for the rest of the URL.
547
# this gets the normalised url back. I.e. '.' -> the full path.
548
url = a_transport.base
551
result = BzrDir.open_from_transport(a_transport)
552
return result, urlutils.unescape(a_transport.relpath(url))
553
except errors.NotBranchError, e:
555
new_t = a_transport.clone('..')
556
if new_t.base == a_transport.base:
557
# reached the root, whatever that may be
558
raise errors.NotBranchError(path=url)
562
def open_containing_tree_or_branch(klass, location):
563
"""Return the branch and working tree contained by a location.
565
Returns (tree, branch, relpath).
566
If there is no tree at containing the location, tree will be None.
567
If there is no branch containing the location, an exception will be
569
relpath is the portion of the path that is contained by the branch.
571
bzrdir, relpath = klass.open_containing(location)
573
tree = bzrdir.open_workingtree()
574
except (errors.NoWorkingTree, errors.NotLocalUrl):
576
branch = bzrdir.open_branch()
579
return tree, branch, relpath
581
def open_repository(self, _unsupported=False):
582
"""Open the repository object at this BzrDir if one is present.
584
This will not follow the Branch object pointer - its strictly a direct
585
open facility. Most client code should use open_branch().repository to
588
_unsupported is a private parameter, not part of the api.
589
TODO: static convenience version of this?
591
raise NotImplementedError(self.open_repository)
593
def open_workingtree(self, _unsupported=False):
594
"""Open the workingtree object at this BzrDir if one is present.
596
TODO: static convenience version of this?
598
raise NotImplementedError(self.open_workingtree)
600
def has_branch(self):
601
"""Tell if this bzrdir contains a branch.
603
Note: if you're going to open the branch, you should just go ahead
604
and try, and not ask permission first. (This method just opens the
605
branch and discards it, and that's somewhat expensive.)
610
except errors.NotBranchError:
613
def has_workingtree(self):
614
"""Tell if this bzrdir contains a working tree.
616
This will still raise an exception if the bzrdir has a workingtree that
617
is remote & inaccessible.
619
Note: if you're going to open the working tree, you should just go ahead
620
and try, and not ask permission first. (This method just opens the
621
workingtree and discards it, and that's somewhat expensive.)
624
self.open_workingtree()
626
except errors.NoWorkingTree:
629
def cloning_metadir(self, basis=None):
630
"""Produce a metadir suitable for cloning with"""
631
def related_repository(bzrdir):
633
branch = bzrdir.open_branch()
634
return branch.repository
635
except errors.NotBranchError:
637
return bzrdir.open_repository()
638
result_format = self._format.__class__()
641
source_repository = related_repository(self)
642
except errors.NoRepositoryPresent:
645
source_repository = related_repository(self)
646
result_format.repository_format = source_repository._format
647
except errors.NoRepositoryPresent:
651
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
652
"""Create a copy of this bzrdir prepared for use as a new line of
655
If urls last component does not exist, it will be created.
657
Attributes related to the identity of the source branch like
658
branch nickname will be cleaned, a working tree is created
659
whether one existed before or not; and a local branch is always
662
if revision_id is not None, then the clone operation may tune
663
itself to download less data.
666
cloning_format = self.cloning_metadir(basis)
667
result = cloning_format.initialize(url)
668
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
670
source_branch = self.open_branch()
671
source_repository = source_branch.repository
672
except errors.NotBranchError:
675
source_repository = self.open_repository()
676
except errors.NoRepositoryPresent:
677
# copy the entire basis one if there is one
678
# but there is no repository.
679
source_repository = basis_repo
684
result_repo = result.find_repository()
685
except errors.NoRepositoryPresent:
687
if source_repository is None and result_repo is not None:
689
elif source_repository is None and result_repo is None:
690
# no repo available, make a new one
691
result.create_repository()
692
elif source_repository is not None and result_repo is None:
693
# have source, and want to make a new target repo
694
# we don't clone the repo because that preserves attributes
695
# like is_shared(), and we have not yet implemented a
696
# repository sprout().
697
result_repo = result.create_repository()
698
if result_repo is not None:
699
# fetch needed content into target.
701
# XXX FIXME RBC 20060214 need tests for this when the basis
703
result_repo.fetch(basis_repo, revision_id=revision_id)
704
if source_repository is not None:
705
result_repo.fetch(source_repository, revision_id=revision_id)
706
if source_branch is not None:
707
source_branch.sprout(result, revision_id=revision_id)
709
result.create_branch()
710
# TODO: jam 20060426 we probably need a test in here in the
711
# case that the newly sprouted branch is a remote one
712
if result_repo is None or result_repo.make_working_trees():
713
wt = result.create_workingtree()
714
if wt.inventory.root is None:
716
wt.set_root_id(self.open_workingtree.get_root_id())
717
except errors.NoWorkingTree:
722
class BzrDirPreSplitOut(BzrDir):
723
"""A common class for the all-in-one formats."""
725
def __init__(self, _transport, _format):
726
"""See BzrDir.__init__."""
727
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
728
assert self._format._lock_class == lockable_files.TransportLock
729
assert self._format._lock_file_name == 'branch-lock'
730
self._control_files = lockable_files.LockableFiles(
731
self.get_branch_transport(None),
732
self._format._lock_file_name,
733
self._format._lock_class)
735
def break_lock(self):
736
"""Pre-splitout bzrdirs do not suffer from stale locks."""
737
raise NotImplementedError(self.break_lock)
739
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
740
"""See BzrDir.clone()."""
741
from bzrlib.workingtree import WorkingTreeFormat2
743
result = self._format._initialize_for_clone(url)
744
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
745
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
746
from_branch = self.open_branch()
747
from_branch.clone(result, revision_id=revision_id)
749
self.open_workingtree().clone(result, basis=basis_tree)
750
except errors.NotLocalUrl:
751
# make a new one, this format always has to have one.
753
WorkingTreeFormat2().initialize(result)
754
except errors.NotLocalUrl:
755
# but we cannot do it for remote trees.
756
to_branch = result.open_branch()
757
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
760
def create_branch(self):
761
"""See BzrDir.create_branch."""
762
return self.open_branch()
764
def create_repository(self, shared=False):
765
"""See BzrDir.create_repository."""
767
raise errors.IncompatibleFormat('shared repository', self._format)
768
return self.open_repository()
770
def create_workingtree(self, revision_id=None):
771
"""See BzrDir.create_workingtree."""
772
# this looks buggy but is not -really-
773
# clone and sprout will have set the revision_id
774
# and that will have set it for us, its only
775
# specific uses of create_workingtree in isolation
776
# that can do wonky stuff here, and that only
777
# happens for creating checkouts, which cannot be
778
# done on this format anyway. So - acceptable wart.
779
result = self.open_workingtree()
780
if revision_id is not None:
781
if revision_id == _mod_revision.NULL_REVISION:
782
result.set_parent_ids([])
784
result.set_parent_ids([revision_id])
787
def destroy_workingtree(self):
788
"""See BzrDir.destroy_workingtree."""
789
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
791
def destroy_workingtree_metadata(self):
792
"""See BzrDir.destroy_workingtree_metadata."""
793
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
796
def get_branch_transport(self, branch_format):
797
"""See BzrDir.get_branch_transport()."""
798
if branch_format is None:
799
return self.transport
801
branch_format.get_format_string()
802
except NotImplementedError:
803
return self.transport
804
raise errors.IncompatibleFormat(branch_format, self._format)
806
def get_repository_transport(self, repository_format):
807
"""See BzrDir.get_repository_transport()."""
808
if repository_format is None:
809
return self.transport
811
repository_format.get_format_string()
812
except NotImplementedError:
813
return self.transport
814
raise errors.IncompatibleFormat(repository_format, self._format)
816
def get_workingtree_transport(self, workingtree_format):
817
"""See BzrDir.get_workingtree_transport()."""
818
if workingtree_format is None:
819
return self.transport
821
workingtree_format.get_format_string()
822
except NotImplementedError:
823
return self.transport
824
raise errors.IncompatibleFormat(workingtree_format, self._format)
826
def needs_format_conversion(self, format=None):
827
"""See BzrDir.needs_format_conversion()."""
828
# if the format is not the same as the system default,
829
# an upgrade is needed.
831
format = BzrDirFormat.get_default_format()
832
return not isinstance(self._format, format.__class__)
834
def open_branch(self, unsupported=False):
835
"""See BzrDir.open_branch."""
836
from bzrlib.branch import BzrBranchFormat4
837
format = BzrBranchFormat4()
838
self._check_supported(format, unsupported)
839
return format.open(self, _found=True)
841
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
842
"""See BzrDir.sprout()."""
843
from bzrlib.workingtree import WorkingTreeFormat2
845
result = self._format._initialize_for_clone(url)
846
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
848
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
849
except errors.NoRepositoryPresent:
852
self.open_branch().sprout(result, revision_id=revision_id)
853
except errors.NotBranchError:
855
# we always want a working tree
856
WorkingTreeFormat2().initialize(result)
860
class BzrDir4(BzrDirPreSplitOut):
861
"""A .bzr version 4 control object.
863
This is a deprecated format and may be removed after sept 2006.
866
def create_repository(self, shared=False):
867
"""See BzrDir.create_repository."""
868
return self._format.repository_format.initialize(self, shared)
870
def needs_format_conversion(self, format=None):
871
"""Format 4 dirs are always in need of conversion."""
874
def open_repository(self):
875
"""See BzrDir.open_repository."""
876
from bzrlib.repository import RepositoryFormat4
877
return RepositoryFormat4().open(self, _found=True)
880
class BzrDir5(BzrDirPreSplitOut):
881
"""A .bzr version 5 control object.
883
This is a deprecated format and may be removed after sept 2006.
886
def open_repository(self):
887
"""See BzrDir.open_repository."""
888
from bzrlib.repository import RepositoryFormat5
889
return RepositoryFormat5().open(self, _found=True)
891
def open_workingtree(self, _unsupported=False):
892
"""See BzrDir.create_workingtree."""
893
from bzrlib.workingtree import WorkingTreeFormat2
894
return WorkingTreeFormat2().open(self, _found=True)
897
class BzrDir6(BzrDirPreSplitOut):
898
"""A .bzr version 6 control object.
900
This is a deprecated format and may be removed after sept 2006.
903
def open_repository(self):
904
"""See BzrDir.open_repository."""
905
from bzrlib.repository import RepositoryFormat6
906
return RepositoryFormat6().open(self, _found=True)
908
def open_workingtree(self, _unsupported=False):
909
"""See BzrDir.create_workingtree."""
910
from bzrlib.workingtree import WorkingTreeFormat2
911
return WorkingTreeFormat2().open(self, _found=True)
914
class BzrDirMeta1(BzrDir):
915
"""A .bzr meta version 1 control object.
917
This is the first control object where the
918
individual aspects are really split out: there are separate repository,
919
workingtree and branch subdirectories and any subset of the three can be
920
present within a BzrDir.
923
def can_convert_format(self):
924
"""See BzrDir.can_convert_format()."""
927
def create_branch(self):
928
"""See BzrDir.create_branch."""
929
return self._format.branch_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:
1015
if not isinstance(self.open_branch()._format,
1016
format.branch_format.__class__):
1017
# the repository needs an upgrade.
1019
except errors.NotBranchError:
1021
# currently there are no other possible conversions for meta1 formats.
1024
def open_branch(self, unsupported=False):
1025
"""See BzrDir.open_branch."""
1026
from bzrlib.branch import BranchFormat
1027
format = BranchFormat.find_format(self)
1028
self._check_supported(format, unsupported)
1029
return format.open(self, _found=True)
1031
def open_repository(self, unsupported=False):
1032
"""See BzrDir.open_repository."""
1033
from bzrlib.repository import RepositoryFormat
1034
format = RepositoryFormat.find_format(self)
1035
self._check_supported(format, unsupported)
1036
return format.open(self, _found=True)
1038
def open_workingtree(self, unsupported=False):
1039
"""See BzrDir.open_workingtree."""
1040
from bzrlib.workingtree import WorkingTreeFormat
1041
format = WorkingTreeFormat.find_format(self)
1042
self._check_supported(format, unsupported)
1043
return format.open(self, _found=True)
1046
class BzrDirFormat(object):
1047
"""An encapsulation of the initialization and open routines for a format.
1049
Formats provide three things:
1050
* An initialization routine,
1054
Formats are placed in an dict by their format string for reference
1055
during bzrdir opening. These should be subclasses of BzrDirFormat
1058
Once a format is deprecated, just deprecate the initialize and open
1059
methods on the format class. Do not deprecate the object, as the
1060
object will be created every system load.
1063
_default_format = None
1064
"""The default format used for new .bzr dirs."""
1067
"""The known formats."""
1069
_control_formats = []
1070
"""The registered control formats - .bzr, ....
1072
This is a list of BzrDirFormat objects.
1075
_lock_file_name = 'branch-lock'
1077
# _lock_class must be set in subclasses to the lock type, typ.
1078
# TransportLock or LockDir
1081
def find_format(klass, transport):
1082
"""Return the format present at transport."""
1083
for format in klass._control_formats:
1085
return format.probe_transport(transport)
1086
except errors.NotBranchError:
1087
# this format does not find a control dir here.
1089
raise errors.NotBranchError(path=transport.base)
1092
def probe_transport(klass, transport):
1093
"""Return the .bzrdir style transport present at URL."""
1095
format_string = transport.get(".bzr/branch-format").read()
1096
except errors.NoSuchFile:
1097
raise errors.NotBranchError(path=transport.base)
1100
return klass._formats[format_string]
1102
raise errors.UnknownFormatError(format=format_string)
1105
def get_default_format(klass):
1106
"""Return the current default format."""
1107
return klass._default_format
1109
def get_format_string(self):
1110
"""Return the ASCII format string that identifies this format."""
1111
raise NotImplementedError(self.get_format_string)
1113
def get_format_description(self):
1114
"""Return the short description for this format."""
1115
raise NotImplementedError(self.get_format_description)
1117
def get_converter(self, format=None):
1118
"""Return the converter to use to convert bzrdirs needing converts.
1120
This returns a bzrlib.bzrdir.Converter object.
1122
This should return the best upgrader to step this format towards the
1123
current default format. In the case of plugins we can/should provide
1124
some means for them to extend the range of returnable converters.
1126
:param format: Optional format to override the default format of the
1129
raise NotImplementedError(self.get_converter)
1131
def initialize(self, url):
1132
"""Create a bzr control dir at this url and return an opened copy.
1134
Subclasses should typically override initialize_on_transport
1135
instead of this method.
1137
return self.initialize_on_transport(get_transport(url))
1139
def initialize_on_transport(self, transport):
1140
"""Initialize a new bzrdir in the base directory of a Transport."""
1141
# Since we don't have a .bzr directory, inherit the
1142
# mode from the root directory
1143
temp_control = lockable_files.LockableFiles(transport,
1144
'', lockable_files.TransportLock)
1145
temp_control._transport.mkdir('.bzr',
1146
# FIXME: RBC 20060121 don't peek under
1148
mode=temp_control._dir_mode)
1149
file_mode = temp_control._file_mode
1151
mutter('created control directory in ' + transport.base)
1152
control = transport.clone('.bzr')
1153
utf8_files = [('README',
1154
"This is a Bazaar-NG control directory.\n"
1155
"Do not change any files in this directory.\n"),
1156
('branch-format', self.get_format_string()),
1158
# NB: no need to escape relative paths that are url safe.
1159
control_files = lockable_files.LockableFiles(control,
1160
self._lock_file_name, self._lock_class)
1161
control_files.create_lock()
1162
control_files.lock_write()
1164
for file, content in utf8_files:
1165
control_files.put_utf8(file, content)
1167
control_files.unlock()
1168
return self.open(transport, _found=True)
1170
def is_supported(self):
1171
"""Is this format supported?
1173
Supported formats must be initializable and openable.
1174
Unsupported formats may not support initialization or committing or
1175
some other features depending on the reason for not being supported.
1179
def same_model(self, target_format):
1180
return (self.repository_format.rich_root_data ==
1181
target_format.rich_root_data)
1184
def known_formats(klass):
1185
"""Return all the known formats.
1187
Concrete formats should override _known_formats.
1189
# There is double indirection here to make sure that control
1190
# formats used by more than one dir format will only be probed
1191
# once. This can otherwise be quite expensive for remote connections.
1193
for format in klass._control_formats:
1194
result.update(format._known_formats())
1198
def _known_formats(klass):
1199
"""Return the known format instances for this control format."""
1200
return set(klass._formats.values())
1202
def open(self, transport, _found=False):
1203
"""Return an instance of this format for the dir transport points at.
1205
_found is a private parameter, do not use it.
1208
found_format = BzrDirFormat.find_format(transport)
1209
if not isinstance(found_format, self.__class__):
1210
raise AssertionError("%s was asked to open %s, but it seems to need "
1212
% (self, transport, found_format))
1213
return self._open(transport)
1215
def _open(self, transport):
1216
"""Template method helper for opening BzrDirectories.
1218
This performs the actual open and any additional logic or parameter
1221
raise NotImplementedError(self._open)
1224
def register_format(klass, format):
1225
klass._formats[format.get_format_string()] = format
1228
def register_control_format(klass, format):
1229
"""Register a format that does not use '.bzrdir' for its control dir.
1231
TODO: This should be pulled up into a 'ControlDirFormat' base class
1232
which BzrDirFormat can inherit from, and renamed to register_format
1233
there. It has been done without that for now for simplicity of
1236
klass._control_formats.append(format)
1239
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1240
def set_default_format(klass, format):
1241
klass._set_default_format(format)
1244
def _set_default_format(klass, format):
1245
"""Set default format (for testing behavior of defaults only)"""
1246
klass._default_format = format
1249
return self.get_format_string()[:-1]
1252
def unregister_format(klass, format):
1253
assert klass._formats[format.get_format_string()] is format
1254
del klass._formats[format.get_format_string()]
1257
def unregister_control_format(klass, format):
1258
klass._control_formats.remove(format)
1261
# register BzrDirFormat as a control format
1262
BzrDirFormat.register_control_format(BzrDirFormat)
1265
class BzrDirFormat4(BzrDirFormat):
1266
"""Bzr dir format 4.
1268
This format is a combined format for working tree, branch and repository.
1270
- Format 1 working trees [always]
1271
- Format 4 branches [always]
1272
- Format 4 repositories [always]
1274
This format is deprecated: it indexes texts using a text it which is
1275
removed in format 5; write support for this format has been removed.
1278
_lock_class = lockable_files.TransportLock
1280
def get_format_string(self):
1281
"""See BzrDirFormat.get_format_string()."""
1282
return "Bazaar-NG branch, format 0.0.4\n"
1284
def get_format_description(self):
1285
"""See BzrDirFormat.get_format_description()."""
1286
return "All-in-one format 4"
1288
def get_converter(self, format=None):
1289
"""See BzrDirFormat.get_converter()."""
1290
# there is one and only one upgrade path here.
1291
return ConvertBzrDir4To5()
1293
def initialize_on_transport(self, transport):
1294
"""Format 4 branches cannot be created."""
1295
raise errors.UninitializableFormat(self)
1297
def is_supported(self):
1298
"""Format 4 is not supported.
1300
It is not supported because the model changed from 4 to 5 and the
1301
conversion logic is expensive - so doing it on the fly was not
1306
def _open(self, transport):
1307
"""See BzrDirFormat._open."""
1308
return BzrDir4(transport, self)
1310
def __return_repository_format(self):
1311
"""Circular import protection."""
1312
from bzrlib.repository import RepositoryFormat4
1313
return RepositoryFormat4()
1314
repository_format = property(__return_repository_format)
1317
class BzrDirFormat5(BzrDirFormat):
1318
"""Bzr control format 5.
1320
This format is a combined format for working tree, branch and repository.
1322
- Format 2 working trees [always]
1323
- Format 4 branches [always]
1324
- Format 5 repositories [always]
1325
Unhashed stores in the repository.
1328
_lock_class = lockable_files.TransportLock
1330
def get_format_string(self):
1331
"""See BzrDirFormat.get_format_string()."""
1332
return "Bazaar-NG branch, format 5\n"
1334
def get_format_description(self):
1335
"""See BzrDirFormat.get_format_description()."""
1336
return "All-in-one format 5"
1338
def get_converter(self, format=None):
1339
"""See BzrDirFormat.get_converter()."""
1340
# there is one and only one upgrade path here.
1341
return ConvertBzrDir5To6()
1343
def _initialize_for_clone(self, url):
1344
return self.initialize_on_transport(get_transport(url), _cloning=True)
1346
def initialize_on_transport(self, transport, _cloning=False):
1347
"""Format 5 dirs always have working tree, branch and repository.
1349
Except when they are being cloned.
1351
from bzrlib.branch import BzrBranchFormat4
1352
from bzrlib.repository import RepositoryFormat5
1353
from bzrlib.workingtree import WorkingTreeFormat2
1354
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1355
RepositoryFormat5().initialize(result, _internal=True)
1357
branch = BzrBranchFormat4().initialize(result)
1359
WorkingTreeFormat2().initialize(result)
1360
except errors.NotLocalUrl:
1361
# Even though we can't access the working tree, we need to
1362
# create its control files.
1363
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1366
def _open(self, transport):
1367
"""See BzrDirFormat._open."""
1368
return BzrDir5(transport, self)
1370
def __return_repository_format(self):
1371
"""Circular import protection."""
1372
from bzrlib.repository import RepositoryFormat5
1373
return RepositoryFormat5()
1374
repository_format = property(__return_repository_format)
1377
class BzrDirFormat6(BzrDirFormat):
1378
"""Bzr control format 6.
1380
This format is a combined format for working tree, branch and repository.
1382
- Format 2 working trees [always]
1383
- Format 4 branches [always]
1384
- Format 6 repositories [always]
1387
_lock_class = lockable_files.TransportLock
1389
def get_format_string(self):
1390
"""See BzrDirFormat.get_format_string()."""
1391
return "Bazaar-NG branch, format 6\n"
1393
def get_format_description(self):
1394
"""See BzrDirFormat.get_format_description()."""
1395
return "All-in-one format 6"
1397
def get_converter(self, format=None):
1398
"""See BzrDirFormat.get_converter()."""
1399
# there is one and only one upgrade path here.
1400
return ConvertBzrDir6ToMeta()
1402
def _initialize_for_clone(self, url):
1403
return self.initialize_on_transport(get_transport(url), _cloning=True)
1405
def initialize_on_transport(self, transport, _cloning=False):
1406
"""Format 6 dirs always have working tree, branch and repository.
1408
Except when they are being cloned.
1410
from bzrlib.branch import BzrBranchFormat4
1411
from bzrlib.repository import RepositoryFormat6
1412
from bzrlib.workingtree import WorkingTreeFormat2
1413
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1414
RepositoryFormat6().initialize(result, _internal=True)
1416
branch = BzrBranchFormat4().initialize(result)
1418
WorkingTreeFormat2().initialize(result)
1419
except errors.NotLocalUrl:
1420
# Even though we can't access the working tree, we need to
1421
# create its control files.
1422
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1425
def _open(self, transport):
1426
"""See BzrDirFormat._open."""
1427
return BzrDir6(transport, self)
1429
def __return_repository_format(self):
1430
"""Circular import protection."""
1431
from bzrlib.repository import RepositoryFormat6
1432
return RepositoryFormat6()
1433
repository_format = property(__return_repository_format)
1436
class BzrDirMetaFormat1(BzrDirFormat):
1437
"""Bzr meta control format 1
1439
This is the first format with split out working tree, branch and repository
1442
- Format 3 working trees [optional]
1443
- Format 5 branches [optional]
1444
- Format 7 repositories [optional]
1447
_lock_class = lockdir.LockDir
1450
self._branch_format = None
1452
def _get_branch_format(self):
1453
if self._branch_format is None:
1454
from bzrlib.branch import BranchFormat
1455
self._branch_format = BranchFormat.get_default_format()
1456
return self._branch_format
1458
def _set_branch_format(self, format):
1459
self._branch_format = format
1461
branch_format = property(_get_branch_format, _set_branch_format)
1463
def get_converter(self, format=None):
1464
"""See BzrDirFormat.get_converter()."""
1466
format = BzrDirFormat.get_default_format()
1467
if not isinstance(self, format.__class__):
1468
# converting away from metadir is not implemented
1469
raise NotImplementedError(self.get_converter)
1470
return ConvertMetaToMeta(format)
1472
def get_format_string(self):
1473
"""See BzrDirFormat.get_format_string()."""
1474
return "Bazaar-NG meta directory, format 1\n"
1476
def get_format_description(self):
1477
"""See BzrDirFormat.get_format_description()."""
1478
return "Meta directory format 1"
1480
def _open(self, transport):
1481
"""See BzrDirFormat._open."""
1482
from bzrlib.repository import RepositoryFormat
1483
from bzrlib.branch import BranchFormat
1484
return BzrDirMeta1(transport, self)
1486
def __return_repository_format(self):
1487
"""Circular import protection."""
1488
if getattr(self, '_repository_format', None):
1489
return self._repository_format
1490
from bzrlib.repository import RepositoryFormat
1491
return RepositoryFormat.get_default_format()
1493
def __set_repository_format(self, value):
1494
"""Allow changint the repository format for metadir formats."""
1495
self._repository_format = value
1497
repository_format = property(__return_repository_format, __set_repository_format)
1500
BzrDirFormat.register_format(BzrDirFormat4())
1501
BzrDirFormat.register_format(BzrDirFormat5())
1502
BzrDirFormat.register_format(BzrDirFormat6())
1503
__default_format = BzrDirMetaFormat1()
1504
BzrDirFormat.register_format(__default_format)
1505
BzrDirFormat._default_format = __default_format
1508
class BzrDirTestProviderAdapter(object):
1509
"""A tool to generate a suite testing multiple bzrdir formats at once.
1511
This is done by copying the test once for each transport and injecting
1512
the transport_server, transport_readonly_server, and bzrdir_format
1513
classes into each copy. Each copy is also given a new id() to make it
1517
def __init__(self, transport_server, transport_readonly_server, formats):
1518
self._transport_server = transport_server
1519
self._transport_readonly_server = transport_readonly_server
1520
self._formats = formats
1522
def adapt(self, test):
1523
result = unittest.TestSuite()
1524
for format in self._formats:
1525
new_test = deepcopy(test)
1526
new_test.transport_server = self._transport_server
1527
new_test.transport_readonly_server = self._transport_readonly_server
1528
new_test.bzrdir_format = format
1529
def make_new_test_id():
1530
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1531
return lambda: new_id
1532
new_test.id = make_new_test_id()
1533
result.addTest(new_test)
1537
class Converter(object):
1538
"""Converts a disk format object from one format to another."""
1540
def convert(self, to_convert, pb):
1541
"""Perform the conversion of to_convert, giving feedback via pb.
1543
:param to_convert: The disk object to convert.
1544
:param pb: a progress bar to use for progress information.
1547
def step(self, message):
1548
"""Update the pb by a step."""
1550
self.pb.update(message, self.count, self.total)
1553
class ConvertBzrDir4To5(Converter):
1554
"""Converts format 4 bzr dirs to format 5."""
1557
super(ConvertBzrDir4To5, self).__init__()
1558
self.converted_revs = set()
1559
self.absent_revisions = set()
1563
def convert(self, to_convert, pb):
1564
"""See Converter.convert()."""
1565
self.bzrdir = to_convert
1567
self.pb.note('starting upgrade from format 4 to 5')
1568
if isinstance(self.bzrdir.transport, LocalTransport):
1569
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1570
self._convert_to_weaves()
1571
return BzrDir.open(self.bzrdir.root_transport.base)
1573
def _convert_to_weaves(self):
1574
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1577
stat = self.bzrdir.transport.stat('weaves')
1578
if not S_ISDIR(stat.st_mode):
1579
self.bzrdir.transport.delete('weaves')
1580
self.bzrdir.transport.mkdir('weaves')
1581
except errors.NoSuchFile:
1582
self.bzrdir.transport.mkdir('weaves')
1583
# deliberately not a WeaveFile as we want to build it up slowly.
1584
self.inv_weave = Weave('inventory')
1585
# holds in-memory weaves for all files
1586
self.text_weaves = {}
1587
self.bzrdir.transport.delete('branch-format')
1588
self.branch = self.bzrdir.open_branch()
1589
self._convert_working_inv()
1590
rev_history = self.branch.revision_history()
1591
# to_read is a stack holding the revisions we still need to process;
1592
# appending to it adds new highest-priority revisions
1593
self.known_revisions = set(rev_history)
1594
self.to_read = rev_history[-1:]
1596
rev_id = self.to_read.pop()
1597
if (rev_id not in self.revisions
1598
and rev_id not in self.absent_revisions):
1599
self._load_one_rev(rev_id)
1601
to_import = self._make_order()
1602
for i, rev_id in enumerate(to_import):
1603
self.pb.update('converting revision', i, len(to_import))
1604
self._convert_one_rev(rev_id)
1606
self._write_all_weaves()
1607
self._write_all_revs()
1608
self.pb.note('upgraded to weaves:')
1609
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1610
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1611
self.pb.note(' %6d texts', self.text_count)
1612
self._cleanup_spare_files_after_format4()
1613
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1615
def _cleanup_spare_files_after_format4(self):
1616
# FIXME working tree upgrade foo.
1617
for n in 'merged-patches', 'pending-merged-patches':
1619
## assert os.path.getsize(p) == 0
1620
self.bzrdir.transport.delete(n)
1621
except errors.NoSuchFile:
1623
self.bzrdir.transport.delete_tree('inventory-store')
1624
self.bzrdir.transport.delete_tree('text-store')
1626
def _convert_working_inv(self):
1627
inv = xml4.serializer_v4.read_inventory(
1628
self.branch.control_files.get('inventory'))
1629
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1630
# FIXME inventory is a working tree change.
1631
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1633
def _write_all_weaves(self):
1634
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1635
weave_transport = self.bzrdir.transport.clone('weaves')
1636
weaves = WeaveStore(weave_transport, prefixed=False)
1637
transaction = WriteTransaction()
1641
for file_id, file_weave in self.text_weaves.items():
1642
self.pb.update('writing weave', i, len(self.text_weaves))
1643
weaves._put_weave(file_id, file_weave, transaction)
1645
self.pb.update('inventory', 0, 1)
1646
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1647
self.pb.update('inventory', 1, 1)
1651
def _write_all_revs(self):
1652
"""Write all revisions out in new form."""
1653
self.bzrdir.transport.delete_tree('revision-store')
1654
self.bzrdir.transport.mkdir('revision-store')
1655
revision_transport = self.bzrdir.transport.clone('revision-store')
1657
_revision_store = TextRevisionStore(TextStore(revision_transport,
1661
transaction = WriteTransaction()
1662
for i, rev_id in enumerate(self.converted_revs):
1663
self.pb.update('write revision', i, len(self.converted_revs))
1664
_revision_store.add_revision(self.revisions[rev_id], transaction)
1668
def _load_one_rev(self, rev_id):
1669
"""Load a revision object into memory.
1671
Any parents not either loaded or abandoned get queued to be
1673
self.pb.update('loading revision',
1674
len(self.revisions),
1675
len(self.known_revisions))
1676
if not self.branch.repository.has_revision(rev_id):
1678
self.pb.note('revision {%s} not present in branch; '
1679
'will be converted as a ghost',
1681
self.absent_revisions.add(rev_id)
1683
rev = self.branch.repository._revision_store.get_revision(rev_id,
1684
self.branch.repository.get_transaction())
1685
for parent_id in rev.parent_ids:
1686
self.known_revisions.add(parent_id)
1687
self.to_read.append(parent_id)
1688
self.revisions[rev_id] = rev
1690
def _load_old_inventory(self, rev_id):
1691
assert rev_id not in self.converted_revs
1692
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1693
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1694
inv.revision_id = rev_id
1695
rev = self.revisions[rev_id]
1696
if rev.inventory_sha1:
1697
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1698
'inventory sha mismatch for {%s}' % rev_id
1701
def _load_updated_inventory(self, rev_id):
1702
assert rev_id in self.converted_revs
1703
inv_xml = self.inv_weave.get_text(rev_id)
1704
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1707
def _convert_one_rev(self, rev_id):
1708
"""Convert revision and all referenced objects to new format."""
1709
rev = self.revisions[rev_id]
1710
inv = self._load_old_inventory(rev_id)
1711
present_parents = [p for p in rev.parent_ids
1712
if p not in self.absent_revisions]
1713
self._convert_revision_contents(rev, inv, present_parents)
1714
self._store_new_weave(rev, inv, present_parents)
1715
self.converted_revs.add(rev_id)
1717
def _store_new_weave(self, rev, inv, present_parents):
1718
# the XML is now updated with text versions
1720
entries = inv.iter_entries()
1722
for path, ie in entries:
1723
assert getattr(ie, 'revision', None) is not None, \
1724
'no revision on {%s} in {%s}' % \
1725
(file_id, rev.revision_id)
1726
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1727
new_inv_sha1 = sha_string(new_inv_xml)
1728
self.inv_weave.add_lines(rev.revision_id,
1730
new_inv_xml.splitlines(True))
1731
rev.inventory_sha1 = new_inv_sha1
1733
def _convert_revision_contents(self, rev, inv, present_parents):
1734
"""Convert all the files within a revision.
1736
Also upgrade the inventory to refer to the text revision ids."""
1737
rev_id = rev.revision_id
1738
mutter('converting texts of revision {%s}',
1740
parent_invs = map(self._load_updated_inventory, present_parents)
1741
entries = inv.iter_entries()
1743
for path, ie in entries:
1744
self._convert_file_version(rev, ie, parent_invs)
1746
def _convert_file_version(self, rev, ie, parent_invs):
1747
"""Convert one version of one file.
1749
The file needs to be added into the weave if it is a merge
1750
of >=2 parents or if it's changed from its parent.
1752
file_id = ie.file_id
1753
rev_id = rev.revision_id
1754
w = self.text_weaves.get(file_id)
1757
self.text_weaves[file_id] = w
1758
text_changed = False
1759
previous_entries = ie.find_previous_heads(parent_invs,
1763
for old_revision in previous_entries:
1764
# if this fails, its a ghost ?
1765
assert old_revision in self.converted_revs, \
1766
"Revision {%s} not in converted_revs" % old_revision
1767
self.snapshot_ie(previous_entries, ie, w, rev_id)
1769
assert getattr(ie, 'revision', None) is not None
1771
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1772
# TODO: convert this logic, which is ~= snapshot to
1773
# a call to:. This needs the path figured out. rather than a work_tree
1774
# a v4 revision_tree can be given, or something that looks enough like
1775
# one to give the file content to the entry if it needs it.
1776
# and we need something that looks like a weave store for snapshot to
1778
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1779
if len(previous_revisions) == 1:
1780
previous_ie = previous_revisions.values()[0]
1781
if ie._unchanged(previous_ie):
1782
ie.revision = previous_ie.revision
1785
text = self.branch.repository.text_store.get(ie.text_id)
1786
file_lines = text.readlines()
1787
assert sha_strings(file_lines) == ie.text_sha1
1788
assert sum(map(len, file_lines)) == ie.text_size
1789
w.add_lines(rev_id, previous_revisions, file_lines)
1790
self.text_count += 1
1792
w.add_lines(rev_id, previous_revisions, [])
1793
ie.revision = rev_id
1795
def _make_order(self):
1796
"""Return a suitable order for importing revisions.
1798
The order must be such that an revision is imported after all
1799
its (present) parents.
1801
todo = set(self.revisions.keys())
1802
done = self.absent_revisions.copy()
1805
# scan through looking for a revision whose parents
1807
for rev_id in sorted(list(todo)):
1808
rev = self.revisions[rev_id]
1809
parent_ids = set(rev.parent_ids)
1810
if parent_ids.issubset(done):
1811
# can take this one now
1812
order.append(rev_id)
1818
class ConvertBzrDir5To6(Converter):
1819
"""Converts format 5 bzr dirs to format 6."""
1821
def convert(self, to_convert, pb):
1822
"""See Converter.convert()."""
1823
self.bzrdir = to_convert
1825
self.pb.note('starting upgrade from format 5 to 6')
1826
self._convert_to_prefixed()
1827
return BzrDir.open(self.bzrdir.root_transport.base)
1829
def _convert_to_prefixed(self):
1830
from bzrlib.store import TransportStore
1831
self.bzrdir.transport.delete('branch-format')
1832
for store_name in ["weaves", "revision-store"]:
1833
self.pb.note("adding prefixes to %s" % store_name)
1834
store_transport = self.bzrdir.transport.clone(store_name)
1835
store = TransportStore(store_transport, prefixed=True)
1836
for urlfilename in store_transport.list_dir('.'):
1837
filename = urlutils.unescape(urlfilename)
1838
if (filename.endswith(".weave") or
1839
filename.endswith(".gz") or
1840
filename.endswith(".sig")):
1841
file_id = os.path.splitext(filename)[0]
1844
prefix_dir = store.hash_prefix(file_id)
1845
# FIXME keep track of the dirs made RBC 20060121
1847
store_transport.move(filename, prefix_dir + '/' + filename)
1848
except errors.NoSuchFile: # catches missing dirs strangely enough
1849
store_transport.mkdir(prefix_dir)
1850
store_transport.move(filename, prefix_dir + '/' + filename)
1851
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1854
class ConvertBzrDir6ToMeta(Converter):
1855
"""Converts format 6 bzr dirs to metadirs."""
1857
def convert(self, to_convert, pb):
1858
"""See Converter.convert()."""
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', _mod_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
branch_format=None):
2026
"""Register a metadir subformat.
2028
repo is the repository format name as a string.
2030
# This should be expanded to support setting WorkingTree and Branch
2031
# formats, once BzrDirMetaFormat1 supports that.
2033
import bzrlib.repository
2034
import bzrlib.branch
2035
repo_format = getattr(bzrlib.repository, repo)
2036
bd = BzrDirMetaFormat1()
2037
bd.repository_format = repo_format()
2038
if branch_format is not None:
2039
bd.branch_format = getattr(bzrlib.branch, branch_format)()
2041
self.register(key, helper, help, native, deprecated)
2043
def register(self, key, factory, help, native=True, deprecated=False):
2044
"""Register a BzrDirFormat factory.
2046
The factory must be a callable that takes one parameter: the key.
2047
It must produce an instance of the BzrDirFormat when called.
2049
This function mainly exists to prevent the info object from being
2052
registry.Registry.register(self, key, factory, help,
2053
BzrDirFormatInfo(native, deprecated))
2055
def register_lazy(self, key, module_name, member_name, help, native=True,
2057
registry.Registry.register_lazy(self, key, module_name, member_name,
2058
help, BzrDirFormatInfo(native, deprecated))
2060
def set_default(self, key):
2061
"""Set the 'default' key to be a clone of the supplied key.
2063
This method must be called once and only once.
2065
registry.Registry.register(self, 'default', self.get(key),
2066
self.get_help(key), info=self.get_info(key))
2068
def set_default_repository(self, key):
2069
"""Set the FormatRegistry default and Repository default.
2071
This is a transitional method while Repository.set_default_format
2074
if 'default' in self:
2075
self.remove('default')
2076
self.set_default(key)
2077
format = self.get('default')()
2078
assert isinstance(format, BzrDirMetaFormat1)
2080
def make_bzrdir(self, key):
2081
return self.get(key)()
2083
def help_topic(self, topic):
2084
output = textwrap.dedent("""\
2085
Bazaar directory formats
2086
------------------------
2088
These formats can be used for creating branches, working trees, and
2092
default_help = self.get_help('default')
2094
for key in self.keys():
2095
if key == 'default':
2097
help = self.get_help(key)
2098
if help == default_help:
2099
default_realkey = key
2101
help_pairs.append((key, help))
2103
def wrapped(key, help, info):
2105
help = '(native) ' + help
2106
return ' %s:\n%s\n\n' % (key,
2107
textwrap.fill(help, initial_indent=' ',
2108
subsequent_indent=' '))
2109
output += wrapped('%s/default' % default_realkey, default_help,
2110
self.get_info('default'))
2111
deprecated_pairs = []
2112
for key, help in help_pairs:
2113
info = self.get_info(key)
2115
deprecated_pairs.append((key, help))
2117
output += wrapped(key, help, info)
2118
if len(deprecated_pairs) > 0:
2119
output += "Deprecated formats\n------------------\n\n"
2120
for key, help in deprecated_pairs:
2121
info = self.get_info(key)
2122
output += wrapped(key, help, info)
2127
format_registry = BzrDirFormatRegistry()
2128
format_registry.register('weave', BzrDirFormat6,
2129
'Pre-0.8 format. Slower than knit and does not'
2130
' support checkouts or shared repositories.', deprecated=True)
2131
format_registry.register_metadir('knit', 'RepositoryFormatKnit1',
2132
'Format using knits. Recommended.', branch_format='BzrBranchFormat5')
2133
format_registry.register_metadir('metaweave', 'RepositoryFormat7',
2134
'Transitional format in 0.8. Slower than knit.',
2135
branch_format='BzrBranchFormat5',
2137
format_registry.register_metadir('experimental-knit2', 'RepositoryFormatKnit2',
2138
'Experimental successor to knit. Use at your own risk.',
2139
branch_format='BzrBranchFormat5')
2140
format_registry.register_metadir('experimental-branch6',
2141
'RepositoryFormatKnit1',
2142
'Experimental successor to knit. Use at your own risk.',
2143
branch_format='BzrBranchFormat6')
2144
format_registry.set_default('knit')