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 formats are really split out.
706
def can_convert_format(self):
707
"""See BzrDir.can_convert_format()."""
710
def create_branch(self):
711
"""See BzrDir.create_branch."""
712
from bzrlib.branch import BranchFormat
713
return BranchFormat.get_default_format().initialize(self)
715
def create_repository(self, shared=False):
716
"""See BzrDir.create_repository."""
717
return self._format.repository_format.initialize(self, shared)
719
def create_workingtree(self, revision_id=None):
720
"""See BzrDir.create_workingtree."""
721
from bzrlib.workingtree import WorkingTreeFormat
722
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
724
def get_branch_transport(self, branch_format):
725
"""See BzrDir.get_branch_transport()."""
726
if branch_format is None:
727
return self.transport.clone('branch')
729
branch_format.get_format_string()
730
except NotImplementedError:
731
raise errors.IncompatibleFormat(branch_format, self._format)
733
self.transport.mkdir('branch')
734
except errors.FileExists:
736
return self.transport.clone('branch')
738
def get_repository_transport(self, repository_format):
739
"""See BzrDir.get_repository_transport()."""
740
if repository_format is None:
741
return self.transport.clone('repository')
743
repository_format.get_format_string()
744
except NotImplementedError:
745
raise errors.IncompatibleFormat(repository_format, self._format)
747
self.transport.mkdir('repository')
748
except errors.FileExists:
750
return self.transport.clone('repository')
752
def get_workingtree_transport(self, workingtree_format):
753
"""See BzrDir.get_workingtree_transport()."""
754
if workingtree_format is None:
755
return self.transport.clone('checkout')
757
workingtree_format.get_format_string()
758
except NotImplementedError:
759
raise errors.IncompatibleFormat(workingtree_format, self._format)
761
self.transport.mkdir('checkout')
762
except errors.FileExists:
764
return self.transport.clone('checkout')
766
def needs_format_conversion(self, format=None):
767
"""See BzrDir.needs_format_conversion()."""
769
format = BzrDirFormat.get_default_format()
770
if not isinstance(self._format, format.__class__):
771
# it is not a meta dir format, conversion is needed.
773
# we might want to push this down to the repository?
775
if not isinstance(self.open_repository()._format,
776
format.repository_format.__class__):
777
# the repository needs an upgrade.
779
except errors.NoRepositoryPresent:
781
# currently there are no other possible conversions for meta1 formats.
784
def open_branch(self, unsupported=False):
785
"""See BzrDir.open_branch."""
786
from bzrlib.branch import BranchFormat
787
format = BranchFormat.find_format(self)
788
self._check_supported(format, unsupported)
789
return format.open(self, _found=True)
791
def open_repository(self, unsupported=False):
792
"""See BzrDir.open_repository."""
793
from bzrlib.repository import RepositoryFormat
794
format = RepositoryFormat.find_format(self)
795
self._check_supported(format, unsupported)
796
return format.open(self, _found=True)
798
def open_workingtree(self, unsupported=False):
799
"""See BzrDir.open_workingtree."""
800
from bzrlib.workingtree import WorkingTreeFormat
801
format = WorkingTreeFormat.find_format(self)
802
self._check_supported(format, unsupported)
803
return format.open(self, _found=True)
806
class BzrDirFormat(object):
807
"""An encapsulation of the initialization and open routines for a format.
809
Formats provide three things:
810
* An initialization routine,
814
Formats are placed in an dict by their format string for reference
815
during bzrdir opening. These should be subclasses of BzrDirFormat
818
Once a format is deprecated, just deprecate the initialize and open
819
methods on the format class. Do not deprecate the object, as the
820
object will be created every system load.
823
_default_format = None
824
"""The default format used for new .bzr dirs."""
827
"""The known formats."""
830
def find_format(klass, transport):
831
"""Return the format registered for URL."""
833
format_string = transport.get(".bzr/branch-format").read()
834
return klass._formats[format_string]
835
except errors.NoSuchFile:
836
raise errors.NotBranchError(path=transport.base)
838
raise errors.UnknownFormatError(format_string)
841
def get_default_format(klass):
842
"""Return the current default format."""
843
return klass._default_format
845
def get_format_string(self):
846
"""Return the ASCII format string that identifies this format."""
847
raise NotImplementedError(self.get_format_string)
849
def get_converter(self, format=None):
850
"""Return the converter to use to convert bzrdirs needing converts.
852
This returns a bzrlib.bzrdir.Converter object.
854
This should return the best upgrader to step this format towards the
855
current default format. In the case of plugins we can/shouold provide
856
some means for them to extend the range of returnable converters.
858
:param format: Optional format to override the default foramt of the
861
raise NotImplementedError(self.get_converter)
863
def initialize(self, url):
864
"""Create a bzr control dir at this url and return an opened copy."""
865
# Since we don't have a .bzr directory, inherit the
866
# mode from the root directory
867
t = get_transport(url)
868
temp_control = LockableFiles(t, '', TransportLock)
869
temp_control._transport.mkdir('.bzr',
870
# FIXME: RBC 20060121 dont peek under
872
mode=temp_control._dir_mode)
873
file_mode = temp_control._file_mode
875
mutter('created control directory in ' + t.base)
876
control = t.clone('.bzr')
877
lock_file = 'branch-lock'
878
utf8_files = [('README',
879
"This is a Bazaar-NG control directory.\n"
880
"Do not change any files in this directory.\n"),
881
('branch-format', self.get_format_string()),
883
# NB: no need to escape relative paths that are url safe.
884
control_files = LockableFiles(control, lock_file, TransportLock)
885
control_files.create_lock()
886
control_files.lock_write()
888
for file, content in utf8_files:
889
control_files.put_utf8(file, content)
891
control_files.unlock()
892
return self.open(t, _found=True)
894
def is_supported(self):
895
"""Is this format supported?
897
Supported formats must be initializable and openable.
898
Unsupported formats may not support initialization or committing or
899
some other features depending on the reason for not being supported.
903
def open(self, transport, _found=False):
904
"""Return an instance of this format for the dir transport points at.
906
_found is a private parameter, do not use it.
909
assert isinstance(BzrDirFormat.find_format(transport),
911
return self._open(transport)
913
def _open(self, transport):
914
"""Template method helper for opening BzrDirectories.
916
This performs the actual open and any additional logic or parameter
919
raise NotImplementedError(self._open)
922
def register_format(klass, format):
923
klass._formats[format.get_format_string()] = format
926
def set_default_format(klass, format):
927
klass._default_format = format
930
return self.get_format_string()[:-1]
933
def unregister_format(klass, format):
934
assert klass._formats[format.get_format_string()] is format
935
del klass._formats[format.get_format_string()]
938
class BzrDirFormat4(BzrDirFormat):
941
This format is a combined format for working tree, branch and repository.
943
- Format 1 working trees [always]
944
- Format 4 branches [always]
945
- Format 4 repositories [always]
947
This format is deprecated: it indexes texts using a text it which is
948
removed in format 5; write support for this format has been removed.
951
def get_format_string(self):
952
"""See BzrDirFormat.get_format_string()."""
953
return "Bazaar-NG branch, format 0.0.4\n"
955
def get_converter(self, format=None):
956
"""See BzrDirFormat.get_converter()."""
957
# there is one and only one upgrade path here.
958
return ConvertBzrDir4To5()
960
def initialize(self, url):
961
"""Format 4 branches cannot be created."""
962
raise errors.UninitializableFormat(self)
964
def is_supported(self):
965
"""Format 4 is not supported.
967
It is not supported because the model changed from 4 to 5 and the
968
conversion logic is expensive - so doing it on the fly was not
973
def _open(self, transport):
974
"""See BzrDirFormat._open."""
975
return BzrDir4(transport, self)
977
def __return_repository_format(self):
978
"""Circular import protection."""
979
from bzrlib.repository import RepositoryFormat4
980
return RepositoryFormat4(self)
981
repository_format = property(__return_repository_format)
984
class BzrDirFormat5(BzrDirFormat):
985
"""Bzr control format 5.
987
This format is a combined format for working tree, branch and repository.
989
- Format 2 working trees [always]
990
- Format 4 branches [always]
991
- Format 5 repositories [always]
992
Unhashed stores in the repository.
995
def get_format_string(self):
996
"""See BzrDirFormat.get_format_string()."""
997
return "Bazaar-NG branch, format 5\n"
999
def get_converter(self, format=None):
1000
"""See BzrDirFormat.get_converter()."""
1001
# there is one and only one upgrade path here.
1002
return ConvertBzrDir5To6()
1004
def initialize(self, url, _cloning=False):
1005
"""Format 5 dirs always have working tree, branch and repository.
1007
Except when they are being cloned.
1009
from bzrlib.branch import BzrBranchFormat4
1010
from bzrlib.repository import RepositoryFormat5
1011
from bzrlib.workingtree import WorkingTreeFormat2
1012
result = super(BzrDirFormat5, self).initialize(url)
1013
RepositoryFormat5().initialize(result, _internal=True)
1015
BzrBranchFormat4().initialize(result)
1016
WorkingTreeFormat2().initialize(result)
1019
def _open(self, transport):
1020
"""See BzrDirFormat._open."""
1021
return BzrDir5(transport, self)
1023
def __return_repository_format(self):
1024
"""Circular import protection."""
1025
from bzrlib.repository import RepositoryFormat5
1026
return RepositoryFormat5(self)
1027
repository_format = property(__return_repository_format)
1030
class BzrDirFormat6(BzrDirFormat):
1031
"""Bzr control format 6.
1033
This format is a combined format for working tree, branch and repository.
1035
- Format 2 working trees [always]
1036
- Format 4 branches [always]
1037
- Format 6 repositories [always]
1040
def get_format_string(self):
1041
"""See BzrDirFormat.get_format_string()."""
1042
return "Bazaar-NG branch, format 6\n"
1044
def get_converter(self, format=None):
1045
"""See BzrDirFormat.get_converter()."""
1046
# there is one and only one upgrade path here.
1047
return ConvertBzrDir6ToMeta()
1049
def initialize(self, url, _cloning=False):
1050
"""Format 6 dirs always have working tree, branch and repository.
1052
Except when they are being cloned.
1054
from bzrlib.branch import BzrBranchFormat4
1055
from bzrlib.repository import RepositoryFormat6
1056
from bzrlib.workingtree import WorkingTreeFormat2
1057
result = super(BzrDirFormat6, self).initialize(url)
1058
RepositoryFormat6().initialize(result, _internal=True)
1060
BzrBranchFormat4().initialize(result)
1062
WorkingTreeFormat2().initialize(result)
1063
except errors.NotLocalUrl:
1064
# emulate pre-check behaviour for working tree and silently
1069
def _open(self, transport):
1070
"""See BzrDirFormat._open."""
1071
return BzrDir6(transport, self)
1073
def __return_repository_format(self):
1074
"""Circular import protection."""
1075
from bzrlib.repository import RepositoryFormat6
1076
return RepositoryFormat6(self)
1077
repository_format = property(__return_repository_format)
1080
class BzrDirMetaFormat1(BzrDirFormat):
1081
"""Bzr meta control format 1
1083
This is the first format with split out working tree, branch and repository
1086
- Format 3 working trees [optional]
1087
- Format 5 branches [optional]
1088
- Format 7 repositories [optional]
1091
def get_converter(self, format=None):
1092
"""See BzrDirFormat.get_converter()."""
1094
format = BzrDirFormat.get_default_format()
1095
if not isinstance(self, format.__class__):
1096
# converting away from metadir is not implemented
1097
raise NotImplementedError(self.get_converter)
1098
return ConvertMetaToMeta(format)
1100
def get_format_string(self):
1101
"""See BzrDirFormat.get_format_string()."""
1102
return "Bazaar-NG meta directory, format 1\n"
1104
def _open(self, transport):
1105
"""See BzrDirFormat._open."""
1106
return BzrDirMeta1(transport, self)
1108
def __return_repository_format(self):
1109
"""Circular import protection."""
1110
if getattr(self, '_repository_format', None):
1111
return self._repository_format
1112
from bzrlib.repository import RepositoryFormat
1113
return RepositoryFormat.get_default_format()
1115
def __set_repository_format(self, value):
1116
"""Allow changint the repository format for metadir formats."""
1117
self._repository_format = value
1118
repository_format = property(__return_repository_format, __set_repository_format)
1121
BzrDirFormat.register_format(BzrDirFormat4())
1122
BzrDirFormat.register_format(BzrDirFormat5())
1123
BzrDirFormat.register_format(BzrDirMetaFormat1())
1124
__default_format = BzrDirFormat6()
1125
BzrDirFormat.register_format(__default_format)
1126
BzrDirFormat.set_default_format(__default_format)
1129
class BzrDirTestProviderAdapter(object):
1130
"""A tool to generate a suite testing multiple bzrdir formats at once.
1132
This is done by copying the test once for each transport and injecting
1133
the transport_server, transport_readonly_server, and bzrdir_format
1134
classes into each copy. Each copy is also given a new id() to make it
1138
def __init__(self, transport_server, transport_readonly_server, formats):
1139
self._transport_server = transport_server
1140
self._transport_readonly_server = transport_readonly_server
1141
self._formats = formats
1143
def adapt(self, test):
1144
result = TestSuite()
1145
for format in self._formats:
1146
new_test = deepcopy(test)
1147
new_test.transport_server = self._transport_server
1148
new_test.transport_readonly_server = self._transport_readonly_server
1149
new_test.bzrdir_format = format
1150
def make_new_test_id():
1151
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1152
return lambda: new_id
1153
new_test.id = make_new_test_id()
1154
result.addTest(new_test)
1158
class ScratchDir(BzrDir6):
1159
"""Special test class: a bzrdir that cleans up itself..
1161
>>> d = ScratchDir()
1162
>>> base = d.transport.base
1165
>>> b.transport.__del__()
1170
def __init__(self, files=[], dirs=[], transport=None):
1171
"""Make a test branch.
1173
This creates a temporary directory and runs init-tree in it.
1175
If any files are listed, they are created in the working copy.
1177
if transport is None:
1178
transport = bzrlib.transport.local.ScratchTransport()
1179
# local import for scope restriction
1180
BzrDirFormat6().initialize(transport.base)
1181
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1182
self.create_repository()
1183
self.create_branch()
1184
self.create_workingtree()
1186
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1188
# BzrBranch creates a clone to .bzr and then forgets about the
1189
# original transport. A ScratchTransport() deletes itself and
1190
# everything underneath it when it goes away, so we need to
1191
# grab a local copy to prevent that from happening
1192
self._transport = transport
1195
self._transport.mkdir(d)
1198
self._transport.put(f, 'content of %s' % f)
1202
>>> orig = ScratchDir(files=["file1", "file2"])
1203
>>> os.listdir(orig.base)
1204
[u'.bzr', u'file1', u'file2']
1205
>>> clone = orig.clone()
1206
>>> if os.name != 'nt':
1207
... os.path.samefile(orig.base, clone.base)
1209
... orig.base == clone.base
1212
>>> os.listdir(clone.base)
1213
[u'.bzr', u'file1', u'file2']
1215
from shutil import copytree
1216
from bzrlib.osutils import mkdtemp
1219
copytree(self.base, base, symlinks=True)
1221
transport=bzrlib.transport.local.ScratchTransport(base))
1224
class Converter(object):
1225
"""Converts a disk format object from one format to another."""
1227
def convert(self, to_convert, pb):
1228
"""Perform the conversion of to_convert, giving feedback via pb.
1230
:param to_convert: The disk object to convert.
1231
:param pb: a progress bar to use for progress information.
1234
def step(self, message):
1235
"""Update the pb by a step."""
1237
self.pb.update(message, self.count, self.total)
1240
class ConvertBzrDir4To5(Converter):
1241
"""Converts format 4 bzr dirs to format 5."""
1244
super(ConvertBzrDir4To5, self).__init__()
1245
self.converted_revs = set()
1246
self.absent_revisions = set()
1250
def convert(self, to_convert, pb):
1251
"""See Converter.convert()."""
1252
self.bzrdir = to_convert
1254
self.pb.note('starting upgrade from format 4 to 5')
1255
if isinstance(self.bzrdir.transport, LocalTransport):
1256
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1257
self._convert_to_weaves()
1258
return BzrDir.open(self.bzrdir.root_transport.base)
1260
def _convert_to_weaves(self):
1261
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1264
stat = self.bzrdir.transport.stat('weaves')
1265
if not S_ISDIR(stat.st_mode):
1266
self.bzrdir.transport.delete('weaves')
1267
self.bzrdir.transport.mkdir('weaves')
1268
except errors.NoSuchFile:
1269
self.bzrdir.transport.mkdir('weaves')
1270
self.inv_weave = Weave('inventory')
1271
# holds in-memory weaves for all files
1272
self.text_weaves = {}
1273
self.bzrdir.transport.delete('branch-format')
1274
self.branch = self.bzrdir.open_branch()
1275
self._convert_working_inv()
1276
rev_history = self.branch.revision_history()
1277
# to_read is a stack holding the revisions we still need to process;
1278
# appending to it adds new highest-priority revisions
1279
self.known_revisions = set(rev_history)
1280
self.to_read = rev_history[-1:]
1282
rev_id = self.to_read.pop()
1283
if (rev_id not in self.revisions
1284
and rev_id not in self.absent_revisions):
1285
self._load_one_rev(rev_id)
1287
to_import = self._make_order()
1288
for i, rev_id in enumerate(to_import):
1289
self.pb.update('converting revision', i, len(to_import))
1290
self._convert_one_rev(rev_id)
1292
self._write_all_weaves()
1293
self._write_all_revs()
1294
self.pb.note('upgraded to weaves:')
1295
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1296
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1297
self.pb.note(' %6d texts', self.text_count)
1298
self._cleanup_spare_files_after_format4()
1299
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1301
def _cleanup_spare_files_after_format4(self):
1302
# FIXME working tree upgrade foo.
1303
for n in 'merged-patches', 'pending-merged-patches':
1305
## assert os.path.getsize(p) == 0
1306
self.bzrdir.transport.delete(n)
1307
except errors.NoSuchFile:
1309
self.bzrdir.transport.delete_tree('inventory-store')
1310
self.bzrdir.transport.delete_tree('text-store')
1312
def _convert_working_inv(self):
1313
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1314
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1315
# FIXME inventory is a working tree change.
1316
self.branch.control_files.put('inventory', new_inv_xml)
1318
def _write_all_weaves(self):
1319
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1320
weave_transport = self.bzrdir.transport.clone('weaves')
1321
weaves = WeaveStore(weave_transport, prefixed=False)
1322
transaction = PassThroughTransaction()
1324
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1327
for file_id, file_weave in self.text_weaves.items():
1328
self.pb.update('writing weave', i, len(self.text_weaves))
1329
weaves.put_weave(file_id, file_weave, transaction)
1334
def _write_all_revs(self):
1335
"""Write all revisions out in new form."""
1336
self.bzrdir.transport.delete_tree('revision-store')
1337
self.bzrdir.transport.mkdir('revision-store')
1338
revision_transport = self.bzrdir.transport.clone('revision-store')
1340
revision_store = TextStore(revision_transport,
1344
for i, rev_id in enumerate(self.converted_revs):
1345
self.pb.update('write revision', i, len(self.converted_revs))
1346
rev_tmp = StringIO()
1347
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1349
revision_store.add(rev_tmp, rev_id)
1353
def _load_one_rev(self, rev_id):
1354
"""Load a revision object into memory.
1356
Any parents not either loaded or abandoned get queued to be
1358
self.pb.update('loading revision',
1359
len(self.revisions),
1360
len(self.known_revisions))
1361
if not self.branch.repository.revision_store.has_id(rev_id):
1363
self.pb.note('revision {%s} not present in branch; '
1364
'will be converted as a ghost',
1366
self.absent_revisions.add(rev_id)
1368
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1369
rev = serializer_v4.read_revision_from_string(rev_xml)
1370
for parent_id in rev.parent_ids:
1371
self.known_revisions.add(parent_id)
1372
self.to_read.append(parent_id)
1373
self.revisions[rev_id] = rev
1375
def _load_old_inventory(self, rev_id):
1376
assert rev_id not in self.converted_revs
1377
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1378
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1379
rev = self.revisions[rev_id]
1380
if rev.inventory_sha1:
1381
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1382
'inventory sha mismatch for {%s}' % rev_id
1385
def _load_updated_inventory(self, rev_id):
1386
assert rev_id in self.converted_revs
1387
inv_xml = self.inv_weave.get_text(rev_id)
1388
inv = serializer_v5.read_inventory_from_string(inv_xml)
1391
def _convert_one_rev(self, rev_id):
1392
"""Convert revision and all referenced objects to new format."""
1393
rev = self.revisions[rev_id]
1394
inv = self._load_old_inventory(rev_id)
1395
present_parents = [p for p in rev.parent_ids
1396
if p not in self.absent_revisions]
1397
self._convert_revision_contents(rev, inv, present_parents)
1398
self._store_new_weave(rev, inv, present_parents)
1399
self.converted_revs.add(rev_id)
1401
def _store_new_weave(self, rev, inv, present_parents):
1402
# the XML is now updated with text versions
1406
if ie.kind == 'root_directory':
1408
assert hasattr(ie, 'revision'), \
1409
'no revision on {%s} in {%s}' % \
1410
(file_id, rev.revision_id)
1411
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1412
new_inv_sha1 = sha_string(new_inv_xml)
1413
self.inv_weave.add(rev.revision_id,
1415
new_inv_xml.splitlines(True),
1417
rev.inventory_sha1 = new_inv_sha1
1419
def _convert_revision_contents(self, rev, inv, present_parents):
1420
"""Convert all the files within a revision.
1422
Also upgrade the inventory to refer to the text revision ids."""
1423
rev_id = rev.revision_id
1424
mutter('converting texts of revision {%s}',
1426
parent_invs = map(self._load_updated_inventory, present_parents)
1429
self._convert_file_version(rev, ie, parent_invs)
1431
def _convert_file_version(self, rev, ie, parent_invs):
1432
"""Convert one version of one file.
1434
The file needs to be added into the weave if it is a merge
1435
of >=2 parents or if it's changed from its parent.
1437
if ie.kind == 'root_directory':
1439
file_id = ie.file_id
1440
rev_id = rev.revision_id
1441
w = self.text_weaves.get(file_id)
1444
self.text_weaves[file_id] = w
1445
text_changed = False
1446
previous_entries = ie.find_previous_heads(parent_invs, w)
1447
for old_revision in previous_entries:
1448
# if this fails, its a ghost ?
1449
assert old_revision in self.converted_revs
1450
self.snapshot_ie(previous_entries, ie, w, rev_id)
1452
assert getattr(ie, 'revision', None) is not None
1454
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1455
# TODO: convert this logic, which is ~= snapshot to
1456
# a call to:. This needs the path figured out. rather than a work_tree
1457
# a v4 revision_tree can be given, or something that looks enough like
1458
# one to give the file content to the entry if it needs it.
1459
# and we need something that looks like a weave store for snapshot to
1461
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1462
if len(previous_revisions) == 1:
1463
previous_ie = previous_revisions.values()[0]
1464
if ie._unchanged(previous_ie):
1465
ie.revision = previous_ie.revision
1467
parent_indexes = map(w.lookup, previous_revisions)
1469
text = self.branch.repository.text_store.get(ie.text_id)
1470
file_lines = text.readlines()
1471
assert sha_strings(file_lines) == ie.text_sha1
1472
assert sum(map(len, file_lines)) == ie.text_size
1473
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1474
self.text_count += 1
1476
w.add(rev_id, parent_indexes, [], None)
1477
ie.revision = rev_id
1479
def _make_order(self):
1480
"""Return a suitable order for importing revisions.
1482
The order must be such that an revision is imported after all
1483
its (present) parents.
1485
todo = set(self.revisions.keys())
1486
done = self.absent_revisions.copy()
1489
# scan through looking for a revision whose parents
1491
for rev_id in sorted(list(todo)):
1492
rev = self.revisions[rev_id]
1493
parent_ids = set(rev.parent_ids)
1494
if parent_ids.issubset(done):
1495
# can take this one now
1496
order.append(rev_id)
1502
class ConvertBzrDir5To6(Converter):
1503
"""Converts format 5 bzr dirs to format 6."""
1505
def convert(self, to_convert, pb):
1506
"""See Converter.convert()."""
1507
self.bzrdir = to_convert
1509
self.pb.note('starting upgrade from format 5 to 6')
1510
self._convert_to_prefixed()
1511
return BzrDir.open(self.bzrdir.root_transport.base)
1513
def _convert_to_prefixed(self):
1514
from bzrlib.store import hash_prefix
1515
self.bzrdir.transport.delete('branch-format')
1516
for store_name in ["weaves", "revision-store"]:
1517
self.pb.note("adding prefixes to %s" % store_name)
1518
store_transport = self.bzrdir.transport.clone(store_name)
1519
for filename in store_transport.list_dir('.'):
1520
if (filename.endswith(".weave") or
1521
filename.endswith(".gz") or
1522
filename.endswith(".sig")):
1523
file_id = os.path.splitext(filename)[0]
1526
prefix_dir = hash_prefix(file_id)
1527
# FIXME keep track of the dirs made RBC 20060121
1529
store_transport.move(filename, prefix_dir + '/' + filename)
1530
except errors.NoSuchFile: # catches missing dirs strangely enough
1531
store_transport.mkdir(prefix_dir)
1532
store_transport.move(filename, prefix_dir + '/' + filename)
1533
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1536
class ConvertBzrDir6ToMeta(Converter):
1537
"""Converts format 6 bzr dirs to metadirs."""
1539
def convert(self, to_convert, pb):
1540
"""See Converter.convert()."""
1541
self.bzrdir = to_convert
1544
self.total = 20 # the steps we know about
1545
self.garbage_inventories = []
1547
self.pb.note('starting upgrade from format 6 to metadir')
1548
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1549
# its faster to move specific files around than to open and use the apis...
1550
# first off, nuke ancestry.weave, it was never used.
1552
self.step('Removing ancestry.weave')
1553
self.bzrdir.transport.delete('ancestry.weave')
1554
except errors.NoSuchFile:
1556
# find out whats there
1557
self.step('Finding branch files')
1558
last_revision = self.bzrdir.open_workingtree().last_revision()
1559
bzrcontents = self.bzrdir.transport.list_dir('.')
1560
for name in bzrcontents:
1561
if name.startswith('basis-inventory.'):
1562
self.garbage_inventories.append(name)
1563
# create new directories for repository, working tree and branch
1564
dir_mode = self.bzrdir._control_files._dir_mode
1565
self.file_mode = self.bzrdir._control_files._file_mode
1566
repository_names = [('inventory.weave', True),
1567
('revision-store', True),
1569
self.step('Upgrading repository ')
1570
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1571
self.make_lock('repository')
1572
# we hard code the formats here because we are converting into
1573
# the meta format. The meta format upgrader can take this to a
1574
# future format within each component.
1575
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1576
for entry in repository_names:
1577
self.move_entry('repository', entry)
1579
self.step('Upgrading branch ')
1580
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1581
self.make_lock('branch')
1582
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1583
branch_files = [('revision-history', True),
1584
('branch-name', True),
1586
for entry in branch_files:
1587
self.move_entry('branch', entry)
1589
self.step('Upgrading working tree')
1590
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1591
self.make_lock('checkout')
1592
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1593
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1594
checkout_files = [('pending-merges', True),
1595
('inventory', True),
1596
('stat-cache', False)]
1597
for entry in checkout_files:
1598
self.move_entry('checkout', entry)
1599
if last_revision is not None:
1600
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1602
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1603
return BzrDir.open(self.bzrdir.root_transport.base)
1605
def make_lock(self, name):
1606
"""Make a lock for the new control dir name."""
1607
self.step('Make %s lock' % name)
1608
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1610
def move_entry(self, new_dir, entry):
1611
"""Move then entry name into new_dir."""
1613
mandatory = entry[1]
1614
self.step('Moving %s' % name)
1616
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1617
except errors.NoSuchFile:
1621
def put_format(self, dirname, format):
1622
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1625
class ConvertMetaToMeta(Converter):
1626
"""Converts the components of metadirs."""
1628
def __init__(self, target_format):
1629
"""Create a metadir to metadir converter.
1631
:param target_format: The final metadir format that is desired.
1633
self.target_format = target_format
1635
def convert(self, to_convert, pb):
1636
"""See Converter.convert()."""
1637
self.bzrdir = to_convert
1641
self.step('checking repository format')
1643
repo = self.bzrdir.open_repository()
1644
except errors.NoRepositoryPresent:
1647
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1648
from bzrlib.repository import CopyConverter
1649
self.pb.note('starting repository conversion')
1650
converter = CopyConverter(self.target_format.repository_format)
1651
converter.convert(repo, pb)