1
# Copyright (C) 2005 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
from copy import deepcopy
25
from cStringIO import StringIO
26
from unittest import TestSuite
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles
31
from bzrlib.osutils import safe_unicode
32
from bzrlib.osutils import (
39
from bzrlib.store.text import TextStore
40
from bzrlib.store.versioned.weave import WeaveStore
41
from bzrlib.symbol_versioning import *
42
from bzrlib.trace import mutter
43
from bzrlib.transactions import PassThroughTransaction
44
from bzrlib.transport import get_transport
45
from bzrlib.transport.local import LocalTransport
46
from bzrlib.weave import Weave
47
from bzrlib.xml4 import serializer_v4
48
from bzrlib.xml5 import serializer_v5
52
"""A .bzr control diretory.
54
BzrDir instances let you create or open any of the things that can be
55
found within .bzr - checkouts, branches and repositories.
58
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
60
a transport connected to the directory this bzr was opened from.
63
def can_convert_format(self):
64
"""Return true if this bzrdir is one whose format we can convert from."""
67
def _check_supported(self, format, allow_unsupported):
68
"""Check whether format is a supported format.
70
If allow_unsupported is True, this is a no-op.
72
if not allow_unsupported and not format.is_supported():
73
raise errors.UnsupportedFormatError(format)
75
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
76
"""Clone this bzrdir and its contents to url verbatim.
78
If urls last component does not exist, it will be created.
80
if revision_id is not None, then the clone operation may tune
81
itself to download less data.
82
:param force_new_repo: Do not use a shared repository for the target
83
even if one is available.
86
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
87
result = self._format.initialize(url)
89
local_repo = self.find_repository()
90
except errors.NoRepositoryPresent:
93
# may need to copy content in
95
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
98
result_repo = result.find_repository()
99
# fetch content this dir needs.
101
# XXX FIXME RBC 20060214 need tests for this when the basis
103
result_repo.fetch(basis_repo, revision_id=revision_id)
104
result_repo.fetch(local_repo, revision_id=revision_id)
105
except errors.NoRepositoryPresent:
106
# needed to make one anyway.
107
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
108
# 1 if there is a branch present
109
# make sure its content is available in the target repository
112
self.open_branch().clone(result, revision_id=revision_id)
113
except errors.NotBranchError:
116
self.open_workingtree().clone(result, basis=basis_tree)
117
except (errors.NoWorkingTree, errors.NotLocalUrl):
121
def _get_basis_components(self, basis):
122
"""Retrieve the basis components that are available at basis."""
124
return None, None, None
126
basis_tree = basis.open_workingtree()
127
basis_branch = basis_tree.branch
128
basis_repo = basis_branch.repository
129
except (errors.NoWorkingTree, errors.NotLocalUrl):
132
basis_branch = basis.open_branch()
133
basis_repo = basis_branch.repository
134
except errors.NotBranchError:
137
basis_repo = basis.open_repository()
138
except errors.NoRepositoryPresent:
140
return basis_repo, basis_branch, basis_tree
142
def _make_tail(self, url):
143
segments = url.split('/')
144
if segments and segments[-1] not in ('', '.'):
145
parent = '/'.join(segments[:-1])
146
t = bzrlib.transport.get_transport(parent)
148
t.mkdir(segments[-1])
149
except errors.FileExists:
154
"""Create a new BzrDir at the url 'base'.
156
This will call the current default formats initialize with base
157
as the only parameter.
159
If you need a specific format, consider creating an instance
160
of that and calling initialize().
162
segments = base.split('/')
163
if segments and segments[-1] not in ('', '.'):
164
parent = '/'.join(segments[:-1])
165
t = bzrlib.transport.get_transport(parent)
167
t.mkdir(segments[-1])
168
except errors.FileExists:
170
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
172
def create_branch(self):
173
"""Create a branch in this BzrDir.
175
The bzrdirs format will control what branch format is created.
176
For more control see BranchFormatXX.create(a_bzrdir).
178
raise NotImplementedError(self.create_branch)
181
def create_branch_and_repo(base, force_new_repo=False):
182
"""Create a new BzrDir, Branch and Repository at the url 'base'.
184
This will use the current default BzrDirFormat, and use whatever
185
repository format that that uses via bzrdir.create_branch and
186
create_repository. If a shared repository is available that is used
189
The created Branch object is returned.
191
:param base: The URL to create the branch at.
192
:param force_new_repo: If True a new repository is always created.
194
bzrdir = BzrDir.create(base)
195
bzrdir._find_or_create_repository(force_new_repo)
196
return bzrdir.create_branch()
198
def _find_or_create_repository(self, force_new_repo):
199
"""Create a new repository if needed, returning the repository."""
201
return self.create_repository()
203
return self.find_repository()
204
except errors.NoRepositoryPresent:
205
return self.create_repository()
208
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
209
"""Create a new BzrDir, Branch and Repository at the url 'base'.
211
This is a convenience function - it will use an existing repository
212
if possible, can be told explicitly whether to create a working tree or
215
This will use the current default BzrDirFormat, and use whatever
216
repository format that that uses via bzrdir.create_branch and
217
create_repository. If a shared repository is available that is used
218
preferentially. Whatever repository is used, its tree creation policy
221
The created Branch object is returned.
222
If a working tree cannot be made due to base not being a file:// url,
223
no error is raised unless force_new_tree is True, in which case no
224
data is created on disk and NotLocalUrl is raised.
226
:param base: The URL to create the branch at.
227
:param force_new_repo: If True a new repository is always created.
228
:param force_new_tree: If True or False force creation of a tree or
229
prevent such creation respectively.
232
# check for non local urls
233
t = get_transport(safe_unicode(base))
234
if not isinstance(t, LocalTransport):
235
raise errors.NotLocalUrl(base)
236
bzrdir = BzrDir.create(base)
237
repo = bzrdir._find_or_create_repository(force_new_repo)
238
result = bzrdir.create_branch()
239
if force_new_tree or (repo.make_working_trees() and
240
force_new_tree is None):
242
bzrdir.create_workingtree()
243
except errors.NotLocalUrl:
248
def create_repository(base, shared=False):
249
"""Create a new BzrDir and Repository at the url 'base'.
251
This will use the current default BzrDirFormat, and use whatever
252
repository format that that uses for bzrdirformat.create_repository.
254
;param shared: Create a shared repository rather than a standalone
256
The Repository object is returned.
258
This must be overridden as an instance method in child classes, where
259
it should take no parameters and construct whatever repository format
260
that child class desires.
262
bzrdir = BzrDir.create(base)
263
return bzrdir.create_repository()
266
def create_standalone_workingtree(base):
267
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
269
'base' must be a local path or a file:// url.
271
This will use the current default BzrDirFormat, and use whatever
272
repository format that that uses for bzrdirformat.create_workingtree,
273
create_branch and create_repository.
275
The WorkingTree object is returned.
277
t = get_transport(safe_unicode(base))
278
if not isinstance(t, LocalTransport):
279
raise errors.NotLocalUrl(base)
280
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
281
force_new_repo=True).bzrdir
282
return bzrdir.create_workingtree()
284
def create_workingtree(self, revision_id=None):
285
"""Create a working tree at this BzrDir.
287
revision_id: create it as of this revision id.
289
raise NotImplementedError(self.create_workingtree)
291
def find_repository(self):
292
"""Find the repository that should be used for a_bzrdir.
294
This does not require a branch as we use it to find the repo for
295
new branches as well as to hook existing branches up to their
299
return self.open_repository()
300
except errors.NoRepositoryPresent:
302
next_transport = self.root_transport.clone('..')
305
found_bzrdir = BzrDir.open_containing_from_transport(
307
except errors.NotBranchError:
308
raise errors.NoRepositoryPresent(self)
310
repository = found_bzrdir.open_repository()
311
except errors.NoRepositoryPresent:
312
next_transport = found_bzrdir.root_transport.clone('..')
314
if ((found_bzrdir.root_transport.base ==
315
self.root_transport.base) or repository.is_shared()):
318
raise errors.NoRepositoryPresent(self)
319
raise errors.NoRepositoryPresent(self)
321
def get_branch_transport(self, branch_format):
322
"""Get the transport for use by branch format in this BzrDir.
324
Note that bzr dirs that do not support format strings will raise
325
IncompatibleFormat if the branch format they are given has
326
a format string, and vice verca.
328
If branch_format is None, the transport is returned with no
329
checking. if it is not None, then the returned transport is
330
guaranteed to point to an existing directory ready for use.
332
raise NotImplementedError(self.get_branch_transport)
334
def get_repository_transport(self, repository_format):
335
"""Get the transport for use by repository format in this BzrDir.
337
Note that bzr dirs that do not support format strings will raise
338
IncompatibleFormat if the repository format they are given has
339
a format string, and vice verca.
341
If repository_format is None, the transport is returned with no
342
checking. if it is not None, then the returned transport is
343
guaranteed to point to an existing directory ready for use.
345
raise NotImplementedError(self.get_repository_transport)
347
def get_workingtree_transport(self, tree_format):
348
"""Get the transport for use by workingtree format in this BzrDir.
350
Note that bzr dirs that do not support format strings will raise
351
IncompatibleFormat if the workingtree format they are given has
352
a format string, and vice verca.
354
If workingtree_format is None, the transport is returned with no
355
checking. if it is not None, then the returned transport is
356
guaranteed to point to an existing directory ready for use.
358
raise NotImplementedError(self.get_workingtree_transport)
360
def __init__(self, _transport, _format):
361
"""Initialize a Bzr control dir object.
363
Only really common logic should reside here, concrete classes should be
364
made with varying behaviours.
366
:param _format: the format that is creating this BzrDir instance.
367
:param _transport: the transport this dir is based at.
369
self._format = _format
370
self.transport = _transport.clone('.bzr')
371
self.root_transport = _transport
373
def needs_format_conversion(self, format=None):
374
"""Return true if this bzrdir needs convert_format run on it.
376
For instance, if the repository format is out of date but the
377
branch and working tree are not, this should return True.
379
:param format: Optional parameter indicating a specific desired
380
format we plan to arrive at.
382
raise NotImplementedError(self.needs_format_conversion)
385
def open_unsupported(base):
386
"""Open a branch which is not supported."""
387
return BzrDir.open(base, _unsupported=True)
390
def open(base, _unsupported=False):
391
"""Open an existing bzrdir, rooted at 'base' (url)
393
_unsupported is a private parameter to the BzrDir class.
395
t = get_transport(base)
396
mutter("trying to open %r with transport %r", base, t)
397
format = BzrDirFormat.find_format(t)
398
if not _unsupported and not format.is_supported():
399
# see open_downlevel to open legacy branches.
400
raise errors.UnsupportedFormatError(
401
'sorry, format %s not supported' % format,
402
['use a different bzr version',
403
'or remove the .bzr directory'
404
' and "bzr init" again'])
405
return format.open(t, _found=True)
407
def open_branch(self, unsupported=False):
408
"""Open the branch object at this BzrDir if one is present.
410
If unsupported is True, then no longer supported branch formats can
413
TODO: static convenience version of this?
415
raise NotImplementedError(self.open_branch)
418
def open_containing(url):
419
"""Open an existing branch which contains url.
421
:param url: url to search from.
422
See open_containing_from_transport for more detail.
424
return BzrDir.open_containing_from_transport(get_transport(url))
427
def open_containing_from_transport(a_transport):
428
"""Open an existing branch which contains a_transport.base
430
This probes for a branch at a_transport, and searches upwards from there.
432
Basically we keep looking up until we find the control directory or
433
run into the root. If there isn't one, raises NotBranchError.
434
If there is one and it is either an unrecognised format or an unsupported
435
format, UnknownFormatError or UnsupportedFormatError are raised.
436
If there is one, it is returned, along with the unused portion of url.
438
# this gets the normalised url back. I.e. '.' -> the full path.
439
url = a_transport.base
442
format = BzrDirFormat.find_format(a_transport)
443
return format.open(a_transport), a_transport.relpath(url)
444
except errors.NotBranchError, e:
445
mutter('not a branch in: %r %s', a_transport.base, e)
446
new_t = a_transport.clone('..')
447
if new_t.base == a_transport.base:
448
# reached the root, whatever that may be
449
raise errors.NotBranchError(path=url)
452
def open_repository(self, _unsupported=False):
453
"""Open the repository object at this BzrDir if one is present.
455
This will not follow the Branch object pointer - its strictly a direct
456
open facility. Most client code should use open_branch().repository to
459
_unsupported is a private parameter, not part of the api.
460
TODO: static convenience version of this?
462
raise NotImplementedError(self.open_repository)
464
def open_workingtree(self, _unsupported=False):
465
"""Open the workingtree object at this BzrDir if one is present.
467
TODO: static convenience version of this?
469
raise NotImplementedError(self.open_workingtree)
471
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
472
"""Create a copy of this bzrdir prepared for use as a new line of
475
If urls last component does not exist, it will be created.
477
Attributes related to the identity of the source branch like
478
branch nickname will be cleaned, a working tree is created
479
whether one existed before or not; and a local branch is always
482
if revision_id is not None, then the clone operation may tune
483
itself to download less data.
486
result = self._format.initialize(url)
487
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
489
source_branch = self.open_branch()
490
source_repository = source_branch.repository
491
except errors.NotBranchError:
494
source_repository = self.open_repository()
495
except errors.NoRepositoryPresent:
496
# copy the entire basis one if there is one
497
# but there is no repository.
498
source_repository = basis_repo
503
result_repo = result.find_repository()
504
except errors.NoRepositoryPresent:
506
if source_repository is None and result_repo is not None:
508
elif source_repository is None and result_repo is None:
509
# no repo available, make a new one
510
result.create_repository()
511
elif source_repository is not None and result_repo is None:
512
# have soure, and want to make a new target repo
513
source_repository.clone(result,
514
revision_id=revision_id,
517
# fetch needed content into target.
519
# XXX FIXME RBC 20060214 need tests for this when the basis
521
result_repo.fetch(basis_repo, revision_id=revision_id)
522
result_repo.fetch(source_repository, revision_id=revision_id)
523
if source_branch is not None:
524
source_branch.sprout(result, revision_id=revision_id)
526
result.create_branch()
528
self.open_workingtree().clone(result,
529
revision_id=revision_id,
531
except (errors.NoWorkingTree, errors.NotLocalUrl):
532
result.create_workingtree()
536
class BzrDirPreSplitOut(BzrDir):
537
"""A common class for the all-in-one formats."""
539
def __init__(self, _transport, _format):
540
"""See BzrDir.__init__."""
541
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
542
self._control_files = LockableFiles(self.get_branch_transport(None),
545
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
546
"""See BzrDir.clone()."""
547
from bzrlib.workingtree import WorkingTreeFormat2
549
result = self._format.initialize(url, _cloning=True)
550
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
551
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
552
self.open_branch().clone(result, revision_id=revision_id)
554
self.open_workingtree().clone(result, basis=basis_tree)
555
except errors.NotLocalUrl:
556
# make a new one, this format always has to have one.
557
WorkingTreeFormat2().initialize(result)
560
def create_branch(self):
561
"""See BzrDir.create_branch."""
562
return self.open_branch()
564
def create_repository(self, shared=False):
565
"""See BzrDir.create_repository."""
567
raise errors.IncompatibleFormat('shared repository', self._format)
568
return self.open_repository()
570
def create_workingtree(self, revision_id=None):
571
"""See BzrDir.create_workingtree."""
572
# this looks buggy but is not -really-
573
# clone and sprout will have set the revision_id
574
# and that will have set it for us, its only
575
# specific uses of create_workingtree in isolation
576
# that can do wonky stuff here, and that only
577
# happens for creating checkouts, which cannot be
578
# done on this format anyway. So - acceptable wart.
579
result = self.open_workingtree()
580
if revision_id is not None:
581
result.set_last_revision(revision_id)
584
def get_branch_transport(self, branch_format):
585
"""See BzrDir.get_branch_transport()."""
586
if branch_format is None:
587
return self.transport
589
branch_format.get_format_string()
590
except NotImplementedError:
591
return self.transport
592
raise errors.IncompatibleFormat(branch_format, self._format)
594
def get_repository_transport(self, repository_format):
595
"""See BzrDir.get_repository_transport()."""
596
if repository_format is None:
597
return self.transport
599
repository_format.get_format_string()
600
except NotImplementedError:
601
return self.transport
602
raise errors.IncompatibleFormat(repository_format, self._format)
604
def get_workingtree_transport(self, workingtree_format):
605
"""See BzrDir.get_workingtree_transport()."""
606
if workingtree_format is None:
607
return self.transport
609
workingtree_format.get_format_string()
610
except NotImplementedError:
611
return self.transport
612
raise errors.IncompatibleFormat(workingtree_format, self._format)
614
def needs_format_conversion(self, format=None):
615
"""See BzrDir.needs_format_conversion()."""
616
# if the format is not the same as the system default,
617
# an upgrade is needed.
619
format = BzrDirFormat.get_default_format()
620
return not isinstance(self._format, format.__class__)
622
def open_branch(self, unsupported=False):
623
"""See BzrDir.open_branch."""
624
from bzrlib.branch import BzrBranchFormat4
625
format = BzrBranchFormat4()
626
self._check_supported(format, unsupported)
627
return format.open(self, _found=True)
629
def sprout(self, url, revision_id=None, basis=None):
630
"""See BzrDir.sprout()."""
631
from bzrlib.workingtree import WorkingTreeFormat2
633
result = self._format.initialize(url, _cloning=True)
634
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
636
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
637
except errors.NoRepositoryPresent:
640
self.open_branch().sprout(result, revision_id=revision_id)
641
except errors.NotBranchError:
644
self.open_workingtree().clone(result, basis=basis_tree)
645
except (errors.NotBranchError, errors.NotLocalUrl):
646
# we always want a working tree
647
WorkingTreeFormat2().initialize(result)
651
class BzrDir4(BzrDirPreSplitOut):
652
"""A .bzr version 4 control object.
654
This is a deprecated format and may be removed after sept 2006.
657
def create_repository(self, shared=False):
658
"""See BzrDir.create_repository."""
659
return self._format.repository_format.initialize(self, shared)
661
def needs_format_conversion(self, format=None):
662
"""Format 4 dirs are always in need of conversion."""
665
def open_repository(self):
666
"""See BzrDir.open_repository."""
667
from bzrlib.repository import RepositoryFormat4
668
return RepositoryFormat4().open(self, _found=True)
671
class BzrDir5(BzrDirPreSplitOut):
672
"""A .bzr version 5 control object.
674
This is a deprecated format and may be removed after sept 2006.
677
def open_repository(self):
678
"""See BzrDir.open_repository."""
679
from bzrlib.repository import RepositoryFormat5
680
return RepositoryFormat5().open(self, _found=True)
682
def open_workingtree(self, _unsupported=False):
683
"""See BzrDir.create_workingtree."""
684
from bzrlib.workingtree import WorkingTreeFormat2
685
return WorkingTreeFormat2().open(self, _found=True)
688
class BzrDir6(BzrDirPreSplitOut):
689
"""A .bzr version 6 control object.
691
This is a deprecated format and may be removed after sept 2006.
694
def open_repository(self):
695
"""See BzrDir.open_repository."""
696
from bzrlib.repository import RepositoryFormat6
697
return RepositoryFormat6().open(self, _found=True)
699
def open_workingtree(self, _unsupported=False):
700
"""See BzrDir.create_workingtree."""
701
from bzrlib.workingtree import WorkingTreeFormat2
702
return WorkingTreeFormat2().open(self, _found=True)
705
class BzrDirMeta1(BzrDir):
706
"""A .bzr meta version 1 control object.
708
This is the first control object where the
709
individual formats are really split out.
712
def can_convert_format(self):
713
"""See BzrDir.can_convert_format()."""
716
def create_branch(self):
717
"""See BzrDir.create_branch."""
718
from bzrlib.branch import BranchFormat
719
return BranchFormat.get_default_format().initialize(self)
721
def create_repository(self, shared=False):
722
"""See BzrDir.create_repository."""
723
return self._format.repository_format.initialize(self, shared)
725
def create_workingtree(self, revision_id=None):
726
"""See BzrDir.create_workingtree."""
727
from bzrlib.workingtree import WorkingTreeFormat
728
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
730
def get_branch_transport(self, branch_format):
731
"""See BzrDir.get_branch_transport()."""
732
if branch_format is None:
733
return self.transport.clone('branch')
735
branch_format.get_format_string()
736
except NotImplementedError:
737
raise errors.IncompatibleFormat(branch_format, self._format)
739
self.transport.mkdir('branch')
740
except errors.FileExists:
742
return self.transport.clone('branch')
744
def get_repository_transport(self, repository_format):
745
"""See BzrDir.get_repository_transport()."""
746
if repository_format is None:
747
return self.transport.clone('repository')
749
repository_format.get_format_string()
750
except NotImplementedError:
751
raise errors.IncompatibleFormat(repository_format, self._format)
753
self.transport.mkdir('repository')
754
except errors.FileExists:
756
return self.transport.clone('repository')
758
def get_workingtree_transport(self, workingtree_format):
759
"""See BzrDir.get_workingtree_transport()."""
760
if workingtree_format is None:
761
return self.transport.clone('checkout')
763
workingtree_format.get_format_string()
764
except NotImplementedError:
765
raise errors.IncompatibleFormat(workingtree_format, self._format)
767
self.transport.mkdir('checkout')
768
except errors.FileExists:
770
return self.transport.clone('checkout')
772
def needs_format_conversion(self, format=None):
773
"""See BzrDir.needs_format_conversion()."""
775
format = BzrDirFormat.get_default_format()
776
if not isinstance(self._format, format.__class__):
777
# it is not a meta dir format, conversion is needed.
779
# we might want to push this down to the repository?
781
if not isinstance(self.open_repository()._format,
782
format.repository_format.__class__):
783
# the repository needs an upgrade.
785
except errors.NoRepositoryPresent:
787
# currently there are no other possible conversions for meta1 formats.
790
def open_branch(self, unsupported=False):
791
"""See BzrDir.open_branch."""
792
from bzrlib.branch import BranchFormat
793
format = BranchFormat.find_format(self)
794
self._check_supported(format, unsupported)
795
return format.open(self, _found=True)
797
def open_repository(self, unsupported=False):
798
"""See BzrDir.open_repository."""
799
from bzrlib.repository import RepositoryFormat
800
format = RepositoryFormat.find_format(self)
801
self._check_supported(format, unsupported)
802
return format.open(self, _found=True)
804
def open_workingtree(self, unsupported=False):
805
"""See BzrDir.open_workingtree."""
806
from bzrlib.workingtree import WorkingTreeFormat
807
format = WorkingTreeFormat.find_format(self)
808
self._check_supported(format, unsupported)
809
return format.open(self, _found=True)
812
class BzrDirFormat(object):
813
"""An encapsulation of the initialization and open routines for a format.
815
Formats provide three things:
816
* An initialization routine,
820
Formats are placed in an dict by their format string for reference
821
during bzrdir opening. These should be subclasses of BzrDirFormat
824
Once a format is deprecated, just deprecate the initialize and open
825
methods on the format class. Do not deprecate the object, as the
826
object will be created every system load.
829
_default_format = None
830
"""The default format used for new .bzr dirs."""
833
"""The known formats."""
836
def find_format(klass, transport):
837
"""Return the format registered for URL."""
839
format_string = transport.get(".bzr/branch-format").read()
840
return klass._formats[format_string]
841
except errors.NoSuchFile:
842
raise errors.NotBranchError(path=transport.base)
844
raise errors.UnknownFormatError(format_string)
847
def get_default_format(klass):
848
"""Return the current default format."""
849
return klass._default_format
851
def get_format_string(self):
852
"""Return the ASCII format string that identifies this format."""
853
raise NotImplementedError(self.get_format_string)
855
def get_converter(self, format=None):
856
"""Return the converter to use to convert bzrdirs needing converts.
858
This returns a bzrlib.bzrdir.Converter object.
860
This should return the best upgrader to step this format towards the
861
current default format. In the case of plugins we can/shouold provide
862
some means for them to extend the range of returnable converters.
864
:param format: Optional format to override the default foramt of the
867
raise NotImplementedError(self.get_converter)
869
def initialize(self, url):
870
"""Create a bzr control dir at this url and return an opened copy."""
871
# Since we don't have a .bzr directory, inherit the
872
# mode from the root directory
873
t = get_transport(url)
874
temp_control = LockableFiles(t, '')
875
temp_control._transport.mkdir('.bzr',
876
# FIXME: RBC 20060121 dont peek under
878
mode=temp_control._dir_mode)
879
file_mode = temp_control._file_mode
881
mutter('created control directory in ' + t.base)
882
control = t.clone('.bzr')
883
lock_file = 'branch-lock'
884
utf8_files = [('README',
885
"This is a Bazaar-NG control directory.\n"
886
"Do not change any files in this directory.\n"),
887
('branch-format', self.get_format_string()),
889
# NB: no need to escape relative paths that are url safe.
890
control.put(lock_file, StringIO(), mode=file_mode)
891
control_files = LockableFiles(control, lock_file)
892
control_files.lock_write()
894
for file, content in utf8_files:
895
control_files.put_utf8(file, content)
897
control_files.unlock()
898
return self.open(t, _found=True)
900
def is_supported(self):
901
"""Is this format supported?
903
Supported formats must be initializable and openable.
904
Unsupported formats may not support initialization or committing or
905
some other features depending on the reason for not being supported.
909
def open(self, transport, _found=False):
910
"""Return an instance of this format for the dir transport points at.
912
_found is a private parameter, do not use it.
915
assert isinstance(BzrDirFormat.find_format(transport),
917
return self._open(transport)
919
def _open(self, transport):
920
"""Template method helper for opening BzrDirectories.
922
This performs the actual open and any additional logic or parameter
925
raise NotImplementedError(self._open)
928
def register_format(klass, format):
929
klass._formats[format.get_format_string()] = format
932
def set_default_format(klass, format):
933
klass._default_format = format
936
return self.get_format_string()[:-1]
939
def unregister_format(klass, format):
940
assert klass._formats[format.get_format_string()] is format
941
del klass._formats[format.get_format_string()]
944
class BzrDirFormat4(BzrDirFormat):
947
This format is a combined format for working tree, branch and repository.
949
- Format 1 working trees [always]
950
- Format 4 branches [always]
951
- Format 4 repositories [always]
953
This format is deprecated: it indexes texts using a text it which is
954
removed in format 5; write support for this format has been removed.
957
def get_format_string(self):
958
"""See BzrDirFormat.get_format_string()."""
959
return "Bazaar-NG branch, format 0.0.4\n"
961
def get_converter(self, format=None):
962
"""See BzrDirFormat.get_converter()."""
963
# there is one and only one upgrade path here.
964
return ConvertBzrDir4To5()
966
def initialize(self, url):
967
"""Format 4 branches cannot be created."""
968
raise errors.UninitializableFormat(self)
970
def is_supported(self):
971
"""Format 4 is not supported.
973
It is not supported because the model changed from 4 to 5 and the
974
conversion logic is expensive - so doing it on the fly was not
979
def _open(self, transport):
980
"""See BzrDirFormat._open."""
981
return BzrDir4(transport, self)
983
def __return_repository_format(self):
984
"""Circular import protection."""
985
from bzrlib.repository import RepositoryFormat4
986
return RepositoryFormat4(self)
987
repository_format = property(__return_repository_format)
990
class BzrDirFormat5(BzrDirFormat):
991
"""Bzr control format 5.
993
This format is a combined format for working tree, branch and repository.
995
- Format 2 working trees [always]
996
- Format 4 branches [always]
997
- Format 5 repositories [always]
998
Unhashed stores in the repository.
1001
def get_format_string(self):
1002
"""See BzrDirFormat.get_format_string()."""
1003
return "Bazaar-NG branch, format 5\n"
1005
def get_converter(self, format=None):
1006
"""See BzrDirFormat.get_converter()."""
1007
# there is one and only one upgrade path here.
1008
return ConvertBzrDir5To6()
1010
def initialize(self, url, _cloning=False):
1011
"""Format 5 dirs always have working tree, branch and repository.
1013
Except when they are being cloned.
1015
from bzrlib.branch import BzrBranchFormat4
1016
from bzrlib.repository import RepositoryFormat5
1017
from bzrlib.workingtree import WorkingTreeFormat2
1018
result = super(BzrDirFormat5, self).initialize(url)
1019
RepositoryFormat5().initialize(result, _internal=True)
1021
BzrBranchFormat4().initialize(result)
1022
WorkingTreeFormat2().initialize(result)
1025
def _open(self, transport):
1026
"""See BzrDirFormat._open."""
1027
return BzrDir5(transport, self)
1029
def __return_repository_format(self):
1030
"""Circular import protection."""
1031
from bzrlib.repository import RepositoryFormat5
1032
return RepositoryFormat5(self)
1033
repository_format = property(__return_repository_format)
1036
class BzrDirFormat6(BzrDirFormat):
1037
"""Bzr control format 6.
1039
This format is a combined format for working tree, branch and repository.
1041
- Format 2 working trees [always]
1042
- Format 4 branches [always]
1043
- Format 6 repositories [always]
1046
def get_format_string(self):
1047
"""See BzrDirFormat.get_format_string()."""
1048
return "Bazaar-NG branch, format 6\n"
1050
def get_converter(self, format=None):
1051
"""See BzrDirFormat.get_converter()."""
1052
# there is one and only one upgrade path here.
1053
return ConvertBzrDir6ToMeta()
1055
def initialize(self, url, _cloning=False):
1056
"""Format 6 dirs always have working tree, branch and repository.
1058
Except when they are being cloned.
1060
from bzrlib.branch import BzrBranchFormat4
1061
from bzrlib.repository import RepositoryFormat6
1062
from bzrlib.workingtree import WorkingTreeFormat2
1063
result = super(BzrDirFormat6, self).initialize(url)
1064
RepositoryFormat6().initialize(result, _internal=True)
1066
BzrBranchFormat4().initialize(result)
1068
WorkingTreeFormat2().initialize(result)
1069
except errors.NotLocalUrl:
1070
# emulate pre-check behaviour for working tree and silently
1075
def _open(self, transport):
1076
"""See BzrDirFormat._open."""
1077
return BzrDir6(transport, self)
1079
def __return_repository_format(self):
1080
"""Circular import protection."""
1081
from bzrlib.repository import RepositoryFormat6
1082
return RepositoryFormat6(self)
1083
repository_format = property(__return_repository_format)
1086
class BzrDirMetaFormat1(BzrDirFormat):
1087
"""Bzr meta control format 1
1089
This is the first format with split out working tree, branch and repository
1092
- Format 3 working trees [optional]
1093
- Format 5 branches [optional]
1094
- Format 7 repositories [optional]
1097
def get_converter(self, format=None):
1098
"""See BzrDirFormat.get_converter()."""
1100
format = BzrDirFormat.get_default_format()
1101
if not isinstance(self, format.__class__):
1102
# converting away from metadir is not implemented
1103
raise NotImplementedError(self.get_converter)
1104
return ConvertMetaToMeta(format)
1106
def get_format_string(self):
1107
"""See BzrDirFormat.get_format_string()."""
1108
return "Bazaar-NG meta directory, format 1\n"
1110
def _open(self, transport):
1111
"""See BzrDirFormat._open."""
1112
return BzrDirMeta1(transport, self)
1114
def __return_repository_format(self):
1115
"""Circular import protection."""
1116
if getattr(self, '_repository_format', None):
1117
return self._repository_format
1118
from bzrlib.repository import RepositoryFormat
1119
return RepositoryFormat.get_default_format()
1121
def __set_repository_format(self, value):
1122
"""Allow changint the repository format for metadir formats."""
1123
self._repository_format = value
1124
repository_format = property(__return_repository_format, __set_repository_format)
1127
BzrDirFormat.register_format(BzrDirFormat4())
1128
BzrDirFormat.register_format(BzrDirFormat5())
1129
BzrDirFormat.register_format(BzrDirMetaFormat1())
1130
__default_format = BzrDirFormat6()
1131
BzrDirFormat.register_format(__default_format)
1132
BzrDirFormat.set_default_format(__default_format)
1135
class BzrDirTestProviderAdapter(object):
1136
"""A tool to generate a suite testing multiple bzrdir formats at once.
1138
This is done by copying the test once for each transport and injecting
1139
the transport_server, transport_readonly_server, and bzrdir_format
1140
classes into each copy. Each copy is also given a new id() to make it
1144
def __init__(self, transport_server, transport_readonly_server, formats):
1145
self._transport_server = transport_server
1146
self._transport_readonly_server = transport_readonly_server
1147
self._formats = formats
1149
def adapt(self, test):
1150
result = TestSuite()
1151
for format in self._formats:
1152
new_test = deepcopy(test)
1153
new_test.transport_server = self._transport_server
1154
new_test.transport_readonly_server = self._transport_readonly_server
1155
new_test.bzrdir_format = format
1156
def make_new_test_id():
1157
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1158
return lambda: new_id
1159
new_test.id = make_new_test_id()
1160
result.addTest(new_test)
1164
class ScratchDir(BzrDir6):
1165
"""Special test class: a bzrdir that cleans up itself..
1167
>>> d = ScratchDir()
1168
>>> base = d.transport.base
1171
>>> b.transport.__del__()
1176
def __init__(self, files=[], dirs=[], transport=None):
1177
"""Make a test branch.
1179
This creates a temporary directory and runs init-tree in it.
1181
If any files are listed, they are created in the working copy.
1183
if transport is None:
1184
transport = bzrlib.transport.local.ScratchTransport()
1185
# local import for scope restriction
1186
BzrDirFormat6().initialize(transport.base)
1187
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1188
self.create_repository()
1189
self.create_branch()
1190
self.create_workingtree()
1192
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1194
# BzrBranch creates a clone to .bzr and then forgets about the
1195
# original transport. A ScratchTransport() deletes itself and
1196
# everything underneath it when it goes away, so we need to
1197
# grab a local copy to prevent that from happening
1198
self._transport = transport
1201
self._transport.mkdir(d)
1204
self._transport.put(f, 'content of %s' % f)
1208
>>> orig = ScratchDir(files=["file1", "file2"])
1209
>>> os.listdir(orig.base)
1210
[u'.bzr', u'file1', u'file2']
1211
>>> clone = orig.clone()
1212
>>> if os.name != 'nt':
1213
... os.path.samefile(orig.base, clone.base)
1215
... orig.base == clone.base
1218
>>> os.listdir(clone.base)
1219
[u'.bzr', u'file1', u'file2']
1221
from shutil import copytree
1222
from bzrlib.osutils import mkdtemp
1225
copytree(self.base, base, symlinks=True)
1227
transport=bzrlib.transport.local.ScratchTransport(base))
1230
class Converter(object):
1231
"""Converts a disk format object from one format to another."""
1233
def convert(self, to_convert, pb):
1234
"""Perform the conversion of to_convert, giving feedback via pb.
1236
:param to_convert: The disk object to convert.
1237
:param pb: a progress bar to use for progress information.
1240
def step(self, message):
1241
"""Update the pb by a step."""
1243
self.pb.update(message, self.count, self.total)
1246
class ConvertBzrDir4To5(Converter):
1247
"""Converts format 4 bzr dirs to format 5."""
1250
super(ConvertBzrDir4To5, self).__init__()
1251
self.converted_revs = set()
1252
self.absent_revisions = set()
1256
def convert(self, to_convert, pb):
1257
"""See Converter.convert()."""
1258
self.bzrdir = to_convert
1260
self.pb.note('starting upgrade from format 4 to 5')
1261
if isinstance(self.bzrdir.transport, LocalTransport):
1262
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1263
self._convert_to_weaves()
1264
return BzrDir.open(self.bzrdir.root_transport.base)
1266
def _convert_to_weaves(self):
1267
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1270
stat = self.bzrdir.transport.stat('weaves')
1271
if not S_ISDIR(stat.st_mode):
1272
self.bzrdir.transport.delete('weaves')
1273
self.bzrdir.transport.mkdir('weaves')
1274
except errors.NoSuchFile:
1275
self.bzrdir.transport.mkdir('weaves')
1276
# deliberately not a WeaveFile as we want to build it up slowly.
1277
self.inv_weave = Weave('inventory')
1278
# holds in-memory weaves for all files
1279
self.text_weaves = {}
1280
self.bzrdir.transport.delete('branch-format')
1281
self.branch = self.bzrdir.open_branch()
1282
self._convert_working_inv()
1283
rev_history = self.branch.revision_history()
1284
# to_read is a stack holding the revisions we still need to process;
1285
# appending to it adds new highest-priority revisions
1286
self.known_revisions = set(rev_history)
1287
self.to_read = rev_history[-1:]
1289
rev_id = self.to_read.pop()
1290
if (rev_id not in self.revisions
1291
and rev_id not in self.absent_revisions):
1292
self._load_one_rev(rev_id)
1294
to_import = self._make_order()
1295
for i, rev_id in enumerate(to_import):
1296
self.pb.update('converting revision', i, len(to_import))
1297
self._convert_one_rev(rev_id)
1299
self._write_all_weaves()
1300
self._write_all_revs()
1301
self.pb.note('upgraded to weaves:')
1302
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1303
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1304
self.pb.note(' %6d texts', self.text_count)
1305
self._cleanup_spare_files_after_format4()
1306
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1308
def _cleanup_spare_files_after_format4(self):
1309
# FIXME working tree upgrade foo.
1310
for n in 'merged-patches', 'pending-merged-patches':
1312
## assert os.path.getsize(p) == 0
1313
self.bzrdir.transport.delete(n)
1314
except errors.NoSuchFile:
1316
self.bzrdir.transport.delete_tree('inventory-store')
1317
self.bzrdir.transport.delete_tree('text-store')
1319
def _convert_working_inv(self):
1320
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1321
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1322
# FIXME inventory is a working tree change.
1323
self.branch.control_files.put('inventory', new_inv_xml)
1325
def _write_all_weaves(self):
1326
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1327
weave_transport = self.bzrdir.transport.clone('weaves')
1328
weaves = WeaveStore(weave_transport, prefixed=False)
1329
transaction = PassThroughTransaction()
1333
for file_id, file_weave in self.text_weaves.items():
1334
self.pb.update('writing weave', i, len(self.text_weaves))
1335
weaves._put_weave(file_id, file_weave, transaction)
1337
self.pb.update('inventory', 0, 1)
1338
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1339
self.pb.update('inventory', 1, 1)
1343
def _write_all_revs(self):
1344
"""Write all revisions out in new form."""
1345
self.bzrdir.transport.delete_tree('revision-store')
1346
self.bzrdir.transport.mkdir('revision-store')
1347
revision_transport = self.bzrdir.transport.clone('revision-store')
1349
revision_store = TextStore(revision_transport,
1353
for i, rev_id in enumerate(self.converted_revs):
1354
self.pb.update('write revision', i, len(self.converted_revs))
1355
rev_tmp = StringIO()
1356
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1358
revision_store.add(rev_tmp, rev_id)
1362
def _load_one_rev(self, rev_id):
1363
"""Load a revision object into memory.
1365
Any parents not either loaded or abandoned get queued to be
1367
self.pb.update('loading revision',
1368
len(self.revisions),
1369
len(self.known_revisions))
1370
if not self.branch.repository.revision_store.has_id(rev_id):
1372
self.pb.note('revision {%s} not present in branch; '
1373
'will be converted as a ghost',
1375
self.absent_revisions.add(rev_id)
1377
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1378
rev = serializer_v4.read_revision_from_string(rev_xml)
1379
for parent_id in rev.parent_ids:
1380
self.known_revisions.add(parent_id)
1381
self.to_read.append(parent_id)
1382
self.revisions[rev_id] = rev
1384
def _load_old_inventory(self, rev_id):
1385
assert rev_id not in self.converted_revs
1386
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1387
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1388
rev = self.revisions[rev_id]
1389
if rev.inventory_sha1:
1390
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1391
'inventory sha mismatch for {%s}' % rev_id
1394
def _load_updated_inventory(self, rev_id):
1395
assert rev_id in self.converted_revs
1396
inv_xml = self.inv_weave.get_text(rev_id)
1397
inv = serializer_v5.read_inventory_from_string(inv_xml)
1400
def _convert_one_rev(self, rev_id):
1401
"""Convert revision and all referenced objects to new format."""
1402
rev = self.revisions[rev_id]
1403
inv = self._load_old_inventory(rev_id)
1404
present_parents = [p for p in rev.parent_ids
1405
if p not in self.absent_revisions]
1406
self._convert_revision_contents(rev, inv, present_parents)
1407
self._store_new_weave(rev, inv, present_parents)
1408
self.converted_revs.add(rev_id)
1410
def _store_new_weave(self, rev, inv, present_parents):
1411
# the XML is now updated with text versions
1415
if ie.kind == 'root_directory':
1417
assert hasattr(ie, 'revision'), \
1418
'no revision on {%s} in {%s}' % \
1419
(file_id, rev.revision_id)
1420
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1421
new_inv_sha1 = sha_string(new_inv_xml)
1422
self.inv_weave.add(rev.revision_id,
1424
new_inv_xml.splitlines(True),
1426
rev.inventory_sha1 = new_inv_sha1
1428
def _convert_revision_contents(self, rev, inv, present_parents):
1429
"""Convert all the files within a revision.
1431
Also upgrade the inventory to refer to the text revision ids."""
1432
rev_id = rev.revision_id
1433
mutter('converting texts of revision {%s}',
1435
parent_invs = map(self._load_updated_inventory, present_parents)
1438
self._convert_file_version(rev, ie, parent_invs)
1440
def _convert_file_version(self, rev, ie, parent_invs):
1441
"""Convert one version of one file.
1443
The file needs to be added into the weave if it is a merge
1444
of >=2 parents or if it's changed from its parent.
1446
if ie.kind == 'root_directory':
1448
file_id = ie.file_id
1449
rev_id = rev.revision_id
1450
w = self.text_weaves.get(file_id)
1453
self.text_weaves[file_id] = w
1454
text_changed = False
1455
previous_entries = ie.find_previous_heads(parent_invs, w)
1456
for old_revision in previous_entries:
1457
# if this fails, its a ghost ?
1458
assert old_revision in self.converted_revs
1459
self.snapshot_ie(previous_entries, ie, w, rev_id)
1461
assert getattr(ie, 'revision', None) is not None
1463
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1464
# TODO: convert this logic, which is ~= snapshot to
1465
# a call to:. This needs the path figured out. rather than a work_tree
1466
# a v4 revision_tree can be given, or something that looks enough like
1467
# one to give the file content to the entry if it needs it.
1468
# and we need something that looks like a weave store for snapshot to
1470
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1471
if len(previous_revisions) == 1:
1472
previous_ie = previous_revisions.values()[0]
1473
if ie._unchanged(previous_ie):
1474
ie.revision = previous_ie.revision
1476
parent_indexes = map(w.lookup, previous_revisions)
1478
text = self.branch.repository.text_store.get(ie.text_id)
1479
file_lines = text.readlines()
1480
assert sha_strings(file_lines) == ie.text_sha1
1481
assert sum(map(len, file_lines)) == ie.text_size
1482
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1483
self.text_count += 1
1485
w.add(rev_id, parent_indexes, [], None)
1486
ie.revision = rev_id
1488
def _make_order(self):
1489
"""Return a suitable order for importing revisions.
1491
The order must be such that an revision is imported after all
1492
its (present) parents.
1494
todo = set(self.revisions.keys())
1495
done = self.absent_revisions.copy()
1498
# scan through looking for a revision whose parents
1500
for rev_id in sorted(list(todo)):
1501
rev = self.revisions[rev_id]
1502
parent_ids = set(rev.parent_ids)
1503
if parent_ids.issubset(done):
1504
# can take this one now
1505
order.append(rev_id)
1511
class ConvertBzrDir5To6(Converter):
1512
"""Converts format 5 bzr dirs to format 6."""
1514
def convert(self, to_convert, pb):
1515
"""See Converter.convert()."""
1516
self.bzrdir = to_convert
1518
self.pb.note('starting upgrade from format 5 to 6')
1519
self._convert_to_prefixed()
1520
return BzrDir.open(self.bzrdir.root_transport.base)
1522
def _convert_to_prefixed(self):
1523
from bzrlib.store import hash_prefix
1524
self.bzrdir.transport.delete('branch-format')
1525
for store_name in ["weaves", "revision-store"]:
1526
self.pb.note("adding prefixes to %s" % store_name)
1527
store_transport = self.bzrdir.transport.clone(store_name)
1528
for filename in store_transport.list_dir('.'):
1529
if (filename.endswith(".weave") or
1530
filename.endswith(".gz") or
1531
filename.endswith(".sig")):
1532
file_id = os.path.splitext(filename)[0]
1535
prefix_dir = hash_prefix(file_id)
1536
# FIXME keep track of the dirs made RBC 20060121
1538
store_transport.move(filename, prefix_dir + '/' + filename)
1539
except errors.NoSuchFile: # catches missing dirs strangely enough
1540
store_transport.mkdir(prefix_dir)
1541
store_transport.move(filename, prefix_dir + '/' + filename)
1542
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1545
class ConvertBzrDir6ToMeta(Converter):
1546
"""Converts format 6 bzr dirs to metadirs."""
1548
def convert(self, to_convert, pb):
1549
"""See Converter.convert()."""
1550
self.bzrdir = to_convert
1553
self.total = 20 # the steps we know about
1554
self.garbage_inventories = []
1556
self.pb.note('starting upgrade from format 6 to metadir')
1557
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1558
# its faster to move specific files around than to open and use the apis...
1559
# first off, nuke ancestry.weave, it was never used.
1561
self.step('Removing ancestry.weave')
1562
self.bzrdir.transport.delete('ancestry.weave')
1563
except errors.NoSuchFile:
1565
# find out whats there
1566
self.step('Finding branch files')
1567
last_revision = self.bzrdir.open_workingtree().last_revision()
1568
bzrcontents = self.bzrdir.transport.list_dir('.')
1569
for name in bzrcontents:
1570
if name.startswith('basis-inventory.'):
1571
self.garbage_inventories.append(name)
1572
# create new directories for repository, working tree and branch
1573
dir_mode = self.bzrdir._control_files._dir_mode
1574
self.file_mode = self.bzrdir._control_files._file_mode
1575
repository_names = [('inventory.weave', True),
1576
('revision-store', True),
1578
self.step('Upgrading repository ')
1579
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1580
self.make_lock('repository')
1581
# we hard code the formats here because we are converting into
1582
# the meta format. The meta format upgrader can take this to a
1583
# future format within each component.
1584
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1585
for entry in repository_names:
1586
self.move_entry('repository', entry)
1588
self.step('Upgrading branch ')
1589
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1590
self.make_lock('branch')
1591
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1592
branch_files = [('revision-history', True),
1593
('branch-name', True),
1595
for entry in branch_files:
1596
self.move_entry('branch', entry)
1598
self.step('Upgrading working tree')
1599
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1600
self.make_lock('checkout')
1601
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1602
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1603
checkout_files = [('pending-merges', True),
1604
('inventory', True),
1605
('stat-cache', False)]
1606
for entry in checkout_files:
1607
self.move_entry('checkout', entry)
1608
if last_revision is not None:
1609
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1611
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1612
return BzrDir.open(self.bzrdir.root_transport.base)
1614
def make_lock(self, name):
1615
"""Make a lock for the new control dir name."""
1616
self.step('Make %s lock' % name)
1617
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1619
def move_entry(self, new_dir, entry):
1620
"""Move then entry name into new_dir."""
1622
mandatory = entry[1]
1623
self.step('Moving %s' % name)
1625
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1626
except errors.NoSuchFile:
1630
def put_format(self, dirname, format):
1631
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1634
class ConvertMetaToMeta(Converter):
1635
"""Converts the components of metadirs."""
1637
def __init__(self, target_format):
1638
"""Create a metadir to metadir converter.
1640
:param target_format: The final metadir format that is desired.
1642
self.target_format = target_format
1644
def convert(self, to_convert, pb):
1645
"""See Converter.convert()."""
1646
self.bzrdir = to_convert
1650
self.step('checking repository format')
1652
repo = self.bzrdir.open_repository()
1653
except errors.NoRepositoryPresent:
1656
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1657
from bzrlib.repository import CopyConverter
1658
self.pb.note('starting repository conversion')
1659
converter = CopyConverter(self.target_format.repository_format)
1660
converter.convert(repo, pb)