1
# Copyright (C) 2005 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
from copy import deepcopy
25
from cStringIO import StringIO
26
from unittest import TestSuite
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles
31
from bzrlib.osutils import safe_unicode
32
from bzrlib.osutils import (
39
from bzrlib.store.text import TextStore
40
from bzrlib.store.versioned.weave import WeaveStore
41
from bzrlib.symbol_versioning import *
42
from bzrlib.trace import mutter
43
from bzrlib.transactions import PassThroughTransaction
44
from bzrlib.transport import get_transport
45
from bzrlib.transport.local import LocalTransport
46
from bzrlib.weave import Weave
47
from bzrlib.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()
529
self.open_workingtree().clone(result,
530
revision_id=revision_id,
532
except (errors.NoWorkingTree, errors.NotLocalUrl):
533
result.create_workingtree()
537
class BzrDirPreSplitOut(BzrDir):
538
"""A common class for the all-in-one formats."""
540
def __init__(self, _transport, _format):
541
"""See BzrDir.__init__."""
542
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
543
self._control_files = LockableFiles(self.get_branch_transport(None),
546
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
547
"""See BzrDir.clone()."""
548
from bzrlib.workingtree import WorkingTreeFormat2
550
result = self._format.initialize(url, _cloning=True)
551
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
552
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
553
self.open_branch().clone(result, revision_id=revision_id)
555
self.open_workingtree().clone(result, basis=basis_tree)
556
except errors.NotLocalUrl:
557
# make a new one, this format always has to have one.
558
WorkingTreeFormat2().initialize(result)
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:
645
self.open_workingtree().clone(result, basis=basis_tree)
646
except (errors.NotBranchError, errors.NotLocalUrl):
647
# we always want a working tree
648
WorkingTreeFormat2().initialize(result)
652
class BzrDir4(BzrDirPreSplitOut):
653
"""A .bzr version 4 control object.
655
This is a deprecated format and may be removed after sept 2006.
658
def create_repository(self, shared=False):
659
"""See BzrDir.create_repository."""
660
return self._format.repository_format.initialize(self, shared)
662
def needs_format_conversion(self, format=None):
663
"""Format 4 dirs are always in need of conversion."""
666
def open_repository(self):
667
"""See BzrDir.open_repository."""
668
from bzrlib.repository import RepositoryFormat4
669
return RepositoryFormat4().open(self, _found=True)
672
class BzrDir5(BzrDirPreSplitOut):
673
"""A .bzr version 5 control object.
675
This is a deprecated format and may be removed after sept 2006.
678
def open_repository(self):
679
"""See BzrDir.open_repository."""
680
from bzrlib.repository import RepositoryFormat5
681
return RepositoryFormat5().open(self, _found=True)
683
def open_workingtree(self, _unsupported=False):
684
"""See BzrDir.create_workingtree."""
685
from bzrlib.workingtree import WorkingTreeFormat2
686
return WorkingTreeFormat2().open(self, _found=True)
689
class BzrDir6(BzrDirPreSplitOut):
690
"""A .bzr version 6 control object.
692
This is a deprecated format and may be removed after sept 2006.
695
def open_repository(self):
696
"""See BzrDir.open_repository."""
697
from bzrlib.repository import RepositoryFormat6
698
return RepositoryFormat6().open(self, _found=True)
700
def open_workingtree(self, _unsupported=False):
701
"""See BzrDir.create_workingtree."""
702
from bzrlib.workingtree import WorkingTreeFormat2
703
return WorkingTreeFormat2().open(self, _found=True)
706
class BzrDirMeta1(BzrDir):
707
"""A .bzr meta version 1 control object.
709
This is the first control object where the
710
individual formats are really split out.
713
def can_convert_format(self):
714
"""See BzrDir.can_convert_format()."""
717
def create_branch(self):
718
"""See BzrDir.create_branch."""
719
from bzrlib.branch import BranchFormat
720
return BranchFormat.get_default_format().initialize(self)
722
def create_repository(self, shared=False):
723
"""See BzrDir.create_repository."""
724
return self._format.repository_format.initialize(self, shared)
726
def create_workingtree(self, revision_id=None):
727
"""See BzrDir.create_workingtree."""
728
from bzrlib.workingtree import WorkingTreeFormat
729
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
731
def get_branch_transport(self, branch_format):
732
"""See BzrDir.get_branch_transport()."""
733
if branch_format is None:
734
return self.transport.clone('branch')
736
branch_format.get_format_string()
737
except NotImplementedError:
738
raise errors.IncompatibleFormat(branch_format, self._format)
740
self.transport.mkdir('branch')
741
except errors.FileExists:
743
return self.transport.clone('branch')
745
def get_repository_transport(self, repository_format):
746
"""See BzrDir.get_repository_transport()."""
747
if repository_format is None:
748
return self.transport.clone('repository')
750
repository_format.get_format_string()
751
except NotImplementedError:
752
raise errors.IncompatibleFormat(repository_format, self._format)
754
self.transport.mkdir('repository')
755
except errors.FileExists:
757
return self.transport.clone('repository')
759
def get_workingtree_transport(self, workingtree_format):
760
"""See BzrDir.get_workingtree_transport()."""
761
if workingtree_format is None:
762
return self.transport.clone('checkout')
764
workingtree_format.get_format_string()
765
except NotImplementedError:
766
raise errors.IncompatibleFormat(workingtree_format, self._format)
768
self.transport.mkdir('checkout')
769
except errors.FileExists:
771
return self.transport.clone('checkout')
773
def needs_format_conversion(self, format=None):
774
"""See BzrDir.needs_format_conversion()."""
776
format = BzrDirFormat.get_default_format()
777
if not isinstance(self._format, format.__class__):
778
# it is not a meta dir format, conversion is needed.
780
# we might want to push this down to the repository?
782
if not isinstance(self.open_repository()._format,
783
format.repository_format.__class__):
784
# the repository needs an upgrade.
786
except errors.NoRepositoryPresent:
788
# currently there are no other possible conversions for meta1 formats.
791
def open_branch(self, unsupported=False):
792
"""See BzrDir.open_branch."""
793
from bzrlib.branch import BranchFormat
794
format = BranchFormat.find_format(self)
795
self._check_supported(format, unsupported)
796
return format.open(self, _found=True)
798
def open_repository(self, unsupported=False):
799
"""See BzrDir.open_repository."""
800
from bzrlib.repository import RepositoryFormat
801
format = RepositoryFormat.find_format(self)
802
self._check_supported(format, unsupported)
803
return format.open(self, _found=True)
805
def open_workingtree(self, unsupported=False):
806
"""See BzrDir.open_workingtree."""
807
from bzrlib.workingtree import WorkingTreeFormat
808
format = WorkingTreeFormat.find_format(self)
809
self._check_supported(format, unsupported)
810
return format.open(self, _found=True)
813
class BzrDirFormat(object):
814
"""An encapsulation of the initialization and open routines for a format.
816
Formats provide three things:
817
* An initialization routine,
821
Formats are placed in an dict by their format string for reference
822
during bzrdir opening. These should be subclasses of BzrDirFormat
825
Once a format is deprecated, just deprecate the initialize and open
826
methods on the format class. Do not deprecate the object, as the
827
object will be created every system load.
830
_default_format = None
831
"""The default format used for new .bzr dirs."""
834
"""The known formats."""
837
def find_format(klass, transport):
838
"""Return the format registered for URL."""
840
format_string = transport.get(".bzr/branch-format").read()
841
return klass._formats[format_string]
842
except errors.NoSuchFile:
843
raise errors.NotBranchError(path=transport.base)
845
raise errors.UnknownFormatError(format_string)
848
def get_default_format(klass):
849
"""Return the current default format."""
850
return klass._default_format
852
def get_format_string(self):
853
"""Return the ASCII format string that identifies this format."""
854
raise NotImplementedError(self.get_format_string)
856
def get_converter(self, format=None):
857
"""Return the converter to use to convert bzrdirs needing converts.
859
This returns a bzrlib.bzrdir.Converter object.
861
This should return the best upgrader to step this format towards the
862
current default format. In the case of plugins we can/shouold provide
863
some means for them to extend the range of returnable converters.
865
:param format: Optional format to override the default foramt of the
868
raise NotImplementedError(self.get_converter)
870
def initialize(self, url):
871
"""Create a bzr control dir at this url and return an opened copy."""
872
# Since we don't have a .bzr directory, inherit the
873
# mode from the root directory
874
t = get_transport(url)
875
temp_control = LockableFiles(t, '')
876
temp_control._transport.mkdir('.bzr',
877
# FIXME: RBC 20060121 dont peek under
879
mode=temp_control._dir_mode)
880
file_mode = temp_control._file_mode
882
mutter('created control directory in ' + t.base)
883
control = t.clone('.bzr')
884
lock_file = 'branch-lock'
885
utf8_files = [('README',
886
"This is a Bazaar-NG control directory.\n"
887
"Do not change any files in this directory.\n"),
888
('branch-format', self.get_format_string()),
890
# NB: no need to escape relative paths that are url safe.
891
control.put(lock_file, StringIO(), mode=file_mode)
892
control_files = LockableFiles(control, lock_file)
893
control_files.lock_write()
895
for file, content in utf8_files:
896
control_files.put_utf8(file, content)
898
control_files.unlock()
899
return self.open(t, _found=True)
901
def is_supported(self):
902
"""Is this format supported?
904
Supported formats must be initializable and openable.
905
Unsupported formats may not support initialization or committing or
906
some other features depending on the reason for not being supported.
910
def open(self, transport, _found=False):
911
"""Return an instance of this format for the dir transport points at.
913
_found is a private parameter, do not use it.
916
assert isinstance(BzrDirFormat.find_format(transport),
918
return self._open(transport)
920
def _open(self, transport):
921
"""Template method helper for opening BzrDirectories.
923
This performs the actual open and any additional logic or parameter
926
raise NotImplementedError(self._open)
929
def register_format(klass, format):
930
klass._formats[format.get_format_string()] = format
933
def set_default_format(klass, format):
934
klass._default_format = format
937
return self.get_format_string()[:-1]
940
def unregister_format(klass, format):
941
assert klass._formats[format.get_format_string()] is format
942
del klass._formats[format.get_format_string()]
945
class BzrDirFormat4(BzrDirFormat):
948
This format is a combined format for working tree, branch and repository.
950
- Format 1 working trees [always]
951
- Format 4 branches [always]
952
- Format 4 repositories [always]
954
This format is deprecated: it indexes texts using a text it which is
955
removed in format 5; write support for this format has been removed.
958
def get_format_string(self):
959
"""See BzrDirFormat.get_format_string()."""
960
return "Bazaar-NG branch, format 0.0.4\n"
962
def get_converter(self, format=None):
963
"""See BzrDirFormat.get_converter()."""
964
# there is one and only one upgrade path here.
965
return ConvertBzrDir4To5()
967
def initialize(self, url):
968
"""Format 4 branches cannot be created."""
969
raise errors.UninitializableFormat(self)
971
def is_supported(self):
972
"""Format 4 is not supported.
974
It is not supported because the model changed from 4 to 5 and the
975
conversion logic is expensive - so doing it on the fly was not
980
def _open(self, transport):
981
"""See BzrDirFormat._open."""
982
return BzrDir4(transport, self)
984
def __return_repository_format(self):
985
"""Circular import protection."""
986
from bzrlib.repository import RepositoryFormat4
987
return RepositoryFormat4(self)
988
repository_format = property(__return_repository_format)
991
class BzrDirFormat5(BzrDirFormat):
992
"""Bzr control format 5.
994
This format is a combined format for working tree, branch and repository.
996
- Format 2 working trees [always]
997
- Format 4 branches [always]
998
- Format 5 repositories [always]
999
Unhashed stores in the repository.
1002
def get_format_string(self):
1003
"""See BzrDirFormat.get_format_string()."""
1004
return "Bazaar-NG branch, format 5\n"
1006
def get_converter(self, format=None):
1007
"""See BzrDirFormat.get_converter()."""
1008
# there is one and only one upgrade path here.
1009
return ConvertBzrDir5To6()
1011
def initialize(self, url, _cloning=False):
1012
"""Format 5 dirs always have working tree, branch and repository.
1014
Except when they are being cloned.
1016
from bzrlib.branch import BzrBranchFormat4
1017
from bzrlib.repository import RepositoryFormat5
1018
from bzrlib.workingtree import WorkingTreeFormat2
1019
result = super(BzrDirFormat5, self).initialize(url)
1020
RepositoryFormat5().initialize(result, _internal=True)
1022
BzrBranchFormat4().initialize(result)
1023
WorkingTreeFormat2().initialize(result)
1026
def _open(self, transport):
1027
"""See BzrDirFormat._open."""
1028
return BzrDir5(transport, self)
1030
def __return_repository_format(self):
1031
"""Circular import protection."""
1032
from bzrlib.repository import RepositoryFormat5
1033
return RepositoryFormat5(self)
1034
repository_format = property(__return_repository_format)
1037
class BzrDirFormat6(BzrDirFormat):
1038
"""Bzr control format 6.
1040
This format is a combined format for working tree, branch and repository.
1042
- Format 2 working trees [always]
1043
- Format 4 branches [always]
1044
- Format 6 repositories [always]
1047
def get_format_string(self):
1048
"""See BzrDirFormat.get_format_string()."""
1049
return "Bazaar-NG branch, format 6\n"
1051
def get_converter(self, format=None):
1052
"""See BzrDirFormat.get_converter()."""
1053
# there is one and only one upgrade path here.
1054
return ConvertBzrDir6ToMeta()
1056
def initialize(self, url, _cloning=False):
1057
"""Format 6 dirs always have working tree, branch and repository.
1059
Except when they are being cloned.
1061
from bzrlib.branch import BzrBranchFormat4
1062
from bzrlib.repository import RepositoryFormat6
1063
from bzrlib.workingtree import WorkingTreeFormat2
1064
result = super(BzrDirFormat6, self).initialize(url)
1065
RepositoryFormat6().initialize(result, _internal=True)
1067
BzrBranchFormat4().initialize(result)
1069
WorkingTreeFormat2().initialize(result)
1070
except errors.NotLocalUrl:
1071
# emulate pre-check behaviour for working tree and silently
1076
def _open(self, transport):
1077
"""See BzrDirFormat._open."""
1078
return BzrDir6(transport, self)
1080
def __return_repository_format(self):
1081
"""Circular import protection."""
1082
from bzrlib.repository import RepositoryFormat6
1083
return RepositoryFormat6(self)
1084
repository_format = property(__return_repository_format)
1087
class BzrDirMetaFormat1(BzrDirFormat):
1088
"""Bzr meta control format 1
1090
This is the first format with split out working tree, branch and repository
1093
- Format 3 working trees [optional]
1094
- Format 5 branches [optional]
1095
- Format 7 repositories [optional]
1098
def get_converter(self, format=None):
1099
"""See BzrDirFormat.get_converter()."""
1101
format = BzrDirFormat.get_default_format()
1102
if not isinstance(self, format.__class__):
1103
# converting away from metadir is not implemented
1104
raise NotImplementedError(self.get_converter)
1105
return ConvertMetaToMeta(format)
1107
def get_format_string(self):
1108
"""See BzrDirFormat.get_format_string()."""
1109
return "Bazaar-NG meta directory, format 1\n"
1111
def _open(self, transport):
1112
"""See BzrDirFormat._open."""
1113
return BzrDirMeta1(transport, self)
1115
def __return_repository_format(self):
1116
"""Circular import protection."""
1117
if getattr(self, '_repository_format', None):
1118
return self._repository_format
1119
from bzrlib.repository import RepositoryFormat
1120
return RepositoryFormat.get_default_format()
1122
def __set_repository_format(self, value):
1123
"""Allow changint the repository format for metadir formats."""
1124
self._repository_format = value
1125
repository_format = property(__return_repository_format, __set_repository_format)
1128
BzrDirFormat.register_format(BzrDirFormat4())
1129
BzrDirFormat.register_format(BzrDirFormat5())
1130
BzrDirFormat.register_format(BzrDirMetaFormat1())
1131
__default_format = BzrDirFormat6()
1132
BzrDirFormat.register_format(__default_format)
1133
BzrDirFormat.set_default_format(__default_format)
1136
class BzrDirTestProviderAdapter(object):
1137
"""A tool to generate a suite testing multiple bzrdir formats at once.
1139
This is done by copying the test once for each transport and injecting
1140
the transport_server, transport_readonly_server, and bzrdir_format
1141
classes into each copy. Each copy is also given a new id() to make it
1145
def __init__(self, transport_server, transport_readonly_server, formats):
1146
self._transport_server = transport_server
1147
self._transport_readonly_server = transport_readonly_server
1148
self._formats = formats
1150
def adapt(self, test):
1151
result = TestSuite()
1152
for format in self._formats:
1153
new_test = deepcopy(test)
1154
new_test.transport_server = self._transport_server
1155
new_test.transport_readonly_server = self._transport_readonly_server
1156
new_test.bzrdir_format = format
1157
def make_new_test_id():
1158
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1159
return lambda: new_id
1160
new_test.id = make_new_test_id()
1161
result.addTest(new_test)
1165
class ScratchDir(BzrDir6):
1166
"""Special test class: a bzrdir that cleans up itself..
1168
>>> d = ScratchDir()
1169
>>> base = d.transport.base
1172
>>> b.transport.__del__()
1177
def __init__(self, files=[], dirs=[], transport=None):
1178
"""Make a test branch.
1180
This creates a temporary directory and runs init-tree in it.
1182
If any files are listed, they are created in the working copy.
1184
if transport is None:
1185
transport = bzrlib.transport.local.ScratchTransport()
1186
# local import for scope restriction
1187
BzrDirFormat6().initialize(transport.base)
1188
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1189
self.create_repository()
1190
self.create_branch()
1191
self.create_workingtree()
1193
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1195
# BzrBranch creates a clone to .bzr and then forgets about the
1196
# original transport. A ScratchTransport() deletes itself and
1197
# everything underneath it when it goes away, so we need to
1198
# grab a local copy to prevent that from happening
1199
self._transport = transport
1202
self._transport.mkdir(d)
1205
self._transport.put(f, 'content of %s' % f)
1209
>>> orig = ScratchDir(files=["file1", "file2"])
1210
>>> os.listdir(orig.base)
1211
[u'.bzr', u'file1', u'file2']
1212
>>> clone = orig.clone()
1213
>>> if os.name != 'nt':
1214
... os.path.samefile(orig.base, clone.base)
1216
... orig.base == clone.base
1219
>>> os.listdir(clone.base)
1220
[u'.bzr', u'file1', u'file2']
1222
from shutil import copytree
1223
from bzrlib.osutils import mkdtemp
1226
copytree(self.base, base, symlinks=True)
1228
transport=bzrlib.transport.local.ScratchTransport(base))
1231
class Converter(object):
1232
"""Converts a disk format object from one format to another."""
1234
def convert(self, to_convert, pb):
1235
"""Perform the conversion of to_convert, giving feedback via pb.
1237
:param to_convert: The disk object to convert.
1238
:param pb: a progress bar to use for progress information.
1241
def step(self, message):
1242
"""Update the pb by a step."""
1244
self.pb.update(message, self.count, self.total)
1247
class ConvertBzrDir4To5(Converter):
1248
"""Converts format 4 bzr dirs to format 5."""
1251
super(ConvertBzrDir4To5, self).__init__()
1252
self.converted_revs = set()
1253
self.absent_revisions = set()
1257
def convert(self, to_convert, pb):
1258
"""See Converter.convert()."""
1259
self.bzrdir = to_convert
1261
self.pb.note('starting upgrade from format 4 to 5')
1262
if isinstance(self.bzrdir.transport, LocalTransport):
1263
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1264
self._convert_to_weaves()
1265
return BzrDir.open(self.bzrdir.root_transport.base)
1267
def _convert_to_weaves(self):
1268
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1271
stat = self.bzrdir.transport.stat('weaves')
1272
if not S_ISDIR(stat.st_mode):
1273
self.bzrdir.transport.delete('weaves')
1274
self.bzrdir.transport.mkdir('weaves')
1275
except errors.NoSuchFile:
1276
self.bzrdir.transport.mkdir('weaves')
1277
self.inv_weave = Weave('inventory')
1278
# holds in-memory weaves for all files
1279
self.text_weaves = {}
1280
self.bzrdir.transport.delete('branch-format')
1281
self.branch = self.bzrdir.open_branch()
1282
self._convert_working_inv()
1283
rev_history = self.branch.revision_history()
1284
# to_read is a stack holding the revisions we still need to process;
1285
# appending to it adds new highest-priority revisions
1286
self.known_revisions = set(rev_history)
1287
self.to_read = rev_history[-1:]
1289
rev_id = self.to_read.pop()
1290
if (rev_id not in self.revisions
1291
and rev_id not in self.absent_revisions):
1292
self._load_one_rev(rev_id)
1294
to_import = self._make_order()
1295
for i, rev_id in enumerate(to_import):
1296
self.pb.update('converting revision', i, len(to_import))
1297
self._convert_one_rev(rev_id)
1299
self._write_all_weaves()
1300
self._write_all_revs()
1301
self.pb.note('upgraded to weaves:')
1302
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1303
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1304
self.pb.note(' %6d texts', self.text_count)
1305
self._cleanup_spare_files_after_format4()
1306
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1308
def _cleanup_spare_files_after_format4(self):
1309
# FIXME working tree upgrade foo.
1310
for n in 'merged-patches', 'pending-merged-patches':
1312
## assert os.path.getsize(p) == 0
1313
self.bzrdir.transport.delete(n)
1314
except errors.NoSuchFile:
1316
self.bzrdir.transport.delete_tree('inventory-store')
1317
self.bzrdir.transport.delete_tree('text-store')
1319
def _convert_working_inv(self):
1320
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1321
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1322
# FIXME inventory is a working tree change.
1323
self.branch.control_files.put('inventory', new_inv_xml)
1325
def _write_all_weaves(self):
1326
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1327
weave_transport = self.bzrdir.transport.clone('weaves')
1328
weaves = WeaveStore(weave_transport, prefixed=False)
1329
transaction = PassThroughTransaction()
1331
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1334
for file_id, file_weave in self.text_weaves.items():
1335
self.pb.update('writing weave', i, len(self.text_weaves))
1336
weaves.put_weave(file_id, file_weave, transaction)
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 = TextStore(revision_transport,
1351
for i, rev_id in enumerate(self.converted_revs):
1352
self.pb.update('write revision', i, len(self.converted_revs))
1353
rev_tmp = StringIO()
1354
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1356
revision_store.add(rev_tmp, rev_id)
1360
def _load_one_rev(self, rev_id):
1361
"""Load a revision object into memory.
1363
Any parents not either loaded or abandoned get queued to be
1365
self.pb.update('loading revision',
1366
len(self.revisions),
1367
len(self.known_revisions))
1368
if not self.branch.repository.revision_store.has_id(rev_id):
1370
self.pb.note('revision {%s} not present in branch; '
1371
'will be converted as a ghost',
1373
self.absent_revisions.add(rev_id)
1375
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1376
rev = serializer_v4.read_revision_from_string(rev_xml)
1377
for parent_id in rev.parent_ids:
1378
self.known_revisions.add(parent_id)
1379
self.to_read.append(parent_id)
1380
self.revisions[rev_id] = rev
1382
def _load_old_inventory(self, rev_id):
1383
assert rev_id not in self.converted_revs
1384
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1385
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1386
rev = self.revisions[rev_id]
1387
if rev.inventory_sha1:
1388
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1389
'inventory sha mismatch for {%s}' % rev_id
1392
def _load_updated_inventory(self, rev_id):
1393
assert rev_id in self.converted_revs
1394
inv_xml = self.inv_weave.get_text(rev_id)
1395
inv = serializer_v5.read_inventory_from_string(inv_xml)
1398
def _convert_one_rev(self, rev_id):
1399
"""Convert revision and all referenced objects to new format."""
1400
rev = self.revisions[rev_id]
1401
inv = self._load_old_inventory(rev_id)
1402
present_parents = [p for p in rev.parent_ids
1403
if p not in self.absent_revisions]
1404
self._convert_revision_contents(rev, inv, present_parents)
1405
self._store_new_weave(rev, inv, present_parents)
1406
self.converted_revs.add(rev_id)
1408
def _store_new_weave(self, rev, inv, present_parents):
1409
# the XML is now updated with text versions
1413
if ie.kind == 'root_directory':
1415
assert hasattr(ie, 'revision'), \
1416
'no revision on {%s} in {%s}' % \
1417
(file_id, rev.revision_id)
1418
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1419
new_inv_sha1 = sha_string(new_inv_xml)
1420
self.inv_weave.add(rev.revision_id,
1422
new_inv_xml.splitlines(True),
1424
rev.inventory_sha1 = new_inv_sha1
1426
def _convert_revision_contents(self, rev, inv, present_parents):
1427
"""Convert all the files within a revision.
1429
Also upgrade the inventory to refer to the text revision ids."""
1430
rev_id = rev.revision_id
1431
mutter('converting texts of revision {%s}',
1433
parent_invs = map(self._load_updated_inventory, present_parents)
1436
self._convert_file_version(rev, ie, parent_invs)
1438
def _convert_file_version(self, rev, ie, parent_invs):
1439
"""Convert one version of one file.
1441
The file needs to be added into the weave if it is a merge
1442
of >=2 parents or if it's changed from its parent.
1444
if ie.kind == 'root_directory':
1446
file_id = ie.file_id
1447
rev_id = rev.revision_id
1448
w = self.text_weaves.get(file_id)
1451
self.text_weaves[file_id] = w
1452
text_changed = False
1453
previous_entries = ie.find_previous_heads(parent_invs, w)
1454
for old_revision in previous_entries:
1455
# if this fails, its a ghost ?
1456
assert old_revision in self.converted_revs
1457
self.snapshot_ie(previous_entries, ie, w, rev_id)
1459
assert getattr(ie, 'revision', None) is not None
1461
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1462
# TODO: convert this logic, which is ~= snapshot to
1463
# a call to:. This needs the path figured out. rather than a work_tree
1464
# a v4 revision_tree can be given, or something that looks enough like
1465
# one to give the file content to the entry if it needs it.
1466
# and we need something that looks like a weave store for snapshot to
1468
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1469
if len(previous_revisions) == 1:
1470
previous_ie = previous_revisions.values()[0]
1471
if ie._unchanged(previous_ie):
1472
ie.revision = previous_ie.revision
1474
parent_indexes = map(w.lookup, previous_revisions)
1476
text = self.branch.repository.text_store.get(ie.text_id)
1477
file_lines = text.readlines()
1478
assert sha_strings(file_lines) == ie.text_sha1
1479
assert sum(map(len, file_lines)) == ie.text_size
1480
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1481
self.text_count += 1
1483
w.add(rev_id, parent_indexes, [], None)
1484
ie.revision = rev_id
1486
def _make_order(self):
1487
"""Return a suitable order for importing revisions.
1489
The order must be such that an revision is imported after all
1490
its (present) parents.
1492
todo = set(self.revisions.keys())
1493
done = self.absent_revisions.copy()
1496
# scan through looking for a revision whose parents
1498
for rev_id in sorted(list(todo)):
1499
rev = self.revisions[rev_id]
1500
parent_ids = set(rev.parent_ids)
1501
if parent_ids.issubset(done):
1502
# can take this one now
1503
order.append(rev_id)
1509
class ConvertBzrDir5To6(Converter):
1510
"""Converts format 5 bzr dirs to format 6."""
1512
def convert(self, to_convert, pb):
1513
"""See Converter.convert()."""
1514
self.bzrdir = to_convert
1516
self.pb.note('starting upgrade from format 5 to 6')
1517
self._convert_to_prefixed()
1518
return BzrDir.open(self.bzrdir.root_transport.base)
1520
def _convert_to_prefixed(self):
1521
from bzrlib.store import hash_prefix
1522
self.bzrdir.transport.delete('branch-format')
1523
for store_name in ["weaves", "revision-store"]:
1524
self.pb.note("adding prefixes to %s" % store_name)
1525
store_transport = self.bzrdir.transport.clone(store_name)
1526
for filename in store_transport.list_dir('.'):
1527
if (filename.endswith(".weave") or
1528
filename.endswith(".gz") or
1529
filename.endswith(".sig")):
1530
file_id = os.path.splitext(filename)[0]
1533
prefix_dir = hash_prefix(file_id)
1534
# FIXME keep track of the dirs made RBC 20060121
1536
store_transport.move(filename, prefix_dir + '/' + filename)
1537
except errors.NoSuchFile: # catches missing dirs strangely enough
1538
store_transport.mkdir(prefix_dir)
1539
store_transport.move(filename, prefix_dir + '/' + filename)
1540
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1543
class ConvertBzrDir6ToMeta(Converter):
1544
"""Converts format 6 bzr dirs to metadirs."""
1546
def convert(self, to_convert, pb):
1547
"""See Converter.convert()."""
1548
self.bzrdir = to_convert
1551
self.total = 20 # the steps we know about
1552
self.garbage_inventories = []
1554
self.pb.note('starting upgrade from format 6 to metadir')
1555
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1556
# its faster to move specific files around than to open and use the apis...
1557
# first off, nuke ancestry.weave, it was never used.
1559
self.step('Removing ancestry.weave')
1560
self.bzrdir.transport.delete('ancestry.weave')
1561
except errors.NoSuchFile:
1563
# find out whats there
1564
self.step('Finding branch files')
1565
last_revision = self.bzrdir.open_workingtree().last_revision()
1566
bzrcontents = self.bzrdir.transport.list_dir('.')
1567
for name in bzrcontents:
1568
if name.startswith('basis-inventory.'):
1569
self.garbage_inventories.append(name)
1570
# create new directories for repository, working tree and branch
1571
dir_mode = self.bzrdir._control_files._dir_mode
1572
self.file_mode = self.bzrdir._control_files._file_mode
1573
repository_names = [('inventory.weave', True),
1574
('revision-store', True),
1576
self.step('Upgrading repository ')
1577
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1578
self.make_lock('repository')
1579
# we hard code the formats here because we are converting into
1580
# the meta format. The meta format upgrader can take this to a
1581
# future format within each component.
1582
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1583
for entry in repository_names:
1584
self.move_entry('repository', entry)
1586
self.step('Upgrading branch ')
1587
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1588
self.make_lock('branch')
1589
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1590
branch_files = [('revision-history', True),
1591
('branch-name', True),
1593
for entry in branch_files:
1594
self.move_entry('branch', entry)
1596
self.step('Upgrading working tree')
1597
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1598
self.make_lock('checkout')
1599
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1600
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1601
checkout_files = [('pending-merges', True),
1602
('inventory', True),
1603
('stat-cache', False)]
1604
for entry in checkout_files:
1605
self.move_entry('checkout', entry)
1606
if last_revision is not None:
1607
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1609
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1610
return BzrDir.open(self.bzrdir.root_transport.base)
1612
def make_lock(self, name):
1613
"""Make a lock for the new control dir name."""
1614
self.step('Make %s lock' % name)
1615
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1617
def move_entry(self, new_dir, entry):
1618
"""Move then entry name into new_dir."""
1620
mandatory = entry[1]
1621
self.step('Moving %s' % name)
1623
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1624
except errors.NoSuchFile:
1628
def put_format(self, dirname, format):
1629
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1632
class ConvertMetaToMeta(Converter):
1633
"""Converts the components of metadirs."""
1635
def __init__(self, target_format):
1636
"""Create a metadir to metadir converter.
1638
:param target_format: The final metadir format that is desired.
1640
self.target_format = target_format
1642
def convert(self, to_convert, pb):
1643
"""See Converter.convert()."""
1644
self.bzrdir = to_convert
1648
self.step('checking repository format')
1650
repo = self.bzrdir.open_repository()
1651
except errors.NoRepositoryPresent:
1654
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1655
from bzrlib.repository import CopyConverter
1656
self.pb.note('starting repository conversion')
1657
converter = CopyConverter(self.target_format.repository_format)
1658
converter.convert(repo, pb)