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
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, TransportLock
31
from bzrlib.osutils import safe_unicode
32
from bzrlib.osutils import (
39
from bzrlib.store.text import TextStore
40
from bzrlib.store.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.weavefile import read_weave, write_weave
48
from bzrlib.xml4 import serializer_v4
49
from bzrlib.xml5 import serializer_v5
53
"""A .bzr control diretory.
55
BzrDir instances let you create or open any of the things that can be
56
found within .bzr - checkouts, branches and repositories.
59
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
61
a transport connected to the directory this bzr was opened from.
64
def can_convert_format(self):
65
"""Return true if this bzrdir is one whose format we can convert from."""
68
def _check_supported(self, format, allow_unsupported):
69
"""Check whether format is a supported format.
71
If allow_unsupported is True, this is a no-op.
73
if not allow_unsupported and not format.is_supported():
74
raise errors.UnsupportedFormatError(format)
76
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
77
"""Clone this bzrdir and its contents to url verbatim.
79
If urls last component does not exist, it will be created.
81
if revision_id is not None, then the clone operation may tune
82
itself to download less data.
83
:param force_new_repo: Do not use a shared repository for the target
84
even if one is available.
87
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
88
result = self._format.initialize(url)
90
local_repo = self.find_repository()
91
except errors.NoRepositoryPresent:
94
# may need to copy content in
96
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
99
result_repo = result.find_repository()
100
# fetch content this dir needs.
102
# XXX FIXME RBC 20060214 need tests for this when the basis
104
result_repo.fetch(basis_repo, revision_id=revision_id)
105
result_repo.fetch(local_repo, revision_id=revision_id)
106
except errors.NoRepositoryPresent:
107
# needed to make one anyway.
108
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
109
# 1 if there is a branch present
110
# make sure its content is available in the target repository
113
self.open_branch().clone(result, revision_id=revision_id)
114
except errors.NotBranchError:
117
self.open_workingtree().clone(result, basis=basis_tree)
118
except (errors.NoWorkingTree, errors.NotLocalUrl):
122
def _get_basis_components(self, basis):
123
"""Retrieve the basis components that are available at basis."""
125
return None, None, None
127
basis_tree = basis.open_workingtree()
128
basis_branch = basis_tree.branch
129
basis_repo = basis_branch.repository
130
except (errors.NoWorkingTree, errors.NotLocalUrl):
133
basis_branch = basis.open_branch()
134
basis_repo = basis_branch.repository
135
except errors.NotBranchError:
138
basis_repo = basis.open_repository()
139
except errors.NoRepositoryPresent:
141
return basis_repo, basis_branch, basis_tree
143
def _make_tail(self, url):
144
segments = url.split('/')
145
if segments and segments[-1] not in ('', '.'):
146
parent = '/'.join(segments[:-1])
147
t = bzrlib.transport.get_transport(parent)
149
t.mkdir(segments[-1])
150
except errors.FileExists:
155
"""Create a new BzrDir at the url 'base'.
157
This will call the current default formats initialize with base
158
as the only parameter.
160
If you need a specific format, consider creating an instance
161
of that and calling initialize().
163
segments = base.split('/')
164
if segments and segments[-1] not in ('', '.'):
165
parent = '/'.join(segments[:-1])
166
t = bzrlib.transport.get_transport(parent)
168
t.mkdir(segments[-1])
169
except errors.FileExists:
171
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
173
def create_branch(self):
174
"""Create a branch in this BzrDir.
176
The bzrdirs format will control what branch format is created.
177
For more control see BranchFormatXX.create(a_bzrdir).
179
raise NotImplementedError(self.create_branch)
182
def create_branch_and_repo(base, force_new_repo=False):
183
"""Create a new BzrDir, Branch and Repository at the url 'base'.
185
This will use the current default BzrDirFormat, and use whatever
186
repository format that that uses via bzrdir.create_branch and
187
create_repository. If a shared repository is available that is used
190
The created Branch object is returned.
192
:param base: The URL to create the branch at.
193
:param force_new_repo: If True a new repository is always created.
195
bzrdir = BzrDir.create(base)
196
bzrdir._find_or_create_repository(force_new_repo)
197
return bzrdir.create_branch()
199
def _find_or_create_repository(self, force_new_repo):
200
"""Create a new repository if needed, returning the repository."""
202
return self.create_repository()
204
return self.find_repository()
205
except errors.NoRepositoryPresent:
206
return self.create_repository()
209
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
210
"""Create a new BzrDir, Branch and Repository at the url 'base'.
212
This is a convenience function - it will use an existing repository
213
if possible, can be told explicitly whether to create a working tree or
216
This will use the current default BzrDirFormat, and use whatever
217
repository format that that uses via bzrdir.create_branch and
218
create_repository. If a shared repository is available that is used
219
preferentially. Whatever repository is used, its tree creation policy
222
The created Branch object is returned.
223
If a working tree cannot be made due to base not being a file:// url,
224
no error is raised unless force_new_tree is True, in which case no
225
data is created on disk and NotLocalUrl is raised.
227
:param base: The URL to create the branch at.
228
:param force_new_repo: If True a new repository is always created.
229
:param force_new_tree: If True or False force creation of a tree or
230
prevent such creation respectively.
233
# check for non local urls
234
t = get_transport(safe_unicode(base))
235
if not isinstance(t, LocalTransport):
236
raise errors.NotLocalUrl(base)
237
bzrdir = BzrDir.create(base)
238
repo = bzrdir._find_or_create_repository(force_new_repo)
239
result = bzrdir.create_branch()
240
if force_new_tree or (repo.make_working_trees() and
241
force_new_tree is None):
243
bzrdir.create_workingtree()
244
except errors.NotLocalUrl:
249
def create_repository(base, shared=False):
250
"""Create a new BzrDir and Repository at the url 'base'.
252
This will use the current default BzrDirFormat, and use whatever
253
repository format that that uses for bzrdirformat.create_repository.
255
;param shared: Create a shared repository rather than a standalone
257
The Repository object is returned.
259
This must be overridden as an instance method in child classes, where
260
it should take no parameters and construct whatever repository format
261
that child class desires.
263
bzrdir = BzrDir.create(base)
264
return bzrdir.create_repository()
267
def create_standalone_workingtree(base):
268
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
270
'base' must be a local path or a file:// url.
272
This will use the current default BzrDirFormat, and use whatever
273
repository format that that uses for bzrdirformat.create_workingtree,
274
create_branch and create_repository.
276
The WorkingTree object is returned.
278
t = get_transport(safe_unicode(base))
279
if not isinstance(t, LocalTransport):
280
raise errors.NotLocalUrl(base)
281
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
282
force_new_repo=True).bzrdir
283
return bzrdir.create_workingtree()
285
def create_workingtree(self, revision_id=None):
286
"""Create a working tree at this BzrDir.
288
revision_id: create it as of this revision id.
290
raise NotImplementedError(self.create_workingtree)
292
def find_repository(self):
293
"""Find the repository that should be used for a_bzrdir.
295
This does not require a branch as we use it to find the repo for
296
new branches as well as to hook existing branches up to their
300
return self.open_repository()
301
except errors.NoRepositoryPresent:
303
next_transport = self.root_transport.clone('..')
306
found_bzrdir = BzrDir.open_containing_from_transport(
308
except errors.NotBranchError:
309
raise errors.NoRepositoryPresent(self)
311
repository = found_bzrdir.open_repository()
312
except errors.NoRepositoryPresent:
313
next_transport = found_bzrdir.root_transport.clone('..')
315
if ((found_bzrdir.root_transport.base ==
316
self.root_transport.base) or repository.is_shared()):
319
raise errors.NoRepositoryPresent(self)
320
raise errors.NoRepositoryPresent(self)
322
def get_branch_transport(self, branch_format):
323
"""Get the transport for use by branch format in this BzrDir.
325
Note that bzr dirs that do not support format strings will raise
326
IncompatibleFormat if the branch format they are given has
327
a format string, and vice verca.
329
If branch_format is None, the transport is returned with no
330
checking. if it is not None, then the returned transport is
331
guaranteed to point to an existing directory ready for use.
333
raise NotImplementedError(self.get_branch_transport)
335
def get_repository_transport(self, repository_format):
336
"""Get the transport for use by repository format in this BzrDir.
338
Note that bzr dirs that do not support format strings will raise
339
IncompatibleFormat if the repository format they are given has
340
a format string, and vice verca.
342
If repository_format is None, the transport is returned with no
343
checking. if it is not None, then the returned transport is
344
guaranteed to point to an existing directory ready for use.
346
raise NotImplementedError(self.get_repository_transport)
348
def get_workingtree_transport(self, tree_format):
349
"""Get the transport for use by workingtree format in this BzrDir.
351
Note that bzr dirs that do not support format strings will raise
352
IncompatibleFormat if the workingtree format they are given has
353
a format string, and vice verca.
355
If workingtree_format is None, the transport is returned with no
356
checking. if it is not None, then the returned transport is
357
guaranteed to point to an existing directory ready for use.
359
raise NotImplementedError(self.get_workingtree_transport)
361
def __init__(self, _transport, _format):
362
"""Initialize a Bzr control dir object.
364
Only really common logic should reside here, concrete classes should be
365
made with varying behaviours.
367
:param _format: the format that is creating this BzrDir instance.
368
:param _transport: the transport this dir is based at.
370
self._format = _format
371
self.transport = _transport.clone('.bzr')
372
self.root_transport = _transport
374
def needs_format_conversion(self, format=None):
375
"""Return true if this bzrdir needs convert_format run on it.
377
For instance, if the repository format is out of date but the
378
branch and working tree are not, this should return True.
380
:param format: Optional parameter indicating a specific desired
381
format we plan to arrive at.
383
raise NotImplementedError(self.needs_format_conversion)
386
def open_unsupported(base):
387
"""Open a branch which is not supported."""
388
return BzrDir.open(base, _unsupported=True)
391
def open(base, _unsupported=False):
392
"""Open an existing bzrdir, rooted at 'base' (url)
394
_unsupported is a private parameter to the BzrDir class.
396
t = get_transport(base)
397
mutter("trying to open %r with transport %r", base, t)
398
format = BzrDirFormat.find_format(t)
399
if not _unsupported and not format.is_supported():
400
# see open_downlevel to open legacy branches.
401
raise errors.UnsupportedFormatError(
402
'sorry, format %s not supported' % format,
403
['use a different bzr version',
404
'or remove the .bzr directory'
405
' and "bzr init" again'])
406
return format.open(t, _found=True)
408
def open_branch(self, unsupported=False):
409
"""Open the branch object at this BzrDir if one is present.
411
If unsupported is True, then no longer supported branch formats can
414
TODO: static convenience version of this?
416
raise NotImplementedError(self.open_branch)
419
def open_containing(url):
420
"""Open an existing branch which contains url.
422
:param url: url to search from.
423
See open_containing_from_transport for more detail.
425
return BzrDir.open_containing_from_transport(get_transport(url))
428
def open_containing_from_transport(a_transport):
429
"""Open an existing branch which contains a_transport.base
431
This probes for a branch at a_transport, and searches upwards from there.
433
Basically we keep looking up until we find the control directory or
434
run into the root. If there isn't one, raises NotBranchError.
435
If there is one and it is either an unrecognised format or an unsupported
436
format, UnknownFormatError or UnsupportedFormatError are raised.
437
If there is one, it is returned, along with the unused portion of url.
439
# this gets the normalised url back. I.e. '.' -> the full path.
440
url = a_transport.base
443
format = BzrDirFormat.find_format(a_transport)
444
return format.open(a_transport), a_transport.relpath(url)
445
except errors.NotBranchError, e:
446
mutter('not a branch in: %r %s', a_transport.base, e)
447
new_t = a_transport.clone('..')
448
if new_t.base == a_transport.base:
449
# reached the root, whatever that may be
450
raise errors.NotBranchError(path=url)
453
def open_repository(self, _unsupported=False):
454
"""Open the repository object at this BzrDir if one is present.
456
This will not follow the Branch object pointer - its strictly a direct
457
open facility. Most client code should use open_branch().repository to
460
_unsupported is a private parameter, not part of the api.
461
TODO: static convenience version of this?
463
raise NotImplementedError(self.open_repository)
465
def open_workingtree(self, _unsupported=False):
466
"""Open the workingtree object at this BzrDir if one is present.
468
TODO: static convenience version of this?
470
raise NotImplementedError(self.open_workingtree)
472
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
473
"""Create a copy of this bzrdir prepared for use as a new line of
476
If urls last component does not exist, it will be created.
478
Attributes related to the identity of the source branch like
479
branch nickname will be cleaned, a working tree is created
480
whether one existed before or not; and a local branch is always
483
if revision_id is not None, then the clone operation may tune
484
itself to download less data.
487
result = self._format.initialize(url)
488
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
490
source_branch = self.open_branch()
491
source_repository = source_branch.repository
492
except errors.NotBranchError:
495
source_repository = self.open_repository()
496
except errors.NoRepositoryPresent:
497
# copy the entire basis one if there is one
498
# but there is no repository.
499
source_repository = basis_repo
504
result_repo = result.find_repository()
505
except errors.NoRepositoryPresent:
507
if source_repository is None and result_repo is not None:
509
elif source_repository is None and result_repo is None:
510
# no repo available, make a new one
511
result.create_repository()
512
elif source_repository is not None and result_repo is None:
513
# have soure, and want to make a new target repo
514
source_repository.clone(result,
515
revision_id=revision_id,
518
# fetch needed content into target.
520
# XXX FIXME RBC 20060214 need tests for this when the basis
522
result_repo.fetch(basis_repo, revision_id=revision_id)
523
result_repo.fetch(source_repository, revision_id=revision_id)
524
if source_branch is not None:
525
source_branch.sprout(result, revision_id=revision_id)
527
result.create_branch()
528
result.create_workingtree()
532
class BzrDirPreSplitOut(BzrDir):
533
"""A common class for the all-in-one formats."""
535
def __init__(self, _transport, _format):
536
"""See BzrDir.__init__."""
537
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
538
self._control_files = LockableFiles(self.get_branch_transport(None),
542
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
543
"""See BzrDir.clone()."""
544
from bzrlib.workingtree import WorkingTreeFormat2
546
result = self._format.initialize(url, _cloning=True)
547
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
548
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
549
self.open_branch().clone(result, revision_id=revision_id)
551
self.open_workingtree().clone(result, basis=basis_tree)
552
except errors.NotLocalUrl:
553
# make a new one, this format always has to have one.
554
WorkingTreeFormat2().initialize(result)
557
def create_branch(self):
558
"""See BzrDir.create_branch."""
559
return self.open_branch()
561
def create_repository(self, shared=False):
562
"""See BzrDir.create_repository."""
564
raise errors.IncompatibleFormat('shared repository', self._format)
565
return self.open_repository()
567
def create_workingtree(self, revision_id=None):
568
"""See BzrDir.create_workingtree."""
569
# this looks buggy but is not -really-
570
# clone and sprout will have set the revision_id
571
# and that will have set it for us, its only
572
# specific uses of create_workingtree in isolation
573
# that can do wonky stuff here, and that only
574
# happens for creating checkouts, which cannot be
575
# done on this format anyway. So - acceptable wart.
576
result = self.open_workingtree()
577
if revision_id is not None:
578
result.set_last_revision(revision_id)
581
def get_branch_transport(self, branch_format):
582
"""See BzrDir.get_branch_transport()."""
583
if branch_format is None:
584
return self.transport
586
branch_format.get_format_string()
587
except NotImplementedError:
588
return self.transport
589
raise errors.IncompatibleFormat(branch_format, self._format)
591
def get_repository_transport(self, repository_format):
592
"""See BzrDir.get_repository_transport()."""
593
if repository_format is None:
594
return self.transport
596
repository_format.get_format_string()
597
except NotImplementedError:
598
return self.transport
599
raise errors.IncompatibleFormat(repository_format, self._format)
601
def get_workingtree_transport(self, workingtree_format):
602
"""See BzrDir.get_workingtree_transport()."""
603
if workingtree_format is None:
604
return self.transport
606
workingtree_format.get_format_string()
607
except NotImplementedError:
608
return self.transport
609
raise errors.IncompatibleFormat(workingtree_format, self._format)
611
def needs_format_conversion(self, format=None):
612
"""See BzrDir.needs_format_conversion()."""
613
# if the format is not the same as the system default,
614
# an upgrade is needed.
616
format = BzrDirFormat.get_default_format()
617
return not isinstance(self._format, format.__class__)
619
def open_branch(self, unsupported=False):
620
"""See BzrDir.open_branch."""
621
from bzrlib.branch import BzrBranchFormat4
622
format = BzrBranchFormat4()
623
self._check_supported(format, unsupported)
624
return format.open(self, _found=True)
626
def sprout(self, url, revision_id=None, basis=None):
627
"""See BzrDir.sprout()."""
628
from bzrlib.workingtree import WorkingTreeFormat2
630
result = self._format.initialize(url, _cloning=True)
631
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
633
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
634
except errors.NoRepositoryPresent:
637
self.open_branch().sprout(result, revision_id=revision_id)
638
except errors.NotBranchError:
640
# we always want a working tree
641
WorkingTreeFormat2().initialize(result)
645
class BzrDir4(BzrDirPreSplitOut):
646
"""A .bzr version 4 control object.
648
This is a deprecated format and may be removed after sept 2006.
651
def create_repository(self, shared=False):
652
"""See BzrDir.create_repository."""
653
return self._format.repository_format.initialize(self, shared)
655
def needs_format_conversion(self, format=None):
656
"""Format 4 dirs are always in need of conversion."""
659
def open_repository(self):
660
"""See BzrDir.open_repository."""
661
from bzrlib.repository import RepositoryFormat4
662
return RepositoryFormat4().open(self, _found=True)
665
class BzrDir5(BzrDirPreSplitOut):
666
"""A .bzr version 5 control object.
668
This is a deprecated format and may be removed after sept 2006.
671
def open_repository(self):
672
"""See BzrDir.open_repository."""
673
from bzrlib.repository import RepositoryFormat5
674
return RepositoryFormat5().open(self, _found=True)
676
def open_workingtree(self, _unsupported=False):
677
"""See BzrDir.create_workingtree."""
678
from bzrlib.workingtree import WorkingTreeFormat2
679
return WorkingTreeFormat2().open(self, _found=True)
682
class BzrDir6(BzrDirPreSplitOut):
683
"""A .bzr version 6 control object.
685
This is a deprecated format and may be removed after sept 2006.
688
def open_repository(self):
689
"""See BzrDir.open_repository."""
690
from bzrlib.repository import RepositoryFormat6
691
return RepositoryFormat6().open(self, _found=True)
693
def open_workingtree(self, _unsupported=False):
694
"""See BzrDir.create_workingtree."""
695
from bzrlib.workingtree import WorkingTreeFormat2
696
return WorkingTreeFormat2().open(self, _found=True)
699
class BzrDirMeta1(BzrDir):
700
"""A .bzr meta version 1 control object.
702
This is the first control object where the
703
individual aspects are really split out: there are separate repository,
704
workingtree and branch subdirectories and any subset of the three can be
705
present within a BzrDir.
708
def can_convert_format(self):
709
"""See BzrDir.can_convert_format()."""
712
def create_branch(self):
713
"""See BzrDir.create_branch."""
714
from bzrlib.branch import BranchFormat
715
return BranchFormat.get_default_format().initialize(self)
717
def create_repository(self, shared=False):
718
"""See BzrDir.create_repository."""
719
return self._format.repository_format.initialize(self, shared)
721
def create_workingtree(self, revision_id=None):
722
"""See BzrDir.create_workingtree."""
723
from bzrlib.workingtree import WorkingTreeFormat
724
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
726
def get_branch_transport(self, branch_format):
727
"""See BzrDir.get_branch_transport()."""
728
if branch_format is None:
729
return self.transport.clone('branch')
731
branch_format.get_format_string()
732
except NotImplementedError:
733
raise errors.IncompatibleFormat(branch_format, self._format)
735
self.transport.mkdir('branch')
736
except errors.FileExists:
738
return self.transport.clone('branch')
740
def get_repository_transport(self, repository_format):
741
"""See BzrDir.get_repository_transport()."""
742
if repository_format is None:
743
return self.transport.clone('repository')
745
repository_format.get_format_string()
746
except NotImplementedError:
747
raise errors.IncompatibleFormat(repository_format, self._format)
749
self.transport.mkdir('repository')
750
except errors.FileExists:
752
return self.transport.clone('repository')
754
def get_workingtree_transport(self, workingtree_format):
755
"""See BzrDir.get_workingtree_transport()."""
756
if workingtree_format is None:
757
return self.transport.clone('checkout')
759
workingtree_format.get_format_string()
760
except NotImplementedError:
761
raise errors.IncompatibleFormat(workingtree_format, self._format)
763
self.transport.mkdir('checkout')
764
except errors.FileExists:
766
return self.transport.clone('checkout')
768
def needs_format_conversion(self, format=None):
769
"""See BzrDir.needs_format_conversion()."""
771
format = BzrDirFormat.get_default_format()
772
if not isinstance(self._format, format.__class__):
773
# it is not a meta dir format, conversion is needed.
775
# we might want to push this down to the repository?
777
if not isinstance(self.open_repository()._format,
778
format.repository_format.__class__):
779
# the repository needs an upgrade.
781
except errors.NoRepositoryPresent:
783
# currently there are no other possible conversions for meta1 formats.
786
def open_branch(self, unsupported=False):
787
"""See BzrDir.open_branch."""
788
from bzrlib.branch import BranchFormat
789
format = BranchFormat.find_format(self)
790
self._check_supported(format, unsupported)
791
return format.open(self, _found=True)
793
def open_repository(self, unsupported=False):
794
"""See BzrDir.open_repository."""
795
from bzrlib.repository import RepositoryFormat
796
format = RepositoryFormat.find_format(self)
797
self._check_supported(format, unsupported)
798
return format.open(self, _found=True)
800
def open_workingtree(self, unsupported=False):
801
"""See BzrDir.open_workingtree."""
802
from bzrlib.workingtree import WorkingTreeFormat
803
format = WorkingTreeFormat.find_format(self)
804
self._check_supported(format, unsupported)
805
return format.open(self, _found=True)
808
class BzrDirFormat(object):
809
"""An encapsulation of the initialization and open routines for a format.
811
Formats provide three things:
812
* An initialization routine,
816
Formats are placed in an dict by their format string for reference
817
during bzrdir opening. These should be subclasses of BzrDirFormat
820
Once a format is deprecated, just deprecate the initialize and open
821
methods on the format class. Do not deprecate the object, as the
822
object will be created every system load.
825
_default_format = None
826
"""The default format used for new .bzr dirs."""
829
"""The known formats."""
832
def find_format(klass, transport):
833
"""Return the format registered for URL."""
835
format_string = transport.get(".bzr/branch-format").read()
836
return klass._formats[format_string]
837
except errors.NoSuchFile:
838
raise errors.NotBranchError(path=transport.base)
840
raise errors.UnknownFormatError(format_string)
843
def get_default_format(klass):
844
"""Return the current default format."""
845
return klass._default_format
847
def get_format_string(self):
848
"""Return the ASCII format string that identifies this format."""
849
raise NotImplementedError(self.get_format_string)
851
def get_converter(self, format=None):
852
"""Return the converter to use to convert bzrdirs needing converts.
854
This returns a bzrlib.bzrdir.Converter object.
856
This should return the best upgrader to step this format towards the
857
current default format. In the case of plugins we can/shouold provide
858
some means for them to extend the range of returnable converters.
860
:param format: Optional format to override the default foramt of the
863
raise NotImplementedError(self.get_converter)
865
def initialize(self, url):
866
"""Create a bzr control dir at this url and return an opened copy."""
867
# Since we don't have a .bzr directory, inherit the
868
# mode from the root directory
869
t = get_transport(url)
870
temp_control = LockableFiles(t, '', TransportLock)
871
temp_control._transport.mkdir('.bzr',
872
# FIXME: RBC 20060121 dont peek under
874
mode=temp_control._dir_mode)
875
file_mode = temp_control._file_mode
877
mutter('created control directory in ' + t.base)
878
control = t.clone('.bzr')
879
lock_file = 'branch-lock'
880
utf8_files = [('README',
881
"This is a Bazaar-NG control directory.\n"
882
"Do not change any files in this directory.\n"),
883
('branch-format', self.get_format_string()),
885
# NB: no need to escape relative paths that are url safe.
886
control_files = LockableFiles(control, lock_file, TransportLock)
887
control_files.create_lock()
888
control_files.lock_write()
890
for file, content in utf8_files:
891
control_files.put_utf8(file, content)
893
control_files.unlock()
894
return self.open(t, _found=True)
896
def is_supported(self):
897
"""Is this format supported?
899
Supported formats must be initializable and openable.
900
Unsupported formats may not support initialization or committing or
901
some other features depending on the reason for not being supported.
905
def open(self, transport, _found=False):
906
"""Return an instance of this format for the dir transport points at.
908
_found is a private parameter, do not use it.
911
assert isinstance(BzrDirFormat.find_format(transport),
913
return self._open(transport)
915
def _open(self, transport):
916
"""Template method helper for opening BzrDirectories.
918
This performs the actual open and any additional logic or parameter
921
raise NotImplementedError(self._open)
924
def register_format(klass, format):
925
klass._formats[format.get_format_string()] = format
928
def set_default_format(klass, format):
929
klass._default_format = format
932
return self.get_format_string()[:-1]
935
def unregister_format(klass, format):
936
assert klass._formats[format.get_format_string()] is format
937
del klass._formats[format.get_format_string()]
940
class BzrDirFormat4(BzrDirFormat):
943
This format is a combined format for working tree, branch and repository.
945
- Format 1 working trees [always]
946
- Format 4 branches [always]
947
- Format 4 repositories [always]
949
This format is deprecated: it indexes texts using a text it which is
950
removed in format 5; write support for this format has been removed.
953
def get_format_string(self):
954
"""See BzrDirFormat.get_format_string()."""
955
return "Bazaar-NG branch, format 0.0.4\n"
957
def get_converter(self, format=None):
958
"""See BzrDirFormat.get_converter()."""
959
# there is one and only one upgrade path here.
960
return ConvertBzrDir4To5()
962
def initialize(self, url):
963
"""Format 4 branches cannot be created."""
964
raise errors.UninitializableFormat(self)
966
def is_supported(self):
967
"""Format 4 is not supported.
969
It is not supported because the model changed from 4 to 5 and the
970
conversion logic is expensive - so doing it on the fly was not
975
def _open(self, transport):
976
"""See BzrDirFormat._open."""
977
return BzrDir4(transport, self)
979
def __return_repository_format(self):
980
"""Circular import protection."""
981
from bzrlib.repository import RepositoryFormat4
982
return RepositoryFormat4(self)
983
repository_format = property(__return_repository_format)
986
class BzrDirFormat5(BzrDirFormat):
987
"""Bzr control format 5.
989
This format is a combined format for working tree, branch and repository.
991
- Format 2 working trees [always]
992
- Format 4 branches [always]
993
- Format 5 repositories [always]
994
Unhashed stores in the repository.
997
def get_format_string(self):
998
"""See BzrDirFormat.get_format_string()."""
999
return "Bazaar-NG branch, format 5\n"
1001
def get_converter(self, format=None):
1002
"""See BzrDirFormat.get_converter()."""
1003
# there is one and only one upgrade path here.
1004
return ConvertBzrDir5To6()
1006
def initialize(self, url, _cloning=False):
1007
"""Format 5 dirs always have working tree, branch and repository.
1009
Except when they are being cloned.
1011
from bzrlib.branch import BzrBranchFormat4
1012
from bzrlib.repository import RepositoryFormat5
1013
from bzrlib.workingtree import WorkingTreeFormat2
1014
result = super(BzrDirFormat5, self).initialize(url)
1015
RepositoryFormat5().initialize(result, _internal=True)
1017
BzrBranchFormat4().initialize(result)
1018
WorkingTreeFormat2().initialize(result)
1021
def _open(self, transport):
1022
"""See BzrDirFormat._open."""
1023
return BzrDir5(transport, self)
1025
def __return_repository_format(self):
1026
"""Circular import protection."""
1027
from bzrlib.repository import RepositoryFormat5
1028
return RepositoryFormat5(self)
1029
repository_format = property(__return_repository_format)
1032
class BzrDirFormat6(BzrDirFormat):
1033
"""Bzr control format 6.
1035
This format is a combined format for working tree, branch and repository.
1037
- Format 2 working trees [always]
1038
- Format 4 branches [always]
1039
- Format 6 repositories [always]
1042
def get_format_string(self):
1043
"""See BzrDirFormat.get_format_string()."""
1044
return "Bazaar-NG branch, format 6\n"
1046
def get_converter(self, format=None):
1047
"""See BzrDirFormat.get_converter()."""
1048
# there is one and only one upgrade path here.
1049
return ConvertBzrDir6ToMeta()
1051
def initialize(self, url, _cloning=False):
1052
"""Format 6 dirs always have working tree, branch and repository.
1054
Except when they are being cloned.
1056
from bzrlib.branch import BzrBranchFormat4
1057
from bzrlib.repository import RepositoryFormat6
1058
from bzrlib.workingtree import WorkingTreeFormat2
1059
result = super(BzrDirFormat6, self).initialize(url)
1060
RepositoryFormat6().initialize(result, _internal=True)
1062
BzrBranchFormat4().initialize(result)
1064
WorkingTreeFormat2().initialize(result)
1065
except errors.NotLocalUrl:
1066
# emulate pre-check behaviour for working tree and silently
1071
def _open(self, transport):
1072
"""See BzrDirFormat._open."""
1073
return BzrDir6(transport, self)
1075
def __return_repository_format(self):
1076
"""Circular import protection."""
1077
from bzrlib.repository import RepositoryFormat6
1078
return RepositoryFormat6(self)
1079
repository_format = property(__return_repository_format)
1082
class BzrDirMetaFormat1(BzrDirFormat):
1083
"""Bzr meta control format 1
1085
This is the first format with split out working tree, branch and repository
1088
- Format 3 working trees [optional]
1089
- Format 5 branches [optional]
1090
- Format 7 repositories [optional]
1093
def get_converter(self, format=None):
1094
"""See BzrDirFormat.get_converter()."""
1096
format = BzrDirFormat.get_default_format()
1097
if not isinstance(self, format.__class__):
1098
# converting away from metadir is not implemented
1099
raise NotImplementedError(self.get_converter)
1100
return ConvertMetaToMeta(format)
1102
def get_format_string(self):
1103
"""See BzrDirFormat.get_format_string()."""
1104
return "Bazaar-NG meta directory, format 1\n"
1106
def _open(self, transport):
1107
"""See BzrDirFormat._open."""
1108
return BzrDirMeta1(transport, self)
1110
def __return_repository_format(self):
1111
"""Circular import protection."""
1112
if getattr(self, '_repository_format', None):
1113
return self._repository_format
1114
from bzrlib.repository import RepositoryFormat
1115
return RepositoryFormat.get_default_format()
1117
def __set_repository_format(self, value):
1118
"""Allow changint the repository format for metadir formats."""
1119
self._repository_format = value
1120
repository_format = property(__return_repository_format, __set_repository_format)
1123
BzrDirFormat.register_format(BzrDirFormat4())
1124
BzrDirFormat.register_format(BzrDirFormat5())
1125
BzrDirFormat.register_format(BzrDirMetaFormat1())
1126
__default_format = BzrDirFormat6()
1127
BzrDirFormat.register_format(__default_format)
1128
BzrDirFormat.set_default_format(__default_format)
1131
class BzrDirTestProviderAdapter(object):
1132
"""A tool to generate a suite testing multiple bzrdir formats at once.
1134
This is done by copying the test once for each transport and injecting
1135
the transport_server, transport_readonly_server, and bzrdir_format
1136
classes into each copy. Each copy is also given a new id() to make it
1140
def __init__(self, transport_server, transport_readonly_server, formats):
1141
self._transport_server = transport_server
1142
self._transport_readonly_server = transport_readonly_server
1143
self._formats = formats
1145
def adapt(self, test):
1146
result = TestSuite()
1147
for format in self._formats:
1148
new_test = deepcopy(test)
1149
new_test.transport_server = self._transport_server
1150
new_test.transport_readonly_server = self._transport_readonly_server
1151
new_test.bzrdir_format = format
1152
def make_new_test_id():
1153
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1154
return lambda: new_id
1155
new_test.id = make_new_test_id()
1156
result.addTest(new_test)
1160
class ScratchDir(BzrDir6):
1161
"""Special test class: a bzrdir that cleans up itself..
1163
>>> d = ScratchDir()
1164
>>> base = d.transport.base
1167
>>> b.transport.__del__()
1172
def __init__(self, files=[], dirs=[], transport=None):
1173
"""Make a test branch.
1175
This creates a temporary directory and runs init-tree in it.
1177
If any files are listed, they are created in the working copy.
1179
if transport is None:
1180
transport = bzrlib.transport.local.ScratchTransport()
1181
# local import for scope restriction
1182
BzrDirFormat6().initialize(transport.base)
1183
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1184
self.create_repository()
1185
self.create_branch()
1186
self.create_workingtree()
1188
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1190
# BzrBranch creates a clone to .bzr and then forgets about the
1191
# original transport. A ScratchTransport() deletes itself and
1192
# everything underneath it when it goes away, so we need to
1193
# grab a local copy to prevent that from happening
1194
self._transport = transport
1197
self._transport.mkdir(d)
1200
self._transport.put(f, 'content of %s' % f)
1204
>>> orig = ScratchDir(files=["file1", "file2"])
1205
>>> os.listdir(orig.base)
1206
[u'.bzr', u'file1', u'file2']
1207
>>> clone = orig.clone()
1208
>>> if os.name != 'nt':
1209
... os.path.samefile(orig.base, clone.base)
1211
... orig.base == clone.base
1214
>>> os.listdir(clone.base)
1215
[u'.bzr', u'file1', u'file2']
1217
from shutil import copytree
1218
from bzrlib.osutils import mkdtemp
1221
copytree(self.base, base, symlinks=True)
1223
transport=bzrlib.transport.local.ScratchTransport(base))
1226
class Converter(object):
1227
"""Converts a disk format object from one format to another."""
1229
def convert(self, to_convert, pb):
1230
"""Perform the conversion of to_convert, giving feedback via pb.
1232
:param to_convert: The disk object to convert.
1233
:param pb: a progress bar to use for progress information.
1236
def step(self, message):
1237
"""Update the pb by a step."""
1239
self.pb.update(message, self.count, self.total)
1242
class ConvertBzrDir4To5(Converter):
1243
"""Converts format 4 bzr dirs to format 5."""
1246
super(ConvertBzrDir4To5, self).__init__()
1247
self.converted_revs = set()
1248
self.absent_revisions = set()
1252
def convert(self, to_convert, pb):
1253
"""See Converter.convert()."""
1254
self.bzrdir = to_convert
1256
self.pb.note('starting upgrade from format 4 to 5')
1257
if isinstance(self.bzrdir.transport, LocalTransport):
1258
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1259
self._convert_to_weaves()
1260
return BzrDir.open(self.bzrdir.root_transport.base)
1262
def _convert_to_weaves(self):
1263
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1266
stat = self.bzrdir.transport.stat('weaves')
1267
if not S_ISDIR(stat.st_mode):
1268
self.bzrdir.transport.delete('weaves')
1269
self.bzrdir.transport.mkdir('weaves')
1270
except errors.NoSuchFile:
1271
self.bzrdir.transport.mkdir('weaves')
1272
self.inv_weave = Weave('inventory')
1273
# holds in-memory weaves for all files
1274
self.text_weaves = {}
1275
self.bzrdir.transport.delete('branch-format')
1276
self.branch = self.bzrdir.open_branch()
1277
self._convert_working_inv()
1278
rev_history = self.branch.revision_history()
1279
# to_read is a stack holding the revisions we still need to process;
1280
# appending to it adds new highest-priority revisions
1281
self.known_revisions = set(rev_history)
1282
self.to_read = rev_history[-1:]
1284
rev_id = self.to_read.pop()
1285
if (rev_id not in self.revisions
1286
and rev_id not in self.absent_revisions):
1287
self._load_one_rev(rev_id)
1289
to_import = self._make_order()
1290
for i, rev_id in enumerate(to_import):
1291
self.pb.update('converting revision', i, len(to_import))
1292
self._convert_one_rev(rev_id)
1294
self._write_all_weaves()
1295
self._write_all_revs()
1296
self.pb.note('upgraded to weaves:')
1297
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1298
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1299
self.pb.note(' %6d texts', self.text_count)
1300
self._cleanup_spare_files_after_format4()
1301
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1303
def _cleanup_spare_files_after_format4(self):
1304
# FIXME working tree upgrade foo.
1305
for n in 'merged-patches', 'pending-merged-patches':
1307
## assert os.path.getsize(p) == 0
1308
self.bzrdir.transport.delete(n)
1309
except errors.NoSuchFile:
1311
self.bzrdir.transport.delete_tree('inventory-store')
1312
self.bzrdir.transport.delete_tree('text-store')
1314
def _convert_working_inv(self):
1315
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1316
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1317
# FIXME inventory is a working tree change.
1318
self.branch.control_files.put('inventory', new_inv_xml)
1320
def _write_all_weaves(self):
1321
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1322
weave_transport = self.bzrdir.transport.clone('weaves')
1323
weaves = WeaveStore(weave_transport, prefixed=False)
1324
transaction = PassThroughTransaction()
1326
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1329
for file_id, file_weave in self.text_weaves.items():
1330
self.pb.update('writing weave', i, len(self.text_weaves))
1331
weaves.put_weave(file_id, file_weave, transaction)
1336
def _write_all_revs(self):
1337
"""Write all revisions out in new form."""
1338
self.bzrdir.transport.delete_tree('revision-store')
1339
self.bzrdir.transport.mkdir('revision-store')
1340
revision_transport = self.bzrdir.transport.clone('revision-store')
1342
revision_store = TextStore(revision_transport,
1346
for i, rev_id in enumerate(self.converted_revs):
1347
self.pb.update('write revision', i, len(self.converted_revs))
1348
rev_tmp = StringIO()
1349
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1351
revision_store.add(rev_tmp, rev_id)
1355
def _load_one_rev(self, rev_id):
1356
"""Load a revision object into memory.
1358
Any parents not either loaded or abandoned get queued to be
1360
self.pb.update('loading revision',
1361
len(self.revisions),
1362
len(self.known_revisions))
1363
if not self.branch.repository.revision_store.has_id(rev_id):
1365
self.pb.note('revision {%s} not present in branch; '
1366
'will be converted as a ghost',
1368
self.absent_revisions.add(rev_id)
1370
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1371
rev = serializer_v4.read_revision_from_string(rev_xml)
1372
for parent_id in rev.parent_ids:
1373
self.known_revisions.add(parent_id)
1374
self.to_read.append(parent_id)
1375
self.revisions[rev_id] = rev
1377
def _load_old_inventory(self, rev_id):
1378
assert rev_id not in self.converted_revs
1379
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1380
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1381
rev = self.revisions[rev_id]
1382
if rev.inventory_sha1:
1383
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1384
'inventory sha mismatch for {%s}' % rev_id
1387
def _load_updated_inventory(self, rev_id):
1388
assert rev_id in self.converted_revs
1389
inv_xml = self.inv_weave.get_text(rev_id)
1390
inv = serializer_v5.read_inventory_from_string(inv_xml)
1393
def _convert_one_rev(self, rev_id):
1394
"""Convert revision and all referenced objects to new format."""
1395
rev = self.revisions[rev_id]
1396
inv = self._load_old_inventory(rev_id)
1397
present_parents = [p for p in rev.parent_ids
1398
if p not in self.absent_revisions]
1399
self._convert_revision_contents(rev, inv, present_parents)
1400
self._store_new_weave(rev, inv, present_parents)
1401
self.converted_revs.add(rev_id)
1403
def _store_new_weave(self, rev, inv, present_parents):
1404
# the XML is now updated with text versions
1408
if ie.kind == 'root_directory':
1410
assert hasattr(ie, 'revision'), \
1411
'no revision on {%s} in {%s}' % \
1412
(file_id, rev.revision_id)
1413
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1414
new_inv_sha1 = sha_string(new_inv_xml)
1415
self.inv_weave.add(rev.revision_id,
1417
new_inv_xml.splitlines(True),
1419
rev.inventory_sha1 = new_inv_sha1
1421
def _convert_revision_contents(self, rev, inv, present_parents):
1422
"""Convert all the files within a revision.
1424
Also upgrade the inventory to refer to the text revision ids."""
1425
rev_id = rev.revision_id
1426
mutter('converting texts of revision {%s}',
1428
parent_invs = map(self._load_updated_inventory, present_parents)
1431
self._convert_file_version(rev, ie, parent_invs)
1433
def _convert_file_version(self, rev, ie, parent_invs):
1434
"""Convert one version of one file.
1436
The file needs to be added into the weave if it is a merge
1437
of >=2 parents or if it's changed from its parent.
1439
if ie.kind == 'root_directory':
1441
file_id = ie.file_id
1442
rev_id = rev.revision_id
1443
w = self.text_weaves.get(file_id)
1446
self.text_weaves[file_id] = w
1447
text_changed = False
1448
previous_entries = ie.find_previous_heads(parent_invs, w)
1449
for old_revision in previous_entries:
1450
# if this fails, its a ghost ?
1451
assert old_revision in self.converted_revs
1452
self.snapshot_ie(previous_entries, ie, w, rev_id)
1454
assert getattr(ie, 'revision', None) is not None
1456
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1457
# TODO: convert this logic, which is ~= snapshot to
1458
# a call to:. This needs the path figured out. rather than a work_tree
1459
# a v4 revision_tree can be given, or something that looks enough like
1460
# one to give the file content to the entry if it needs it.
1461
# and we need something that looks like a weave store for snapshot to
1463
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1464
if len(previous_revisions) == 1:
1465
previous_ie = previous_revisions.values()[0]
1466
if ie._unchanged(previous_ie):
1467
ie.revision = previous_ie.revision
1469
parent_indexes = map(w.lookup, previous_revisions)
1471
text = self.branch.repository.text_store.get(ie.text_id)
1472
file_lines = text.readlines()
1473
assert sha_strings(file_lines) == ie.text_sha1
1474
assert sum(map(len, file_lines)) == ie.text_size
1475
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1476
self.text_count += 1
1478
w.add(rev_id, parent_indexes, [], None)
1479
ie.revision = rev_id
1481
def _make_order(self):
1482
"""Return a suitable order for importing revisions.
1484
The order must be such that an revision is imported after all
1485
its (present) parents.
1487
todo = set(self.revisions.keys())
1488
done = self.absent_revisions.copy()
1491
# scan through looking for a revision whose parents
1493
for rev_id in sorted(list(todo)):
1494
rev = self.revisions[rev_id]
1495
parent_ids = set(rev.parent_ids)
1496
if parent_ids.issubset(done):
1497
# can take this one now
1498
order.append(rev_id)
1504
class ConvertBzrDir5To6(Converter):
1505
"""Converts format 5 bzr dirs to format 6."""
1507
def convert(self, to_convert, pb):
1508
"""See Converter.convert()."""
1509
self.bzrdir = to_convert
1511
self.pb.note('starting upgrade from format 5 to 6')
1512
self._convert_to_prefixed()
1513
return BzrDir.open(self.bzrdir.root_transport.base)
1515
def _convert_to_prefixed(self):
1516
from bzrlib.store import hash_prefix
1517
self.bzrdir.transport.delete('branch-format')
1518
for store_name in ["weaves", "revision-store"]:
1519
self.pb.note("adding prefixes to %s" % store_name)
1520
store_transport = self.bzrdir.transport.clone(store_name)
1521
for filename in store_transport.list_dir('.'):
1522
if (filename.endswith(".weave") or
1523
filename.endswith(".gz") or
1524
filename.endswith(".sig")):
1525
file_id = os.path.splitext(filename)[0]
1528
prefix_dir = hash_prefix(file_id)
1529
# FIXME keep track of the dirs made RBC 20060121
1531
store_transport.move(filename, prefix_dir + '/' + filename)
1532
except errors.NoSuchFile: # catches missing dirs strangely enough
1533
store_transport.mkdir(prefix_dir)
1534
store_transport.move(filename, prefix_dir + '/' + filename)
1535
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1538
class ConvertBzrDir6ToMeta(Converter):
1539
"""Converts format 6 bzr dirs to metadirs."""
1541
def convert(self, to_convert, pb):
1542
"""See Converter.convert()."""
1543
self.bzrdir = to_convert
1546
self.total = 20 # the steps we know about
1547
self.garbage_inventories = []
1549
self.pb.note('starting upgrade from format 6 to metadir')
1550
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1551
# its faster to move specific files around than to open and use the apis...
1552
# first off, nuke ancestry.weave, it was never used.
1554
self.step('Removing ancestry.weave')
1555
self.bzrdir.transport.delete('ancestry.weave')
1556
except errors.NoSuchFile:
1558
# find out whats there
1559
self.step('Finding branch files')
1560
last_revision = self.bzrdir.open_workingtree().last_revision()
1561
bzrcontents = self.bzrdir.transport.list_dir('.')
1562
for name in bzrcontents:
1563
if name.startswith('basis-inventory.'):
1564
self.garbage_inventories.append(name)
1565
# create new directories for repository, working tree and branch
1566
dir_mode = self.bzrdir._control_files._dir_mode
1567
self.file_mode = self.bzrdir._control_files._file_mode
1568
repository_names = [('inventory.weave', True),
1569
('revision-store', True),
1571
self.step('Upgrading repository ')
1572
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1573
self.make_lock('repository')
1574
# we hard code the formats here because we are converting into
1575
# the meta format. The meta format upgrader can take this to a
1576
# future format within each component.
1577
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1578
for entry in repository_names:
1579
self.move_entry('repository', entry)
1581
self.step('Upgrading branch ')
1582
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1583
self.make_lock('branch')
1584
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1585
branch_files = [('revision-history', True),
1586
('branch-name', True),
1588
for entry in branch_files:
1589
self.move_entry('branch', entry)
1591
self.step('Upgrading working tree')
1592
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1593
self.make_lock('checkout')
1594
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1595
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1596
checkout_files = [('pending-merges', True),
1597
('inventory', True),
1598
('stat-cache', False)]
1599
for entry in checkout_files:
1600
self.move_entry('checkout', entry)
1601
if last_revision is not None:
1602
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1604
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1605
return BzrDir.open(self.bzrdir.root_transport.base)
1607
def make_lock(self, name):
1608
"""Make a lock for the new control dir name."""
1609
self.step('Make %s lock' % name)
1610
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1612
def move_entry(self, new_dir, entry):
1613
"""Move then entry name into new_dir."""
1615
mandatory = entry[1]
1616
self.step('Moving %s' % name)
1618
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1619
except errors.NoSuchFile:
1623
def put_format(self, dirname, format):
1624
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1627
class ConvertMetaToMeta(Converter):
1628
"""Converts the components of metadirs."""
1630
def __init__(self, target_format):
1631
"""Create a metadir to metadir converter.
1633
:param target_format: The final metadir format that is desired.
1635
self.target_format = target_format
1637
def convert(self, to_convert, pb):
1638
"""See Converter.convert()."""
1639
self.bzrdir = to_convert
1643
self.step('checking repository format')
1645
repo = self.bzrdir.open_repository()
1646
except errors.NoRepositoryPresent:
1649
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1650
from bzrlib.repository import CopyConverter
1651
self.pb.note('starting repository conversion')
1652
converter = CopyConverter(self.target_format.repository_format)
1653
converter.convert(repo, pb)