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.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),
541
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
542
"""See BzrDir.clone()."""
543
from bzrlib.workingtree import WorkingTreeFormat2
545
result = self._format.initialize(url, _cloning=True)
546
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
547
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
548
self.open_branch().clone(result, revision_id=revision_id)
550
self.open_workingtree().clone(result, basis=basis_tree)
551
except errors.NotLocalUrl:
552
# make a new one, this format always has to have one.
553
WorkingTreeFormat2().initialize(result)
556
def create_branch(self):
557
"""See BzrDir.create_branch."""
558
return self.open_branch()
560
def create_repository(self, shared=False):
561
"""See BzrDir.create_repository."""
563
raise errors.IncompatibleFormat('shared repository', self._format)
564
return self.open_repository()
566
def create_workingtree(self, revision_id=None):
567
"""See BzrDir.create_workingtree."""
568
# this looks buggy but is not -really-
569
# clone and sprout will have set the revision_id
570
# and that will have set it for us, its only
571
# specific uses of create_workingtree in isolation
572
# that can do wonky stuff here, and that only
573
# happens for creating checkouts, which cannot be
574
# done on this format anyway. So - acceptable wart.
575
result = self.open_workingtree()
576
if revision_id is not None:
577
result.set_last_revision(revision_id)
580
def get_branch_transport(self, branch_format):
581
"""See BzrDir.get_branch_transport()."""
582
if branch_format is None:
583
return self.transport
585
branch_format.get_format_string()
586
except NotImplementedError:
587
return self.transport
588
raise errors.IncompatibleFormat(branch_format, self._format)
590
def get_repository_transport(self, repository_format):
591
"""See BzrDir.get_repository_transport()."""
592
if repository_format is None:
593
return self.transport
595
repository_format.get_format_string()
596
except NotImplementedError:
597
return self.transport
598
raise errors.IncompatibleFormat(repository_format, self._format)
600
def get_workingtree_transport(self, workingtree_format):
601
"""See BzrDir.get_workingtree_transport()."""
602
if workingtree_format is None:
603
return self.transport
605
workingtree_format.get_format_string()
606
except NotImplementedError:
607
return self.transport
608
raise errors.IncompatibleFormat(workingtree_format, self._format)
610
def needs_format_conversion(self, format=None):
611
"""See BzrDir.needs_format_conversion()."""
612
# if the format is not the same as the system default,
613
# an upgrade is needed.
615
format = BzrDirFormat.get_default_format()
616
return not isinstance(self._format, format.__class__)
618
def open_branch(self, unsupported=False):
619
"""See BzrDir.open_branch."""
620
from bzrlib.branch import BzrBranchFormat4
621
format = BzrBranchFormat4()
622
self._check_supported(format, unsupported)
623
return format.open(self, _found=True)
625
def sprout(self, url, revision_id=None, basis=None):
626
"""See BzrDir.sprout()."""
627
from bzrlib.workingtree import WorkingTreeFormat2
629
result = self._format.initialize(url, _cloning=True)
630
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
632
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
633
except errors.NoRepositoryPresent:
636
self.open_branch().sprout(result, revision_id=revision_id)
637
except errors.NotBranchError:
639
# we always want a working tree
640
WorkingTreeFormat2().initialize(result)
644
class BzrDir4(BzrDirPreSplitOut):
645
"""A .bzr version 4 control object.
647
This is a deprecated format and may be removed after sept 2006.
650
def create_repository(self, shared=False):
651
"""See BzrDir.create_repository."""
652
return self._format.repository_format.initialize(self, shared)
654
def needs_format_conversion(self, format=None):
655
"""Format 4 dirs are always in need of conversion."""
658
def open_repository(self):
659
"""See BzrDir.open_repository."""
660
from bzrlib.repository import RepositoryFormat4
661
return RepositoryFormat4().open(self, _found=True)
664
class BzrDir5(BzrDirPreSplitOut):
665
"""A .bzr version 5 control object.
667
This is a deprecated format and may be removed after sept 2006.
670
def open_repository(self):
671
"""See BzrDir.open_repository."""
672
from bzrlib.repository import RepositoryFormat5
673
return RepositoryFormat5().open(self, _found=True)
675
def open_workingtree(self, _unsupported=False):
676
"""See BzrDir.create_workingtree."""
677
from bzrlib.workingtree import WorkingTreeFormat2
678
return WorkingTreeFormat2().open(self, _found=True)
681
class BzrDir6(BzrDirPreSplitOut):
682
"""A .bzr version 6 control object.
684
This is a deprecated format and may be removed after sept 2006.
687
def open_repository(self):
688
"""See BzrDir.open_repository."""
689
from bzrlib.repository import RepositoryFormat6
690
return RepositoryFormat6().open(self, _found=True)
692
def open_workingtree(self, _unsupported=False):
693
"""See BzrDir.create_workingtree."""
694
from bzrlib.workingtree import WorkingTreeFormat2
695
return WorkingTreeFormat2().open(self, _found=True)
698
class BzrDirMeta1(BzrDir):
699
"""A .bzr meta version 1 control object.
701
This is the first control object where the
702
individual formats are really split out.
705
def can_convert_format(self):
706
"""See BzrDir.can_convert_format()."""
709
def create_branch(self):
710
"""See BzrDir.create_branch."""
711
from bzrlib.branch import BranchFormat
712
return BranchFormat.get_default_format().initialize(self)
714
def create_repository(self, shared=False):
715
"""See BzrDir.create_repository."""
716
return self._format.repository_format.initialize(self, shared)
718
def create_workingtree(self, revision_id=None):
719
"""See BzrDir.create_workingtree."""
720
from bzrlib.workingtree import WorkingTreeFormat
721
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
723
def get_branch_transport(self, branch_format):
724
"""See BzrDir.get_branch_transport()."""
725
if branch_format is None:
726
return self.transport.clone('branch')
728
branch_format.get_format_string()
729
except NotImplementedError:
730
raise errors.IncompatibleFormat(branch_format, self._format)
732
self.transport.mkdir('branch')
733
except errors.FileExists:
735
return self.transport.clone('branch')
737
def get_repository_transport(self, repository_format):
738
"""See BzrDir.get_repository_transport()."""
739
if repository_format is None:
740
return self.transport.clone('repository')
742
repository_format.get_format_string()
743
except NotImplementedError:
744
raise errors.IncompatibleFormat(repository_format, self._format)
746
self.transport.mkdir('repository')
747
except errors.FileExists:
749
return self.transport.clone('repository')
751
def get_workingtree_transport(self, workingtree_format):
752
"""See BzrDir.get_workingtree_transport()."""
753
if workingtree_format is None:
754
return self.transport.clone('checkout')
756
workingtree_format.get_format_string()
757
except NotImplementedError:
758
raise errors.IncompatibleFormat(workingtree_format, self._format)
760
self.transport.mkdir('checkout')
761
except errors.FileExists:
763
return self.transport.clone('checkout')
765
def needs_format_conversion(self, format=None):
766
"""See BzrDir.needs_format_conversion()."""
768
format = BzrDirFormat.get_default_format()
769
if not isinstance(self._format, format.__class__):
770
# it is not a meta dir format, conversion is needed.
772
# we might want to push this down to the repository?
774
if not isinstance(self.open_repository()._format,
775
format.repository_format.__class__):
776
# the repository needs an upgrade.
778
except errors.NoRepositoryPresent:
780
# currently there are no other possible conversions for meta1 formats.
783
def open_branch(self, unsupported=False):
784
"""See BzrDir.open_branch."""
785
from bzrlib.branch import BranchFormat
786
format = BranchFormat.find_format(self)
787
self._check_supported(format, unsupported)
788
return format.open(self, _found=True)
790
def open_repository(self, unsupported=False):
791
"""See BzrDir.open_repository."""
792
from bzrlib.repository import RepositoryFormat
793
format = RepositoryFormat.find_format(self)
794
self._check_supported(format, unsupported)
795
return format.open(self, _found=True)
797
def open_workingtree(self, unsupported=False):
798
"""See BzrDir.open_workingtree."""
799
from bzrlib.workingtree import WorkingTreeFormat
800
format = WorkingTreeFormat.find_format(self)
801
self._check_supported(format, unsupported)
802
return format.open(self, _found=True)
805
class BzrDirFormat(object):
806
"""An encapsulation of the initialization and open routines for a format.
808
Formats provide three things:
809
* An initialization routine,
813
Formats are placed in an dict by their format string for reference
814
during bzrdir opening. These should be subclasses of BzrDirFormat
817
Once a format is deprecated, just deprecate the initialize and open
818
methods on the format class. Do not deprecate the object, as the
819
object will be created every system load.
822
_default_format = None
823
"""The default format used for new .bzr dirs."""
826
"""The known formats."""
829
def find_format(klass, transport):
830
"""Return the format registered for URL."""
832
format_string = transport.get(".bzr/branch-format").read()
833
return klass._formats[format_string]
834
except errors.NoSuchFile:
835
raise errors.NotBranchError(path=transport.base)
837
raise errors.UnknownFormatError(format_string)
840
def get_default_format(klass):
841
"""Return the current default format."""
842
return klass._default_format
844
def get_format_string(self):
845
"""Return the ASCII format string that identifies this format."""
846
raise NotImplementedError(self.get_format_string)
848
def get_converter(self, format=None):
849
"""Return the converter to use to convert bzrdirs needing converts.
851
This returns a bzrlib.bzrdir.Converter object.
853
This should return the best upgrader to step this format towards the
854
current default format. In the case of plugins we can/shouold provide
855
some means for them to extend the range of returnable converters.
857
:param format: Optional format to override the default foramt of the
860
raise NotImplementedError(self.get_converter)
862
def initialize(self, url):
863
"""Create a bzr control dir at this url and return an opened copy."""
864
# Since we don't have a .bzr directory, inherit the
865
# mode from the root directory
866
t = get_transport(url)
867
temp_control = LockableFiles(t, '')
868
temp_control._transport.mkdir('.bzr',
869
# FIXME: RBC 20060121 dont peek under
871
mode=temp_control._dir_mode)
872
file_mode = temp_control._file_mode
874
mutter('created control directory in ' + t.base)
875
control = t.clone('.bzr')
876
lock_file = 'branch-lock'
877
utf8_files = [('README',
878
"This is a Bazaar-NG control directory.\n"
879
"Do not change any files in this directory.\n"),
880
('branch-format', self.get_format_string()),
882
# NB: no need to escape relative paths that are url safe.
883
control.put(lock_file, StringIO(), mode=file_mode)
884
control_files = LockableFiles(control, lock_file)
885
control_files.lock_write()
887
for file, content in utf8_files:
888
control_files.put_utf8(file, content)
890
control_files.unlock()
891
return self.open(t, _found=True)
893
def is_supported(self):
894
"""Is this format supported?
896
Supported formats must be initializable and openable.
897
Unsupported formats may not support initialization or committing or
898
some other features depending on the reason for not being supported.
902
def open(self, transport, _found=False):
903
"""Return an instance of this format for the dir transport points at.
905
_found is a private parameter, do not use it.
908
assert isinstance(BzrDirFormat.find_format(transport),
910
return self._open(transport)
912
def _open(self, transport):
913
"""Template method helper for opening BzrDirectories.
915
This performs the actual open and any additional logic or parameter
918
raise NotImplementedError(self._open)
921
def register_format(klass, format):
922
klass._formats[format.get_format_string()] = format
925
def set_default_format(klass, format):
926
klass._default_format = format
929
return self.get_format_string()[:-1]
932
def unregister_format(klass, format):
933
assert klass._formats[format.get_format_string()] is format
934
del klass._formats[format.get_format_string()]
937
class BzrDirFormat4(BzrDirFormat):
940
This format is a combined format for working tree, branch and repository.
942
- Format 1 working trees [always]
943
- Format 4 branches [always]
944
- Format 4 repositories [always]
946
This format is deprecated: it indexes texts using a text it which is
947
removed in format 5; write support for this format has been removed.
950
def get_format_string(self):
951
"""See BzrDirFormat.get_format_string()."""
952
return "Bazaar-NG branch, format 0.0.4\n"
954
def get_converter(self, format=None):
955
"""See BzrDirFormat.get_converter()."""
956
# there is one and only one upgrade path here.
957
return ConvertBzrDir4To5()
959
def initialize(self, url):
960
"""Format 4 branches cannot be created."""
961
raise errors.UninitializableFormat(self)
963
def is_supported(self):
964
"""Format 4 is not supported.
966
It is not supported because the model changed from 4 to 5 and the
967
conversion logic is expensive - so doing it on the fly was not
972
def _open(self, transport):
973
"""See BzrDirFormat._open."""
974
return BzrDir4(transport, self)
976
def __return_repository_format(self):
977
"""Circular import protection."""
978
from bzrlib.repository import RepositoryFormat4
979
return RepositoryFormat4(self)
980
repository_format = property(__return_repository_format)
983
class BzrDirFormat5(BzrDirFormat):
984
"""Bzr control format 5.
986
This format is a combined format for working tree, branch and repository.
988
- Format 2 working trees [always]
989
- Format 4 branches [always]
990
- Format 5 repositories [always]
991
Unhashed stores in the repository.
994
def get_format_string(self):
995
"""See BzrDirFormat.get_format_string()."""
996
return "Bazaar-NG branch, format 5\n"
998
def get_converter(self, format=None):
999
"""See BzrDirFormat.get_converter()."""
1000
# there is one and only one upgrade path here.
1001
return ConvertBzrDir5To6()
1003
def initialize(self, url, _cloning=False):
1004
"""Format 5 dirs always have working tree, branch and repository.
1006
Except when they are being cloned.
1008
from bzrlib.branch import BzrBranchFormat4
1009
from bzrlib.repository import RepositoryFormat5
1010
from bzrlib.workingtree import WorkingTreeFormat2
1011
result = super(BzrDirFormat5, self).initialize(url)
1012
RepositoryFormat5().initialize(result, _internal=True)
1014
BzrBranchFormat4().initialize(result)
1015
WorkingTreeFormat2().initialize(result)
1018
def _open(self, transport):
1019
"""See BzrDirFormat._open."""
1020
return BzrDir5(transport, self)
1022
def __return_repository_format(self):
1023
"""Circular import protection."""
1024
from bzrlib.repository import RepositoryFormat5
1025
return RepositoryFormat5(self)
1026
repository_format = property(__return_repository_format)
1029
class BzrDirFormat6(BzrDirFormat):
1030
"""Bzr control format 6.
1032
This format is a combined format for working tree, branch and repository.
1034
- Format 2 working trees [always]
1035
- Format 4 branches [always]
1036
- Format 6 repositories [always]
1039
def get_format_string(self):
1040
"""See BzrDirFormat.get_format_string()."""
1041
return "Bazaar-NG branch, format 6\n"
1043
def get_converter(self, format=None):
1044
"""See BzrDirFormat.get_converter()."""
1045
# there is one and only one upgrade path here.
1046
return ConvertBzrDir6ToMeta()
1048
def initialize(self, url, _cloning=False):
1049
"""Format 6 dirs always have working tree, branch and repository.
1051
Except when they are being cloned.
1053
from bzrlib.branch import BzrBranchFormat4
1054
from bzrlib.repository import RepositoryFormat6
1055
from bzrlib.workingtree import WorkingTreeFormat2
1056
result = super(BzrDirFormat6, self).initialize(url)
1057
RepositoryFormat6().initialize(result, _internal=True)
1059
BzrBranchFormat4().initialize(result)
1061
WorkingTreeFormat2().initialize(result)
1062
except errors.NotLocalUrl:
1063
# emulate pre-check behaviour for working tree and silently
1068
def _open(self, transport):
1069
"""See BzrDirFormat._open."""
1070
return BzrDir6(transport, self)
1072
def __return_repository_format(self):
1073
"""Circular import protection."""
1074
from bzrlib.repository import RepositoryFormat6
1075
return RepositoryFormat6(self)
1076
repository_format = property(__return_repository_format)
1079
class BzrDirMetaFormat1(BzrDirFormat):
1080
"""Bzr meta control format 1
1082
This is the first format with split out working tree, branch and repository
1085
- Format 3 working trees [optional]
1086
- Format 5 branches [optional]
1087
- Format 7 repositories [optional]
1090
def get_converter(self, format=None):
1091
"""See BzrDirFormat.get_converter()."""
1093
format = BzrDirFormat.get_default_format()
1094
if not isinstance(self, format.__class__):
1095
# converting away from metadir is not implemented
1096
raise NotImplementedError(self.get_converter)
1097
return ConvertMetaToMeta(format)
1099
def get_format_string(self):
1100
"""See BzrDirFormat.get_format_string()."""
1101
return "Bazaar-NG meta directory, format 1\n"
1103
def _open(self, transport):
1104
"""See BzrDirFormat._open."""
1105
return BzrDirMeta1(transport, self)
1107
def __return_repository_format(self):
1108
"""Circular import protection."""
1109
if getattr(self, '_repository_format', None):
1110
return self._repository_format
1111
from bzrlib.repository import RepositoryFormat
1112
return RepositoryFormat.get_default_format()
1114
def __set_repository_format(self, value):
1115
"""Allow changint the repository format for metadir formats."""
1116
self._repository_format = value
1117
repository_format = property(__return_repository_format, __set_repository_format)
1120
BzrDirFormat.register_format(BzrDirFormat4())
1121
BzrDirFormat.register_format(BzrDirFormat5())
1122
BzrDirFormat.register_format(BzrDirMetaFormat1())
1123
__default_format = BzrDirFormat6()
1124
BzrDirFormat.register_format(__default_format)
1125
BzrDirFormat.set_default_format(__default_format)
1128
class BzrDirTestProviderAdapter(object):
1129
"""A tool to generate a suite testing multiple bzrdir formats at once.
1131
This is done by copying the test once for each transport and injecting
1132
the transport_server, transport_readonly_server, and bzrdir_format
1133
classes into each copy. Each copy is also given a new id() to make it
1137
def __init__(self, transport_server, transport_readonly_server, formats):
1138
self._transport_server = transport_server
1139
self._transport_readonly_server = transport_readonly_server
1140
self._formats = formats
1142
def adapt(self, test):
1143
result = TestSuite()
1144
for format in self._formats:
1145
new_test = deepcopy(test)
1146
new_test.transport_server = self._transport_server
1147
new_test.transport_readonly_server = self._transport_readonly_server
1148
new_test.bzrdir_format = format
1149
def make_new_test_id():
1150
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1151
return lambda: new_id
1152
new_test.id = make_new_test_id()
1153
result.addTest(new_test)
1157
class ScratchDir(BzrDir6):
1158
"""Special test class: a bzrdir that cleans up itself..
1160
>>> d = ScratchDir()
1161
>>> base = d.transport.base
1164
>>> b.transport.__del__()
1169
def __init__(self, files=[], dirs=[], transport=None):
1170
"""Make a test branch.
1172
This creates a temporary directory and runs init-tree in it.
1174
If any files are listed, they are created in the working copy.
1176
if transport is None:
1177
transport = bzrlib.transport.local.ScratchTransport()
1178
# local import for scope restriction
1179
BzrDirFormat6().initialize(transport.base)
1180
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1181
self.create_repository()
1182
self.create_branch()
1183
self.create_workingtree()
1185
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1187
# BzrBranch creates a clone to .bzr and then forgets about the
1188
# original transport. A ScratchTransport() deletes itself and
1189
# everything underneath it when it goes away, so we need to
1190
# grab a local copy to prevent that from happening
1191
self._transport = transport
1194
self._transport.mkdir(d)
1197
self._transport.put(f, 'content of %s' % f)
1201
>>> orig = ScratchDir(files=["file1", "file2"])
1202
>>> os.listdir(orig.base)
1203
[u'.bzr', u'file1', u'file2']
1204
>>> clone = orig.clone()
1205
>>> if os.name != 'nt':
1206
... os.path.samefile(orig.base, clone.base)
1208
... orig.base == clone.base
1211
>>> os.listdir(clone.base)
1212
[u'.bzr', u'file1', u'file2']
1214
from shutil import copytree
1215
from bzrlib.osutils import mkdtemp
1218
copytree(self.base, base, symlinks=True)
1220
transport=bzrlib.transport.local.ScratchTransport(base))
1223
class Converter(object):
1224
"""Converts a disk format object from one format to another."""
1226
def convert(self, to_convert, pb):
1227
"""Perform the conversion of to_convert, giving feedback via pb.
1229
:param to_convert: The disk object to convert.
1230
:param pb: a progress bar to use for progress information.
1233
def step(self, message):
1234
"""Update the pb by a step."""
1236
self.pb.update(message, self.count, self.total)
1239
class ConvertBzrDir4To5(Converter):
1240
"""Converts format 4 bzr dirs to format 5."""
1243
super(ConvertBzrDir4To5, self).__init__()
1244
self.converted_revs = set()
1245
self.absent_revisions = set()
1249
def convert(self, to_convert, pb):
1250
"""See Converter.convert()."""
1251
self.bzrdir = to_convert
1253
self.pb.note('starting upgrade from format 4 to 5')
1254
if isinstance(self.bzrdir.transport, LocalTransport):
1255
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1256
self._convert_to_weaves()
1257
return BzrDir.open(self.bzrdir.root_transport.base)
1259
def _convert_to_weaves(self):
1260
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1263
stat = self.bzrdir.transport.stat('weaves')
1264
if not S_ISDIR(stat.st_mode):
1265
self.bzrdir.transport.delete('weaves')
1266
self.bzrdir.transport.mkdir('weaves')
1267
except errors.NoSuchFile:
1268
self.bzrdir.transport.mkdir('weaves')
1269
self.inv_weave = Weave('inventory')
1270
# holds in-memory weaves for all files
1271
self.text_weaves = {}
1272
self.bzrdir.transport.delete('branch-format')
1273
self.branch = self.bzrdir.open_branch()
1274
self._convert_working_inv()
1275
rev_history = self.branch.revision_history()
1276
# to_read is a stack holding the revisions we still need to process;
1277
# appending to it adds new highest-priority revisions
1278
self.known_revisions = set(rev_history)
1279
self.to_read = rev_history[-1:]
1281
rev_id = self.to_read.pop()
1282
if (rev_id not in self.revisions
1283
and rev_id not in self.absent_revisions):
1284
self._load_one_rev(rev_id)
1286
to_import = self._make_order()
1287
for i, rev_id in enumerate(to_import):
1288
self.pb.update('converting revision', i, len(to_import))
1289
self._convert_one_rev(rev_id)
1291
self._write_all_weaves()
1292
self._write_all_revs()
1293
self.pb.note('upgraded to weaves:')
1294
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1295
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1296
self.pb.note(' %6d texts', self.text_count)
1297
self._cleanup_spare_files_after_format4()
1298
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1300
def _cleanup_spare_files_after_format4(self):
1301
# FIXME working tree upgrade foo.
1302
for n in 'merged-patches', 'pending-merged-patches':
1304
## assert os.path.getsize(p) == 0
1305
self.bzrdir.transport.delete(n)
1306
except errors.NoSuchFile:
1308
self.bzrdir.transport.delete_tree('inventory-store')
1309
self.bzrdir.transport.delete_tree('text-store')
1311
def _convert_working_inv(self):
1312
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1313
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1314
# FIXME inventory is a working tree change.
1315
self.branch.control_files.put('inventory', new_inv_xml)
1317
def _write_all_weaves(self):
1318
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1319
weave_transport = self.bzrdir.transport.clone('weaves')
1320
weaves = WeaveStore(weave_transport, prefixed=False)
1321
transaction = PassThroughTransaction()
1323
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1326
for file_id, file_weave in self.text_weaves.items():
1327
self.pb.update('writing weave', i, len(self.text_weaves))
1328
weaves.put_weave(file_id, file_weave, transaction)
1333
def _write_all_revs(self):
1334
"""Write all revisions out in new form."""
1335
self.bzrdir.transport.delete_tree('revision-store')
1336
self.bzrdir.transport.mkdir('revision-store')
1337
revision_transport = self.bzrdir.transport.clone('revision-store')
1339
revision_store = TextStore(revision_transport,
1343
for i, rev_id in enumerate(self.converted_revs):
1344
self.pb.update('write revision', i, len(self.converted_revs))
1345
rev_tmp = StringIO()
1346
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1348
revision_store.add(rev_tmp, rev_id)
1352
def _load_one_rev(self, rev_id):
1353
"""Load a revision object into memory.
1355
Any parents not either loaded or abandoned get queued to be
1357
self.pb.update('loading revision',
1358
len(self.revisions),
1359
len(self.known_revisions))
1360
if not self.branch.repository.revision_store.has_id(rev_id):
1362
self.pb.note('revision {%s} not present in branch; '
1363
'will be converted as a ghost',
1365
self.absent_revisions.add(rev_id)
1367
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1368
rev = serializer_v4.read_revision_from_string(rev_xml)
1369
for parent_id in rev.parent_ids:
1370
self.known_revisions.add(parent_id)
1371
self.to_read.append(parent_id)
1372
self.revisions[rev_id] = rev
1374
def _load_old_inventory(self, rev_id):
1375
assert rev_id not in self.converted_revs
1376
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1377
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1378
rev = self.revisions[rev_id]
1379
if rev.inventory_sha1:
1380
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1381
'inventory sha mismatch for {%s}' % rev_id
1384
def _load_updated_inventory(self, rev_id):
1385
assert rev_id in self.converted_revs
1386
inv_xml = self.inv_weave.get_text(rev_id)
1387
inv = serializer_v5.read_inventory_from_string(inv_xml)
1390
def _convert_one_rev(self, rev_id):
1391
"""Convert revision and all referenced objects to new format."""
1392
rev = self.revisions[rev_id]
1393
inv = self._load_old_inventory(rev_id)
1394
present_parents = [p for p in rev.parent_ids
1395
if p not in self.absent_revisions]
1396
self._convert_revision_contents(rev, inv, present_parents)
1397
self._store_new_weave(rev, inv, present_parents)
1398
self.converted_revs.add(rev_id)
1400
def _store_new_weave(self, rev, inv, present_parents):
1401
# the XML is now updated with text versions
1405
if ie.kind == 'root_directory':
1407
assert hasattr(ie, 'revision'), \
1408
'no revision on {%s} in {%s}' % \
1409
(file_id, rev.revision_id)
1410
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1411
new_inv_sha1 = sha_string(new_inv_xml)
1412
self.inv_weave.add(rev.revision_id,
1414
new_inv_xml.splitlines(True),
1416
rev.inventory_sha1 = new_inv_sha1
1418
def _convert_revision_contents(self, rev, inv, present_parents):
1419
"""Convert all the files within a revision.
1421
Also upgrade the inventory to refer to the text revision ids."""
1422
rev_id = rev.revision_id
1423
mutter('converting texts of revision {%s}',
1425
parent_invs = map(self._load_updated_inventory, present_parents)
1428
self._convert_file_version(rev, ie, parent_invs)
1430
def _convert_file_version(self, rev, ie, parent_invs):
1431
"""Convert one version of one file.
1433
The file needs to be added into the weave if it is a merge
1434
of >=2 parents or if it's changed from its parent.
1436
if ie.kind == 'root_directory':
1438
file_id = ie.file_id
1439
rev_id = rev.revision_id
1440
w = self.text_weaves.get(file_id)
1443
self.text_weaves[file_id] = w
1444
text_changed = False
1445
previous_entries = ie.find_previous_heads(parent_invs, w)
1446
for old_revision in previous_entries:
1447
# if this fails, its a ghost ?
1448
assert old_revision in self.converted_revs
1449
self.snapshot_ie(previous_entries, ie, w, rev_id)
1451
assert getattr(ie, 'revision', None) is not None
1453
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1454
# TODO: convert this logic, which is ~= snapshot to
1455
# a call to:. This needs the path figured out. rather than a work_tree
1456
# a v4 revision_tree can be given, or something that looks enough like
1457
# one to give the file content to the entry if it needs it.
1458
# and we need something that looks like a weave store for snapshot to
1460
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1461
if len(previous_revisions) == 1:
1462
previous_ie = previous_revisions.values()[0]
1463
if ie._unchanged(previous_ie):
1464
ie.revision = previous_ie.revision
1466
parent_indexes = map(w.lookup, previous_revisions)
1468
text = self.branch.repository.text_store.get(ie.text_id)
1469
file_lines = text.readlines()
1470
assert sha_strings(file_lines) == ie.text_sha1
1471
assert sum(map(len, file_lines)) == ie.text_size
1472
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1473
self.text_count += 1
1475
w.add(rev_id, parent_indexes, [], None)
1476
ie.revision = rev_id
1478
def _make_order(self):
1479
"""Return a suitable order for importing revisions.
1481
The order must be such that an revision is imported after all
1482
its (present) parents.
1484
todo = set(self.revisions.keys())
1485
done = self.absent_revisions.copy()
1488
# scan through looking for a revision whose parents
1490
for rev_id in sorted(list(todo)):
1491
rev = self.revisions[rev_id]
1492
parent_ids = set(rev.parent_ids)
1493
if parent_ids.issubset(done):
1494
# can take this one now
1495
order.append(rev_id)
1501
class ConvertBzrDir5To6(Converter):
1502
"""Converts format 5 bzr dirs to format 6."""
1504
def convert(self, to_convert, pb):
1505
"""See Converter.convert()."""
1506
self.bzrdir = to_convert
1508
self.pb.note('starting upgrade from format 5 to 6')
1509
self._convert_to_prefixed()
1510
return BzrDir.open(self.bzrdir.root_transport.base)
1512
def _convert_to_prefixed(self):
1513
from bzrlib.store import hash_prefix
1514
self.bzrdir.transport.delete('branch-format')
1515
for store_name in ["weaves", "revision-store"]:
1516
self.pb.note("adding prefixes to %s" % store_name)
1517
store_transport = self.bzrdir.transport.clone(store_name)
1518
for filename in store_transport.list_dir('.'):
1519
if (filename.endswith(".weave") or
1520
filename.endswith(".gz") or
1521
filename.endswith(".sig")):
1522
file_id = os.path.splitext(filename)[0]
1525
prefix_dir = hash_prefix(file_id)
1526
# FIXME keep track of the dirs made RBC 20060121
1528
store_transport.move(filename, prefix_dir + '/' + filename)
1529
except errors.NoSuchFile: # catches missing dirs strangely enough
1530
store_transport.mkdir(prefix_dir)
1531
store_transport.move(filename, prefix_dir + '/' + filename)
1532
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1535
class ConvertBzrDir6ToMeta(Converter):
1536
"""Converts format 6 bzr dirs to metadirs."""
1538
def convert(self, to_convert, pb):
1539
"""See Converter.convert()."""
1540
self.bzrdir = to_convert
1543
self.total = 20 # the steps we know about
1544
self.garbage_inventories = []
1546
self.pb.note('starting upgrade from format 6 to metadir')
1547
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1548
# its faster to move specific files around than to open and use the apis...
1549
# first off, nuke ancestry.weave, it was never used.
1551
self.step('Removing ancestry.weave')
1552
self.bzrdir.transport.delete('ancestry.weave')
1553
except errors.NoSuchFile:
1555
# find out whats there
1556
self.step('Finding branch files')
1557
last_revision = self.bzrdir.open_workingtree().last_revision()
1558
bzrcontents = self.bzrdir.transport.list_dir('.')
1559
for name in bzrcontents:
1560
if name.startswith('basis-inventory.'):
1561
self.garbage_inventories.append(name)
1562
# create new directories for repository, working tree and branch
1563
dir_mode = self.bzrdir._control_files._dir_mode
1564
self.file_mode = self.bzrdir._control_files._file_mode
1565
repository_names = [('inventory.weave', True),
1566
('revision-store', True),
1568
self.step('Upgrading repository ')
1569
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1570
self.make_lock('repository')
1571
# we hard code the formats here because we are converting into
1572
# the meta format. The meta format upgrader can take this to a
1573
# future format within each component.
1574
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1575
for entry in repository_names:
1576
self.move_entry('repository', entry)
1578
self.step('Upgrading branch ')
1579
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1580
self.make_lock('branch')
1581
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1582
branch_files = [('revision-history', True),
1583
('branch-name', True),
1585
for entry in branch_files:
1586
self.move_entry('branch', entry)
1588
self.step('Upgrading working tree')
1589
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1590
self.make_lock('checkout')
1591
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1592
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1593
checkout_files = [('pending-merges', True),
1594
('inventory', True),
1595
('stat-cache', False)]
1596
for entry in checkout_files:
1597
self.move_entry('checkout', entry)
1598
if last_revision is not None:
1599
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1601
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1602
return BzrDir.open(self.bzrdir.root_transport.base)
1604
def make_lock(self, name):
1605
"""Make a lock for the new control dir name."""
1606
self.step('Make %s lock' % name)
1607
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1609
def move_entry(self, new_dir, entry):
1610
"""Move then entry name into new_dir."""
1612
mandatory = entry[1]
1613
self.step('Moving %s' % name)
1615
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1616
except errors.NoSuchFile:
1620
def put_format(self, dirname, format):
1621
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1624
class ConvertMetaToMeta(Converter):
1625
"""Converts the components of metadirs."""
1627
def __init__(self, target_format):
1628
"""Create a metadir to metadir converter.
1630
:param target_format: The final metadir format that is desired.
1632
self.target_format = target_format
1634
def convert(self, to_convert, pb):
1635
"""See Converter.convert()."""
1636
self.bzrdir = to_convert
1640
self.step('checking repository format')
1642
repo = self.bzrdir.open_repository()
1643
except errors.NoRepositoryPresent:
1646
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1647
from bzrlib.repository import CopyConverter
1648
self.pb.note('starting repository conversion')
1649
converter = CopyConverter(self.target_format.repository_format)
1650
converter.convert(repo, pb)