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.revision.text import TextRevisionStore
40
from bzrlib.store.text import TextStore
41
from bzrlib.store.versioned import WeaveStore
42
from bzrlib.symbol_versioning import *
43
from bzrlib.trace import mutter
44
from bzrlib.transactions import WriteTransaction
45
from bzrlib.transport import get_transport
46
from bzrlib.transport.local import LocalTransport
47
from bzrlib.weave import 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.
555
WorkingTreeFormat2().initialize(result)
556
except errors.NotLocalUrl:
557
# but we canot do it for remote trees.
561
def create_branch(self):
562
"""See BzrDir.create_branch."""
563
return self.open_branch()
565
def create_repository(self, shared=False):
566
"""See BzrDir.create_repository."""
568
raise errors.IncompatibleFormat('shared repository', self._format)
569
return self.open_repository()
571
def create_workingtree(self, revision_id=None):
572
"""See BzrDir.create_workingtree."""
573
# this looks buggy but is not -really-
574
# clone and sprout will have set the revision_id
575
# and that will have set it for us, its only
576
# specific uses of create_workingtree in isolation
577
# that can do wonky stuff here, and that only
578
# happens for creating checkouts, which cannot be
579
# done on this format anyway. So - acceptable wart.
580
result = self.open_workingtree()
581
if revision_id is not None:
582
result.set_last_revision(revision_id)
585
def get_branch_transport(self, branch_format):
586
"""See BzrDir.get_branch_transport()."""
587
if branch_format is None:
588
return self.transport
590
branch_format.get_format_string()
591
except NotImplementedError:
592
return self.transport
593
raise errors.IncompatibleFormat(branch_format, self._format)
595
def get_repository_transport(self, repository_format):
596
"""See BzrDir.get_repository_transport()."""
597
if repository_format is None:
598
return self.transport
600
repository_format.get_format_string()
601
except NotImplementedError:
602
return self.transport
603
raise errors.IncompatibleFormat(repository_format, self._format)
605
def get_workingtree_transport(self, workingtree_format):
606
"""See BzrDir.get_workingtree_transport()."""
607
if workingtree_format is None:
608
return self.transport
610
workingtree_format.get_format_string()
611
except NotImplementedError:
612
return self.transport
613
raise errors.IncompatibleFormat(workingtree_format, self._format)
615
def needs_format_conversion(self, format=None):
616
"""See BzrDir.needs_format_conversion()."""
617
# if the format is not the same as the system default,
618
# an upgrade is needed.
620
format = BzrDirFormat.get_default_format()
621
return not isinstance(self._format, format.__class__)
623
def open_branch(self, unsupported=False):
624
"""See BzrDir.open_branch."""
625
from bzrlib.branch import BzrBranchFormat4
626
format = BzrBranchFormat4()
627
self._check_supported(format, unsupported)
628
return format.open(self, _found=True)
630
def sprout(self, url, revision_id=None, basis=None):
631
"""See BzrDir.sprout()."""
632
from bzrlib.workingtree import WorkingTreeFormat2
634
result = self._format.initialize(url, _cloning=True)
635
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
637
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
638
except errors.NoRepositoryPresent:
641
self.open_branch().sprout(result, revision_id=revision_id)
642
except errors.NotBranchError:
644
# we always want a working tree
645
WorkingTreeFormat2().initialize(result)
649
class BzrDir4(BzrDirPreSplitOut):
650
"""A .bzr version 4 control object.
652
This is a deprecated format and may be removed after sept 2006.
655
def create_repository(self, shared=False):
656
"""See BzrDir.create_repository."""
657
return self._format.repository_format.initialize(self, shared)
659
def needs_format_conversion(self, format=None):
660
"""Format 4 dirs are always in need of conversion."""
663
def open_repository(self):
664
"""See BzrDir.open_repository."""
665
from bzrlib.repository import RepositoryFormat4
666
return RepositoryFormat4().open(self, _found=True)
669
class BzrDir5(BzrDirPreSplitOut):
670
"""A .bzr version 5 control object.
672
This is a deprecated format and may be removed after sept 2006.
675
def open_repository(self):
676
"""See BzrDir.open_repository."""
677
from bzrlib.repository import RepositoryFormat5
678
return RepositoryFormat5().open(self, _found=True)
680
def open_workingtree(self, _unsupported=False):
681
"""See BzrDir.create_workingtree."""
682
from bzrlib.workingtree import WorkingTreeFormat2
683
return WorkingTreeFormat2().open(self, _found=True)
686
class BzrDir6(BzrDirPreSplitOut):
687
"""A .bzr version 6 control object.
689
This is a deprecated format and may be removed after sept 2006.
692
def open_repository(self):
693
"""See BzrDir.open_repository."""
694
from bzrlib.repository import RepositoryFormat6
695
return RepositoryFormat6().open(self, _found=True)
697
def open_workingtree(self, _unsupported=False):
698
"""See BzrDir.create_workingtree."""
699
from bzrlib.workingtree import WorkingTreeFormat2
700
return WorkingTreeFormat2().open(self, _found=True)
703
class BzrDirMeta1(BzrDir):
704
"""A .bzr meta version 1 control object.
706
This is the first control object where the
707
individual formats are really split out.
710
def can_convert_format(self):
711
"""See BzrDir.can_convert_format()."""
714
def create_branch(self):
715
"""See BzrDir.create_branch."""
716
from bzrlib.branch import BranchFormat
717
return BranchFormat.get_default_format().initialize(self)
719
def create_repository(self, shared=False):
720
"""See BzrDir.create_repository."""
721
return self._format.repository_format.initialize(self, shared)
723
def create_workingtree(self, revision_id=None):
724
"""See BzrDir.create_workingtree."""
725
from bzrlib.workingtree import WorkingTreeFormat
726
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
728
def get_branch_transport(self, branch_format):
729
"""See BzrDir.get_branch_transport()."""
730
if branch_format is None:
731
return self.transport.clone('branch')
733
branch_format.get_format_string()
734
except NotImplementedError:
735
raise errors.IncompatibleFormat(branch_format, self._format)
737
self.transport.mkdir('branch')
738
except errors.FileExists:
740
return self.transport.clone('branch')
742
def get_repository_transport(self, repository_format):
743
"""See BzrDir.get_repository_transport()."""
744
if repository_format is None:
745
return self.transport.clone('repository')
747
repository_format.get_format_string()
748
except NotImplementedError:
749
raise errors.IncompatibleFormat(repository_format, self._format)
751
self.transport.mkdir('repository')
752
except errors.FileExists:
754
return self.transport.clone('repository')
756
def get_workingtree_transport(self, workingtree_format):
757
"""See BzrDir.get_workingtree_transport()."""
758
if workingtree_format is None:
759
return self.transport.clone('checkout')
761
workingtree_format.get_format_string()
762
except NotImplementedError:
763
raise errors.IncompatibleFormat(workingtree_format, self._format)
765
self.transport.mkdir('checkout')
766
except errors.FileExists:
768
return self.transport.clone('checkout')
770
def needs_format_conversion(self, format=None):
771
"""See BzrDir.needs_format_conversion()."""
773
format = BzrDirFormat.get_default_format()
774
if not isinstance(self._format, format.__class__):
775
# it is not a meta dir format, conversion is needed.
777
# we might want to push this down to the repository?
779
if not isinstance(self.open_repository()._format,
780
format.repository_format.__class__):
781
# the repository needs an upgrade.
783
except errors.NoRepositoryPresent:
785
# currently there are no other possible conversions for meta1 formats.
788
def open_branch(self, unsupported=False):
789
"""See BzrDir.open_branch."""
790
from bzrlib.branch import BranchFormat
791
format = BranchFormat.find_format(self)
792
self._check_supported(format, unsupported)
793
return format.open(self, _found=True)
795
def open_repository(self, unsupported=False):
796
"""See BzrDir.open_repository."""
797
from bzrlib.repository import RepositoryFormat
798
format = RepositoryFormat.find_format(self)
799
self._check_supported(format, unsupported)
800
return format.open(self, _found=True)
802
def open_workingtree(self, unsupported=False):
803
"""See BzrDir.open_workingtree."""
804
from bzrlib.workingtree import WorkingTreeFormat
805
format = WorkingTreeFormat.find_format(self)
806
self._check_supported(format, unsupported)
807
return format.open(self, _found=True)
810
class BzrDirFormat(object):
811
"""An encapsulation of the initialization and open routines for a format.
813
Formats provide three things:
814
* An initialization routine,
818
Formats are placed in an dict by their format string for reference
819
during bzrdir opening. These should be subclasses of BzrDirFormat
822
Once a format is deprecated, just deprecate the initialize and open
823
methods on the format class. Do not deprecate the object, as the
824
object will be created every system load.
827
_default_format = None
828
"""The default format used for new .bzr dirs."""
831
"""The known formats."""
834
def find_format(klass, transport):
835
"""Return the format registered for URL."""
837
format_string = transport.get(".bzr/branch-format").read()
838
return klass._formats[format_string]
839
except errors.NoSuchFile:
840
raise errors.NotBranchError(path=transport.base)
842
raise errors.UnknownFormatError(format_string)
845
def get_default_format(klass):
846
"""Return the current default format."""
847
return klass._default_format
849
def get_format_string(self):
850
"""Return the ASCII format string that identifies this format."""
851
raise NotImplementedError(self.get_format_string)
853
def get_converter(self, format=None):
854
"""Return the converter to use to convert bzrdirs needing converts.
856
This returns a bzrlib.bzrdir.Converter object.
858
This should return the best upgrader to step this format towards the
859
current default format. In the case of plugins we can/shouold provide
860
some means for them to extend the range of returnable converters.
862
:param format: Optional format to override the default foramt of the
865
raise NotImplementedError(self.get_converter)
867
def initialize(self, url):
868
"""Create a bzr control dir at this url and return an opened copy."""
869
# Since we don't have a .bzr directory, inherit the
870
# mode from the root directory
871
t = get_transport(url)
872
temp_control = LockableFiles(t, '', TransportLock)
873
temp_control._transport.mkdir('.bzr',
874
# FIXME: RBC 20060121 dont peek under
876
mode=temp_control._dir_mode)
877
file_mode = temp_control._file_mode
879
mutter('created control directory in ' + t.base)
880
control = t.clone('.bzr')
881
lock_file = 'branch-lock'
882
utf8_files = [('README',
883
"This is a Bazaar-NG control directory.\n"
884
"Do not change any files in this directory.\n"),
885
('branch-format', self.get_format_string()),
887
# NB: no need to escape relative paths that are url safe.
888
control_files = LockableFiles(control, lock_file, TransportLock)
889
control_files.create_lock()
890
control_files.lock_write()
892
for file, content in utf8_files:
893
control_files.put_utf8(file, content)
895
control_files.unlock()
896
return self.open(t, _found=True)
898
def is_supported(self):
899
"""Is this format supported?
901
Supported formats must be initializable and openable.
902
Unsupported formats may not support initialization or committing or
903
some other features depending on the reason for not being supported.
907
def open(self, transport, _found=False):
908
"""Return an instance of this format for the dir transport points at.
910
_found is a private parameter, do not use it.
913
assert isinstance(BzrDirFormat.find_format(transport),
915
return self._open(transport)
917
def _open(self, transport):
918
"""Template method helper for opening BzrDirectories.
920
This performs the actual open and any additional logic or parameter
923
raise NotImplementedError(self._open)
926
def register_format(klass, format):
927
klass._formats[format.get_format_string()] = format
930
def set_default_format(klass, format):
931
klass._default_format = format
934
return self.get_format_string()[:-1]
937
def unregister_format(klass, format):
938
assert klass._formats[format.get_format_string()] is format
939
del klass._formats[format.get_format_string()]
942
class BzrDirFormat4(BzrDirFormat):
945
This format is a combined format for working tree, branch and repository.
947
- Format 1 working trees [always]
948
- Format 4 branches [always]
949
- Format 4 repositories [always]
951
This format is deprecated: it indexes texts using a text it which is
952
removed in format 5; write support for this format has been removed.
955
def get_format_string(self):
956
"""See BzrDirFormat.get_format_string()."""
957
return "Bazaar-NG branch, format 0.0.4\n"
959
def get_converter(self, format=None):
960
"""See BzrDirFormat.get_converter()."""
961
# there is one and only one upgrade path here.
962
return ConvertBzrDir4To5()
964
def initialize(self, url):
965
"""Format 4 branches cannot be created."""
966
raise errors.UninitializableFormat(self)
968
def is_supported(self):
969
"""Format 4 is not supported.
971
It is not supported because the model changed from 4 to 5 and the
972
conversion logic is expensive - so doing it on the fly was not
977
def _open(self, transport):
978
"""See BzrDirFormat._open."""
979
return BzrDir4(transport, self)
981
def __return_repository_format(self):
982
"""Circular import protection."""
983
from bzrlib.repository import RepositoryFormat4
984
return RepositoryFormat4(self)
985
repository_format = property(__return_repository_format)
988
class BzrDirFormat5(BzrDirFormat):
989
"""Bzr control format 5.
991
This format is a combined format for working tree, branch and repository.
993
- Format 2 working trees [always]
994
- Format 4 branches [always]
995
- Format 5 repositories [always]
996
Unhashed stores in the repository.
999
def get_format_string(self):
1000
"""See BzrDirFormat.get_format_string()."""
1001
return "Bazaar-NG branch, format 5\n"
1003
def get_converter(self, format=None):
1004
"""See BzrDirFormat.get_converter()."""
1005
# there is one and only one upgrade path here.
1006
return ConvertBzrDir5To6()
1008
def initialize(self, url, _cloning=False):
1009
"""Format 5 dirs always have working tree, branch and repository.
1011
Except when they are being cloned.
1013
from bzrlib.branch import BzrBranchFormat4
1014
from bzrlib.repository import RepositoryFormat5
1015
from bzrlib.workingtree import WorkingTreeFormat2
1016
result = super(BzrDirFormat5, self).initialize(url)
1017
RepositoryFormat5().initialize(result, _internal=True)
1019
BzrBranchFormat4().initialize(result)
1020
WorkingTreeFormat2().initialize(result)
1023
def _open(self, transport):
1024
"""See BzrDirFormat._open."""
1025
return BzrDir5(transport, self)
1027
def __return_repository_format(self):
1028
"""Circular import protection."""
1029
from bzrlib.repository import RepositoryFormat5
1030
return RepositoryFormat5(self)
1031
repository_format = property(__return_repository_format)
1034
class BzrDirFormat6(BzrDirFormat):
1035
"""Bzr control format 6.
1037
This format is a combined format for working tree, branch and repository.
1039
- Format 2 working trees [always]
1040
- Format 4 branches [always]
1041
- Format 6 repositories [always]
1044
def get_format_string(self):
1045
"""See BzrDirFormat.get_format_string()."""
1046
return "Bazaar-NG branch, format 6\n"
1048
def get_converter(self, format=None):
1049
"""See BzrDirFormat.get_converter()."""
1050
# there is one and only one upgrade path here.
1051
return ConvertBzrDir6ToMeta()
1053
def initialize(self, url, _cloning=False):
1054
"""Format 6 dirs always have working tree, branch and repository.
1056
Except when they are being cloned.
1058
from bzrlib.branch import BzrBranchFormat4
1059
from bzrlib.repository import RepositoryFormat6
1060
from bzrlib.workingtree import WorkingTreeFormat2
1061
result = super(BzrDirFormat6, self).initialize(url)
1062
RepositoryFormat6().initialize(result, _internal=True)
1064
BzrBranchFormat4().initialize(result)
1066
WorkingTreeFormat2().initialize(result)
1067
except errors.NotLocalUrl:
1068
# emulate pre-check behaviour for working tree and silently
1073
def _open(self, transport):
1074
"""See BzrDirFormat._open."""
1075
return BzrDir6(transport, self)
1077
def __return_repository_format(self):
1078
"""Circular import protection."""
1079
from bzrlib.repository import RepositoryFormat6
1080
return RepositoryFormat6(self)
1081
repository_format = property(__return_repository_format)
1084
class BzrDirMetaFormat1(BzrDirFormat):
1085
"""Bzr meta control format 1
1087
This is the first format with split out working tree, branch and repository
1090
- Format 3 working trees [optional]
1091
- Format 5 branches [optional]
1092
- Format 7 repositories [optional]
1095
def get_converter(self, format=None):
1096
"""See BzrDirFormat.get_converter()."""
1098
format = BzrDirFormat.get_default_format()
1099
if not isinstance(self, format.__class__):
1100
# converting away from metadir is not implemented
1101
raise NotImplementedError(self.get_converter)
1102
return ConvertMetaToMeta(format)
1104
def get_format_string(self):
1105
"""See BzrDirFormat.get_format_string()."""
1106
return "Bazaar-NG meta directory, format 1\n"
1108
def _open(self, transport):
1109
"""See BzrDirFormat._open."""
1110
return BzrDirMeta1(transport, self)
1112
def __return_repository_format(self):
1113
"""Circular import protection."""
1114
if getattr(self, '_repository_format', None):
1115
return self._repository_format
1116
from bzrlib.repository import RepositoryFormat
1117
return RepositoryFormat.get_default_format()
1119
def __set_repository_format(self, value):
1120
"""Allow changint the repository format for metadir formats."""
1121
self._repository_format = value
1122
repository_format = property(__return_repository_format, __set_repository_format)
1125
BzrDirFormat.register_format(BzrDirFormat4())
1126
BzrDirFormat.register_format(BzrDirFormat5())
1127
BzrDirFormat.register_format(BzrDirMetaFormat1())
1128
__default_format = BzrDirFormat6()
1129
BzrDirFormat.register_format(__default_format)
1130
BzrDirFormat.set_default_format(__default_format)
1133
class BzrDirTestProviderAdapter(object):
1134
"""A tool to generate a suite testing multiple bzrdir formats at once.
1136
This is done by copying the test once for each transport and injecting
1137
the transport_server, transport_readonly_server, and bzrdir_format
1138
classes into each copy. Each copy is also given a new id() to make it
1142
def __init__(self, transport_server, transport_readonly_server, formats):
1143
self._transport_server = transport_server
1144
self._transport_readonly_server = transport_readonly_server
1145
self._formats = formats
1147
def adapt(self, test):
1148
result = TestSuite()
1149
for format in self._formats:
1150
new_test = deepcopy(test)
1151
new_test.transport_server = self._transport_server
1152
new_test.transport_readonly_server = self._transport_readonly_server
1153
new_test.bzrdir_format = format
1154
def make_new_test_id():
1155
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1156
return lambda: new_id
1157
new_test.id = make_new_test_id()
1158
result.addTest(new_test)
1162
class ScratchDir(BzrDir6):
1163
"""Special test class: a bzrdir that cleans up itself..
1165
>>> d = ScratchDir()
1166
>>> base = d.transport.base
1169
>>> b.transport.__del__()
1174
def __init__(self, files=[], dirs=[], transport=None):
1175
"""Make a test branch.
1177
This creates a temporary directory and runs init-tree in it.
1179
If any files are listed, they are created in the working copy.
1181
if transport is None:
1182
transport = bzrlib.transport.local.ScratchTransport()
1183
# local import for scope restriction
1184
BzrDirFormat6().initialize(transport.base)
1185
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1186
self.create_repository()
1187
self.create_branch()
1188
self.create_workingtree()
1190
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1192
# BzrBranch creates a clone to .bzr and then forgets about the
1193
# original transport. A ScratchTransport() deletes itself and
1194
# everything underneath it when it goes away, so we need to
1195
# grab a local copy to prevent that from happening
1196
self._transport = transport
1199
self._transport.mkdir(d)
1202
self._transport.put(f, 'content of %s' % f)
1206
>>> orig = ScratchDir(files=["file1", "file2"])
1207
>>> os.listdir(orig.base)
1208
[u'.bzr', u'file1', u'file2']
1209
>>> clone = orig.clone()
1210
>>> if os.name != 'nt':
1211
... os.path.samefile(orig.base, clone.base)
1213
... orig.base == clone.base
1216
>>> os.listdir(clone.base)
1217
[u'.bzr', u'file1', u'file2']
1219
from shutil import copytree
1220
from bzrlib.osutils import mkdtemp
1223
copytree(self.base, base, symlinks=True)
1225
transport=bzrlib.transport.local.ScratchTransport(base))
1228
class Converter(object):
1229
"""Converts a disk format object from one format to another."""
1231
def convert(self, to_convert, pb):
1232
"""Perform the conversion of to_convert, giving feedback via pb.
1234
:param to_convert: The disk object to convert.
1235
:param pb: a progress bar to use for progress information.
1238
def step(self, message):
1239
"""Update the pb by a step."""
1241
self.pb.update(message, self.count, self.total)
1244
class ConvertBzrDir4To5(Converter):
1245
"""Converts format 4 bzr dirs to format 5."""
1248
super(ConvertBzrDir4To5, self).__init__()
1249
self.converted_revs = set()
1250
self.absent_revisions = set()
1254
def convert(self, to_convert, pb):
1255
"""See Converter.convert()."""
1256
self.bzrdir = to_convert
1258
self.pb.note('starting upgrade from format 4 to 5')
1259
if isinstance(self.bzrdir.transport, LocalTransport):
1260
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1261
self._convert_to_weaves()
1262
return BzrDir.open(self.bzrdir.root_transport.base)
1264
def _convert_to_weaves(self):
1265
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1268
stat = self.bzrdir.transport.stat('weaves')
1269
if not S_ISDIR(stat.st_mode):
1270
self.bzrdir.transport.delete('weaves')
1271
self.bzrdir.transport.mkdir('weaves')
1272
except errors.NoSuchFile:
1273
self.bzrdir.transport.mkdir('weaves')
1274
# deliberately not a WeaveFile as we want to build it up slowly.
1275
self.inv_weave = Weave('inventory')
1276
# holds in-memory weaves for all files
1277
self.text_weaves = {}
1278
self.bzrdir.transport.delete('branch-format')
1279
self.branch = self.bzrdir.open_branch()
1280
self._convert_working_inv()
1281
rev_history = self.branch.revision_history()
1282
# to_read is a stack holding the revisions we still need to process;
1283
# appending to it adds new highest-priority revisions
1284
self.known_revisions = set(rev_history)
1285
self.to_read = rev_history[-1:]
1287
rev_id = self.to_read.pop()
1288
if (rev_id not in self.revisions
1289
and rev_id not in self.absent_revisions):
1290
self._load_one_rev(rev_id)
1292
to_import = self._make_order()
1293
for i, rev_id in enumerate(to_import):
1294
self.pb.update('converting revision', i, len(to_import))
1295
self._convert_one_rev(rev_id)
1297
self._write_all_weaves()
1298
self._write_all_revs()
1299
self.pb.note('upgraded to weaves:')
1300
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1301
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1302
self.pb.note(' %6d texts', self.text_count)
1303
self._cleanup_spare_files_after_format4()
1304
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1306
def _cleanup_spare_files_after_format4(self):
1307
# FIXME working tree upgrade foo.
1308
for n in 'merged-patches', 'pending-merged-patches':
1310
## assert os.path.getsize(p) == 0
1311
self.bzrdir.transport.delete(n)
1312
except errors.NoSuchFile:
1314
self.bzrdir.transport.delete_tree('inventory-store')
1315
self.bzrdir.transport.delete_tree('text-store')
1317
def _convert_working_inv(self):
1318
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1319
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1320
# FIXME inventory is a working tree change.
1321
self.branch.control_files.put('inventory', new_inv_xml)
1323
def _write_all_weaves(self):
1324
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1325
weave_transport = self.bzrdir.transport.clone('weaves')
1326
weaves = WeaveStore(weave_transport, prefixed=False)
1327
transaction = WriteTransaction()
1331
for file_id, file_weave in self.text_weaves.items():
1332
self.pb.update('writing weave', i, len(self.text_weaves))
1333
weaves._put_weave(file_id, file_weave, transaction)
1335
self.pb.update('inventory', 0, 1)
1336
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1337
self.pb.update('inventory', 1, 1)
1341
def _write_all_revs(self):
1342
"""Write all revisions out in new form."""
1343
self.bzrdir.transport.delete_tree('revision-store')
1344
self.bzrdir.transport.mkdir('revision-store')
1345
revision_transport = self.bzrdir.transport.clone('revision-store')
1347
_revision_store = TextRevisionStore(TextStore(revision_transport,
1351
transaction = bzrlib.transactions.WriteTransaction()
1352
for i, rev_id in enumerate(self.converted_revs):
1353
self.pb.update('write revision', i, len(self.converted_revs))
1354
_revision_store.add_revision(self.revisions[rev_id], transaction)
1358
def _load_one_rev(self, rev_id):
1359
"""Load a revision object into memory.
1361
Any parents not either loaded or abandoned get queued to be
1363
self.pb.update('loading revision',
1364
len(self.revisions),
1365
len(self.known_revisions))
1366
if not self.branch.repository.has_revision(rev_id):
1368
self.pb.note('revision {%s} not present in branch; '
1369
'will be converted as a ghost',
1371
self.absent_revisions.add(rev_id)
1373
rev = self.branch.repository._revision_store.get_revision(rev_id,
1374
self.branch.repository.get_transaction())
1375
for parent_id in rev.parent_ids:
1376
self.known_revisions.add(parent_id)
1377
self.to_read.append(parent_id)
1378
self.revisions[rev_id] = rev
1380
def _load_old_inventory(self, rev_id):
1381
assert rev_id not in self.converted_revs
1382
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1383
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1384
rev = self.revisions[rev_id]
1385
if rev.inventory_sha1:
1386
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1387
'inventory sha mismatch for {%s}' % rev_id
1390
def _load_updated_inventory(self, rev_id):
1391
assert rev_id in self.converted_revs
1392
inv_xml = self.inv_weave.get_text(rev_id)
1393
inv = serializer_v5.read_inventory_from_string(inv_xml)
1396
def _convert_one_rev(self, rev_id):
1397
"""Convert revision and all referenced objects to new format."""
1398
rev = self.revisions[rev_id]
1399
inv = self._load_old_inventory(rev_id)
1400
present_parents = [p for p in rev.parent_ids
1401
if p not in self.absent_revisions]
1402
self._convert_revision_contents(rev, inv, present_parents)
1403
self._store_new_weave(rev, inv, present_parents)
1404
self.converted_revs.add(rev_id)
1406
def _store_new_weave(self, rev, inv, present_parents):
1407
# the XML is now updated with text versions
1411
if ie.kind == 'root_directory':
1413
assert hasattr(ie, 'revision'), \
1414
'no revision on {%s} in {%s}' % \
1415
(file_id, rev.revision_id)
1416
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1417
new_inv_sha1 = sha_string(new_inv_xml)
1418
self.inv_weave.add_lines(rev.revision_id,
1420
new_inv_xml.splitlines(True))
1421
rev.inventory_sha1 = new_inv_sha1
1423
def _convert_revision_contents(self, rev, inv, present_parents):
1424
"""Convert all the files within a revision.
1426
Also upgrade the inventory to refer to the text revision ids."""
1427
rev_id = rev.revision_id
1428
mutter('converting texts of revision {%s}',
1430
parent_invs = map(self._load_updated_inventory, present_parents)
1433
self._convert_file_version(rev, ie, parent_invs)
1435
def _convert_file_version(self, rev, ie, parent_invs):
1436
"""Convert one version of one file.
1438
The file needs to be added into the weave if it is a merge
1439
of >=2 parents or if it's changed from its parent.
1441
if ie.kind == 'root_directory':
1443
file_id = ie.file_id
1444
rev_id = rev.revision_id
1445
w = self.text_weaves.get(file_id)
1448
self.text_weaves[file_id] = w
1449
text_changed = False
1450
previous_entries = ie.find_previous_heads(parent_invs, w)
1451
for old_revision in previous_entries:
1452
# if this fails, its a ghost ?
1453
assert old_revision in self.converted_revs
1454
self.snapshot_ie(previous_entries, ie, w, rev_id)
1456
assert getattr(ie, 'revision', None) is not None
1458
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1459
# TODO: convert this logic, which is ~= snapshot to
1460
# a call to:. This needs the path figured out. rather than a work_tree
1461
# a v4 revision_tree can be given, or something that looks enough like
1462
# one to give the file content to the entry if it needs it.
1463
# and we need something that looks like a weave store for snapshot to
1465
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1466
if len(previous_revisions) == 1:
1467
previous_ie = previous_revisions.values()[0]
1468
if ie._unchanged(previous_ie):
1469
ie.revision = previous_ie.revision
1472
text = self.branch.repository.text_store.get(ie.text_id)
1473
file_lines = text.readlines()
1474
assert sha_strings(file_lines) == ie.text_sha1
1475
assert sum(map(len, file_lines)) == ie.text_size
1476
w.add_lines(rev_id, previous_revisions, file_lines)
1477
self.text_count += 1
1479
w.add_lines(rev_id, previous_revisions, [])
1480
ie.revision = rev_id
1482
def _make_order(self):
1483
"""Return a suitable order for importing revisions.
1485
The order must be such that an revision is imported after all
1486
its (present) parents.
1488
todo = set(self.revisions.keys())
1489
done = self.absent_revisions.copy()
1492
# scan through looking for a revision whose parents
1494
for rev_id in sorted(list(todo)):
1495
rev = self.revisions[rev_id]
1496
parent_ids = set(rev.parent_ids)
1497
if parent_ids.issubset(done):
1498
# can take this one now
1499
order.append(rev_id)
1505
class ConvertBzrDir5To6(Converter):
1506
"""Converts format 5 bzr dirs to format 6."""
1508
def convert(self, to_convert, pb):
1509
"""See Converter.convert()."""
1510
self.bzrdir = to_convert
1512
self.pb.note('starting upgrade from format 5 to 6')
1513
self._convert_to_prefixed()
1514
return BzrDir.open(self.bzrdir.root_transport.base)
1516
def _convert_to_prefixed(self):
1517
from bzrlib.store import hash_prefix
1518
self.bzrdir.transport.delete('branch-format')
1519
for store_name in ["weaves", "revision-store"]:
1520
self.pb.note("adding prefixes to %s" % store_name)
1521
store_transport = self.bzrdir.transport.clone(store_name)
1522
for filename in store_transport.list_dir('.'):
1523
if (filename.endswith(".weave") or
1524
filename.endswith(".gz") or
1525
filename.endswith(".sig")):
1526
file_id = os.path.splitext(filename)[0]
1529
prefix_dir = hash_prefix(file_id)
1530
# FIXME keep track of the dirs made RBC 20060121
1532
store_transport.move(filename, prefix_dir + '/' + filename)
1533
except errors.NoSuchFile: # catches missing dirs strangely enough
1534
store_transport.mkdir(prefix_dir)
1535
store_transport.move(filename, prefix_dir + '/' + filename)
1536
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1539
class ConvertBzrDir6ToMeta(Converter):
1540
"""Converts format 6 bzr dirs to metadirs."""
1542
def convert(self, to_convert, pb):
1543
"""See Converter.convert()."""
1544
self.bzrdir = to_convert
1547
self.total = 20 # the steps we know about
1548
self.garbage_inventories = []
1550
self.pb.note('starting upgrade from format 6 to metadir')
1551
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1552
# its faster to move specific files around than to open and use the apis...
1553
# first off, nuke ancestry.weave, it was never used.
1555
self.step('Removing ancestry.weave')
1556
self.bzrdir.transport.delete('ancestry.weave')
1557
except errors.NoSuchFile:
1559
# find out whats there
1560
self.step('Finding branch files')
1561
last_revision = self.bzrdir.open_workingtree().last_revision()
1562
bzrcontents = self.bzrdir.transport.list_dir('.')
1563
for name in bzrcontents:
1564
if name.startswith('basis-inventory.'):
1565
self.garbage_inventories.append(name)
1566
# create new directories for repository, working tree and branch
1567
dir_mode = self.bzrdir._control_files._dir_mode
1568
self.file_mode = self.bzrdir._control_files._file_mode
1569
repository_names = [('inventory.weave', True),
1570
('revision-store', True),
1572
self.step('Upgrading repository ')
1573
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1574
self.make_lock('repository')
1575
# we hard code the formats here because we are converting into
1576
# the meta format. The meta format upgrader can take this to a
1577
# future format within each component.
1578
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1579
for entry in repository_names:
1580
self.move_entry('repository', entry)
1582
self.step('Upgrading branch ')
1583
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1584
self.make_lock('branch')
1585
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1586
branch_files = [('revision-history', True),
1587
('branch-name', True),
1589
for entry in branch_files:
1590
self.move_entry('branch', entry)
1592
self.step('Upgrading working tree')
1593
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1594
self.make_lock('checkout')
1595
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1596
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1597
checkout_files = [('pending-merges', True),
1598
('inventory', True),
1599
('stat-cache', False)]
1600
for entry in checkout_files:
1601
self.move_entry('checkout', entry)
1602
if last_revision is not None:
1603
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1605
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1606
return BzrDir.open(self.bzrdir.root_transport.base)
1608
def make_lock(self, name):
1609
"""Make a lock for the new control dir name."""
1610
self.step('Make %s lock' % name)
1611
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1613
def move_entry(self, new_dir, entry):
1614
"""Move then entry name into new_dir."""
1616
mandatory = entry[1]
1617
self.step('Moving %s' % name)
1619
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1620
except errors.NoSuchFile:
1624
def put_format(self, dirname, format):
1625
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1628
class ConvertMetaToMeta(Converter):
1629
"""Converts the components of metadirs."""
1631
def __init__(self, target_format):
1632
"""Create a metadir to metadir converter.
1634
:param target_format: The final metadir format that is desired.
1636
self.target_format = target_format
1638
def convert(self, to_convert, pb):
1639
"""See Converter.convert()."""
1640
self.bzrdir = to_convert
1644
self.step('checking repository format')
1646
repo = self.bzrdir.open_repository()
1647
except errors.NoRepositoryPresent:
1650
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1651
from bzrlib.repository import CopyConverter
1652
self.pb.note('starting repository conversion')
1653
converter = CopyConverter(self.target_format.repository_format)
1654
converter.convert(repo, pb)