1
# Copyright (C) 2005 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
from copy import deepcopy
25
from cStringIO import StringIO
26
from unittest import TestSuite
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles
31
from bzrlib.osutils import safe_unicode
32
from bzrlib.osutils import (
39
from bzrlib.store.text import TextStore
40
from bzrlib.store.weave import WeaveStore
41
from bzrlib.symbol_versioning import *
42
from bzrlib.trace import mutter
43
from bzrlib.transactions import PassThroughTransaction
44
from bzrlib.transport import get_transport
45
from bzrlib.transport.local import LocalTransport
46
from bzrlib.weave import Weave
47
from bzrlib.weavefile import read_weave, write_weave
48
from bzrlib.xml4 import serializer_v4
49
from bzrlib.xml5 import serializer_v5
53
"""A .bzr control diretory.
55
BzrDir instances let you create or open any of the things that can be
56
found within .bzr - checkouts, branches and repositories.
59
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
61
a transport connected to the directory this bzr was opened from.
64
def can_convert_format(self):
65
"""Return true if this bzrdir is one whose format we can convert from."""
68
def _check_supported(self, format, allow_unsupported):
69
"""Check whether format is a supported format.
71
If allow_unsupported is True, this is a no-op.
73
if not allow_unsupported and not format.is_supported():
74
raise errors.UnsupportedFormatError(format)
76
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
77
"""Clone this bzrdir and its contents to url verbatim.
79
If urls last component does not exist, it will be created.
81
if revision_id is not None, then the clone operation may tune
82
itself to download less data.
83
:param force_new_repo: Do not use a shared repository for the target
84
even if one is available.
87
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
88
result = self._format.initialize(url)
90
local_repo = self.find_repository()
91
except errors.NoRepositoryPresent:
94
# may need to copy content in
96
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
99
result_repo = result.find_repository()
100
# fetch content this dir needs.
102
# XXX FIXME RBC 20060214 need tests for this when the basis
104
result_repo.fetch(basis_repo, revision_id=revision_id)
105
result_repo.fetch(local_repo, revision_id=revision_id)
106
except errors.NoRepositoryPresent:
107
# needed to make one anyway.
108
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
109
# 1 if there is a branch present
110
# make sure its content is available in the target repository
113
self.open_branch().clone(result, revision_id=revision_id)
114
except errors.NotBranchError:
117
self.open_workingtree().clone(result, basis=basis_tree)
118
except (errors.NoWorkingTree, errors.NotLocalUrl):
122
def _get_basis_components(self, basis):
123
"""Retrieve the basis components that are available at basis."""
125
return None, None, None
127
basis_tree = basis.open_workingtree()
128
basis_branch = basis_tree.branch
129
basis_repo = basis_branch.repository
130
except (errors.NoWorkingTree, errors.NotLocalUrl):
133
basis_branch = basis.open_branch()
134
basis_repo = basis_branch.repository
135
except errors.NotBranchError:
138
basis_repo = basis.open_repository()
139
except errors.NoRepositoryPresent:
141
return basis_repo, basis_branch, basis_tree
143
def _make_tail(self, url):
144
segments = url.split('/')
145
if segments and segments[-1] not in ('', '.'):
146
parent = '/'.join(segments[:-1])
147
t = bzrlib.transport.get_transport(parent)
149
t.mkdir(segments[-1])
150
except errors.FileExists:
155
"""Create a new BzrDir at the url 'base'.
157
This will call the current default formats initialize with base
158
as the only parameter.
160
If you need a specific format, consider creating an instance
161
of that and calling initialize().
163
segments = base.split('/')
164
if segments and segments[-1] not in ('', '.'):
165
parent = '/'.join(segments[:-1])
166
t = bzrlib.transport.get_transport(parent)
168
t.mkdir(segments[-1])
169
except errors.FileExists:
171
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
173
def create_branch(self):
174
"""Create a branch in this BzrDir.
176
The bzrdirs format will control what branch format is created.
177
For more control see BranchFormatXX.create(a_bzrdir).
179
raise NotImplementedError(self.create_branch)
182
def create_branch_and_repo(base, force_new_repo=False):
183
"""Create a new BzrDir, Branch and Repository at the url 'base'.
185
This will use the current default BzrDirFormat, and use whatever
186
repository format that that uses via bzrdir.create_branch and
187
create_repository. If a shared repository is available that is used
190
The created Branch object is returned.
192
:param base: The URL to create the branch at.
193
:param force_new_repo: If True a new repository is always created.
195
bzrdir = BzrDir.create(base)
196
bzrdir._find_or_create_repository(force_new_repo)
197
return bzrdir.create_branch()
199
def _find_or_create_repository(self, force_new_repo):
200
"""Create a new repository if needed, returning the repository."""
202
return self.create_repository()
204
return self.find_repository()
205
except errors.NoRepositoryPresent:
206
return self.create_repository()
209
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
210
"""Create a new BzrDir, Branch and Repository at the url 'base'.
212
This is a convenience function - it will use an existing repository
213
if possible, can be told explicitly whether to create a working tree or
216
This will use the current default BzrDirFormat, and use whatever
217
repository format that that uses via bzrdir.create_branch and
218
create_repository. If a shared repository is available that is used
219
preferentially. Whatever repository is used, its tree creation policy
222
The created Branch object is returned.
223
If a working tree cannot be made due to base not being a file:// url,
226
:param base: The URL to create the branch at.
227
:param force_new_repo: If True a new repository is always created.
228
:param force_new_tree: If True or False force creation of a tree or
229
prevent such creation respectively.
231
bzrdir = BzrDir.create(base)
232
repo = bzrdir._find_or_create_repository(force_new_repo)
233
result = bzrdir.create_branch()
234
if force_new_tree or (repo.make_working_trees() and
235
force_new_tree is None):
236
bzrdir.create_workingtree()
240
def create_repository(base, shared=False):
241
"""Create a new BzrDir and Repository at the url 'base'.
243
This will use the current default BzrDirFormat, and use whatever
244
repository format that that uses for bzrdirformat.create_repository.
246
;param shared: Create a shared repository rather than a standalone
248
The Repository object is returned.
250
This must be overridden as an instance method in child classes, where
251
it should take no parameters and construct whatever repository format
252
that child class desires.
254
bzrdir = BzrDir.create(base)
255
return bzrdir.create_repository()
258
def create_standalone_workingtree(base):
259
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
261
'base' must be a local path or a file:// url.
263
This will use the current default BzrDirFormat, and use whatever
264
repository format that that uses for bzrdirformat.create_workingtree,
265
create_branch and create_repository.
267
The WorkingTree object is returned.
269
t = get_transport(safe_unicode(base))
270
if not isinstance(t, LocalTransport):
271
raise errors.NotLocalUrl(base)
272
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
273
force_new_repo=True).bzrdir
274
return bzrdir.create_workingtree()
276
def create_workingtree(self, revision_id=None):
277
"""Create a working tree at this BzrDir.
279
revision_id: create it as of this revision id.
281
raise NotImplementedError(self.create_workingtree)
283
def find_repository(self):
284
"""Find the repository that should be used for a_bzrdir.
286
This does not require a branch as we use it to find the repo for
287
new branches as well as to hook existing branches up to their
291
return self.open_repository()
292
except errors.NoRepositoryPresent:
294
next_transport = self.root_transport.clone('..')
297
found_bzrdir = BzrDir.open_containing_from_transport(
299
except errors.NotBranchError:
300
raise errors.NoRepositoryPresent(self)
302
repository = found_bzrdir.open_repository()
303
except errors.NoRepositoryPresent:
304
next_transport = found_bzrdir.root_transport.clone('..')
306
if ((found_bzrdir.root_transport.base ==
307
self.root_transport.base) or repository.is_shared()):
310
raise errors.NoRepositoryPresent(self)
311
raise errors.NoRepositoryPresent(self)
313
def get_branch_transport(self, branch_format):
314
"""Get the transport for use by branch format in this BzrDir.
316
Note that bzr dirs that do not support format strings will raise
317
IncompatibleFormat if the branch format they are given has
318
a format string, and vice verca.
320
If branch_format is None, the transport is returned with no
321
checking. if it is not None, then the returned transport is
322
guaranteed to point to an existing directory ready for use.
324
raise NotImplementedError(self.get_branch_transport)
326
def get_repository_transport(self, repository_format):
327
"""Get the transport for use by repository format in this BzrDir.
329
Note that bzr dirs that do not support format strings will raise
330
IncompatibleFormat if the repository format they are given has
331
a format string, and vice verca.
333
If repository_format is None, the transport is returned with no
334
checking. if it is not None, then the returned transport is
335
guaranteed to point to an existing directory ready for use.
337
raise NotImplementedError(self.get_repository_transport)
339
def get_workingtree_transport(self, tree_format):
340
"""Get the transport for use by workingtree format in this BzrDir.
342
Note that bzr dirs that do not support format strings will raise
343
IncompatibleFormat if the workingtree format they are given has
344
a format string, and vice verca.
346
If workingtree_format is None, the transport is returned with no
347
checking. if it is not None, then the returned transport is
348
guaranteed to point to an existing directory ready for use.
350
raise NotImplementedError(self.get_workingtree_transport)
352
def __init__(self, _transport, _format):
353
"""Initialize a Bzr control dir object.
355
Only really common logic should reside here, concrete classes should be
356
made with varying behaviours.
358
:param _format: the format that is creating this BzrDir instance.
359
:param _transport: the transport this dir is based at.
361
self._format = _format
362
self.transport = _transport.clone('.bzr')
363
self.root_transport = _transport
365
def needs_format_conversion(self, format=None):
366
"""Return true if this bzrdir needs convert_format run on it.
368
For instance, if the repository format is out of date but the
369
branch and working tree are not, this should return True.
371
:param format: Optional parameter indicating a specific desired
372
format we plan to arrive at.
374
raise NotImplementedError(self.needs_format_conversion)
377
def open_unsupported(base):
378
"""Open a branch which is not supported."""
379
return BzrDir.open(base, _unsupported=True)
382
def open(base, _unsupported=False):
383
"""Open an existing bzrdir, rooted at 'base' (url)
385
_unsupported is a private parameter to the BzrDir class.
387
t = get_transport(base)
388
mutter("trying to open %r with transport %r", base, t)
389
format = BzrDirFormat.find_format(t)
390
if not _unsupported and not format.is_supported():
391
# see open_downlevel to open legacy branches.
392
raise errors.UnsupportedFormatError(
393
'sorry, format %s not supported' % format,
394
['use a different bzr version',
395
'or remove the .bzr directory'
396
' and "bzr init" again'])
397
return format.open(t, _found=True)
399
def open_branch(self, unsupported=False):
400
"""Open the branch object at this BzrDir if one is present.
402
If unsupported is True, then no longer supported branch formats can
405
TODO: static convenience version of this?
407
raise NotImplementedError(self.open_branch)
410
def open_containing(url):
411
"""Open an existing branch which contains url.
413
:param url: url to search from.
414
See open_containing_from_transport for more detail.
416
return BzrDir.open_containing_from_transport(get_transport(url))
419
def open_containing_from_transport(a_transport):
420
"""Open an existing branch which contains a_transport.base
422
This probes for a branch at a_transport, and searches upwards from there.
424
Basically we keep looking up until we find the control directory or
425
run into the root. If there isn't one, raises NotBranchError.
426
If there is one and it is either an unrecognised format or an unsupported
427
format, UnknownFormatError or UnsupportedFormatError are raised.
428
If there is one, it is returned, along with the unused portion of url.
430
# this gets the normalised url back. I.e. '.' -> the full path.
431
url = a_transport.base
434
format = BzrDirFormat.find_format(a_transport)
435
return format.open(a_transport), a_transport.relpath(url)
436
except errors.NotBranchError, e:
437
mutter('not a branch in: %r %s', a_transport.base, e)
438
new_t = a_transport.clone('..')
439
if new_t.base == a_transport.base:
440
# reached the root, whatever that may be
441
raise errors.NotBranchError(path=url)
444
def open_repository(self, _unsupported=False):
445
"""Open the repository object at this BzrDir if one is present.
447
This will not follow the Branch object pointer - its strictly a direct
448
open facility. Most client code should use open_branch().repository to
451
_unsupported is a private parameter, not part of the api.
452
TODO: static convenience version of this?
454
raise NotImplementedError(self.open_repository)
456
def open_workingtree(self, _unsupported=False):
457
"""Open the workingtree object at this BzrDir if one is present.
459
TODO: static convenience version of this?
461
raise NotImplementedError(self.open_workingtree)
463
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
464
"""Create a copy of this bzrdir prepared for use as a new line of
467
If urls last component does not exist, it will be created.
469
Attributes related to the identity of the source branch like
470
branch nickname will be cleaned, a working tree is created
471
whether one existed before or not; and a local branch is always
474
if revision_id is not None, then the clone operation may tune
475
itself to download less data.
478
result = self._format.initialize(url)
479
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
481
source_branch = self.open_branch()
482
source_repository = source_branch.repository
483
except errors.NotBranchError:
486
source_repository = self.open_repository()
487
except errors.NoRepositoryPresent:
488
# copy the entire basis one if there is one
489
# but there is no repository.
490
source_repository = basis_repo
495
result_repo = result.find_repository()
496
except errors.NoRepositoryPresent:
498
if source_repository is None and result_repo is not None:
500
elif source_repository is None and result_repo is None:
501
# no repo available, make a new one
502
result.create_repository()
503
elif source_repository is not None and result_repo is None:
504
# have soure, and want to make a new target repo
505
source_repository.clone(result,
506
revision_id=revision_id,
509
# fetch needed content into target.
511
# XXX FIXME RBC 20060214 need tests for this when the basis
513
result_repo.fetch(basis_repo, revision_id=revision_id)
514
result_repo.fetch(source_repository, revision_id=revision_id)
515
if source_branch is not None:
516
source_branch.sprout(result, revision_id=revision_id)
518
result.create_branch()
520
self.open_workingtree().clone(result,
521
revision_id=revision_id,
523
except (errors.NoWorkingTree, errors.NotLocalUrl):
524
result.create_workingtree()
528
class BzrDirPreSplitOut(BzrDir):
529
"""A common class for the all-in-one formats."""
531
def __init__(self, _transport, _format):
532
"""See BzrDir.__init__."""
533
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
534
self._control_files = LockableFiles(self.get_branch_transport(None),
537
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
538
"""See BzrDir.clone()."""
539
from bzrlib.workingtree import WorkingTreeFormat2
541
result = self._format.initialize(url, _cloning=True)
542
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
543
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
544
self.open_branch().clone(result, revision_id=revision_id)
546
self.open_workingtree().clone(result, basis=basis_tree)
547
except errors.NotLocalUrl:
548
# make a new one, this format always has to have one.
549
WorkingTreeFormat2().initialize(result)
552
def create_branch(self):
553
"""See BzrDir.create_branch."""
554
return self.open_branch()
556
def create_repository(self, shared=False):
557
"""See BzrDir.create_repository."""
559
raise errors.IncompatibleFormat('shared repository', self._format)
560
return self.open_repository()
562
def create_workingtree(self, revision_id=None):
563
"""See BzrDir.create_workingtree."""
564
# this looks buggy but is not -really-
565
# clone and sprout will have set the revision_id
566
# and that will have set it for us, its only
567
# specific uses of create_workingtree in isolation
568
# that can do wonky stuff here, and that only
569
# happens for creating checkouts, which cannot be
570
# done on this format anyway. So - acceptable wart.
571
result = self.open_workingtree()
572
if revision_id is not None:
573
result.set_last_revision(revision_id)
576
def get_branch_transport(self, branch_format):
577
"""See BzrDir.get_branch_transport()."""
578
if branch_format is None:
579
return self.transport
581
branch_format.get_format_string()
582
except NotImplementedError:
583
return self.transport
584
raise errors.IncompatibleFormat(branch_format, self._format)
586
def get_repository_transport(self, repository_format):
587
"""See BzrDir.get_repository_transport()."""
588
if repository_format is None:
589
return self.transport
591
repository_format.get_format_string()
592
except NotImplementedError:
593
return self.transport
594
raise errors.IncompatibleFormat(repository_format, self._format)
596
def get_workingtree_transport(self, workingtree_format):
597
"""See BzrDir.get_workingtree_transport()."""
598
if workingtree_format is None:
599
return self.transport
601
workingtree_format.get_format_string()
602
except NotImplementedError:
603
return self.transport
604
raise errors.IncompatibleFormat(workingtree_format, self._format)
606
def needs_format_conversion(self, format=None):
607
"""See BzrDir.needs_format_conversion()."""
608
# if the format is not the same as the system default,
609
# an upgrade is needed.
611
format = BzrDirFormat.get_default_format()
612
return not isinstance(self._format, format.__class__)
614
def open_branch(self, unsupported=False):
615
"""See BzrDir.open_branch."""
616
from bzrlib.branch import BzrBranchFormat4
617
format = BzrBranchFormat4()
618
self._check_supported(format, unsupported)
619
return format.open(self, _found=True)
621
def sprout(self, url, revision_id=None, basis=None):
622
"""See BzrDir.sprout()."""
623
from bzrlib.workingtree import WorkingTreeFormat2
625
result = self._format.initialize(url, _cloning=True)
626
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
628
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
629
except errors.NoRepositoryPresent:
632
self.open_branch().sprout(result, revision_id=revision_id)
633
except errors.NotBranchError:
636
self.open_workingtree().clone(result, basis=basis_tree)
637
except (errors.NotBranchError, errors.NotLocalUrl):
638
# we always want a working tree
639
WorkingTreeFormat2().initialize(result)
643
class BzrDir4(BzrDirPreSplitOut):
644
"""A .bzr version 4 control object.
646
This is a deprecated format and may be removed after sept 2006.
649
def create_repository(self, shared=False):
650
"""See BzrDir.create_repository."""
651
return self._format.repository_format.initialize(self, shared)
653
def needs_format_conversion(self, format=None):
654
"""Format 4 dirs are always in need of conversion."""
657
def open_repository(self):
658
"""See BzrDir.open_repository."""
659
from bzrlib.repository import RepositoryFormat4
660
return RepositoryFormat4().open(self, _found=True)
663
class BzrDir5(BzrDirPreSplitOut):
664
"""A .bzr version 5 control object.
666
This is a deprecated format and may be removed after sept 2006.
669
def open_repository(self):
670
"""See BzrDir.open_repository."""
671
from bzrlib.repository import RepositoryFormat5
672
return RepositoryFormat5().open(self, _found=True)
674
def open_workingtree(self, _unsupported=False):
675
"""See BzrDir.create_workingtree."""
676
from bzrlib.workingtree import WorkingTreeFormat2
677
return WorkingTreeFormat2().open(self, _found=True)
680
class BzrDir6(BzrDirPreSplitOut):
681
"""A .bzr version 6 control object.
683
This is a deprecated format and may be removed after sept 2006.
686
def open_repository(self):
687
"""See BzrDir.open_repository."""
688
from bzrlib.repository import RepositoryFormat6
689
return RepositoryFormat6().open(self, _found=True)
691
def open_workingtree(self, _unsupported=False):
692
"""See BzrDir.create_workingtree."""
693
from bzrlib.workingtree import WorkingTreeFormat2
694
return WorkingTreeFormat2().open(self, _found=True)
697
class BzrDirMeta1(BzrDir):
698
"""A .bzr meta version 1 control object.
700
This is the first control object where the
701
individual formats are really split out.
704
def can_convert_format(self):
705
"""See BzrDir.can_convert_format()."""
708
def create_branch(self):
709
"""See BzrDir.create_branch."""
710
from bzrlib.branch import BranchFormat
711
return BranchFormat.get_default_format().initialize(self)
713
def create_repository(self, shared=False):
714
"""See BzrDir.create_repository."""
715
return self._format.repository_format.initialize(self, shared)
717
def create_workingtree(self, revision_id=None):
718
"""See BzrDir.create_workingtree."""
719
from bzrlib.workingtree import WorkingTreeFormat
720
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
722
def get_branch_transport(self, branch_format):
723
"""See BzrDir.get_branch_transport()."""
724
if branch_format is None:
725
return self.transport.clone('branch')
727
branch_format.get_format_string()
728
except NotImplementedError:
729
raise errors.IncompatibleFormat(branch_format, self._format)
731
self.transport.mkdir('branch')
732
except errors.FileExists:
734
return self.transport.clone('branch')
736
def get_repository_transport(self, repository_format):
737
"""See BzrDir.get_repository_transport()."""
738
if repository_format is None:
739
return self.transport.clone('repository')
741
repository_format.get_format_string()
742
except NotImplementedError:
743
raise errors.IncompatibleFormat(repository_format, self._format)
745
self.transport.mkdir('repository')
746
except errors.FileExists:
748
return self.transport.clone('repository')
750
def get_workingtree_transport(self, workingtree_format):
751
"""See BzrDir.get_workingtree_transport()."""
752
if workingtree_format is None:
753
return self.transport.clone('checkout')
755
workingtree_format.get_format_string()
756
except NotImplementedError:
757
raise errors.IncompatibleFormat(workingtree_format, self._format)
759
self.transport.mkdir('checkout')
760
except errors.FileExists:
762
return self.transport.clone('checkout')
764
def needs_format_conversion(self, format=None):
765
"""See BzrDir.needs_format_conversion()."""
767
format = BzrDirFormat.get_default_format()
768
if not isinstance(self._format, format.__class__):
769
# it is not a meta dir format, conversion is needed.
771
# we might want to push this down to the repository?
773
if not isinstance(self.open_repository()._format,
774
format.repository_format.__class__):
775
# the repository needs an upgrade.
777
except errors.NoRepositoryPresent:
779
# currently there are no other possible conversions for meta1 formats.
782
def open_branch(self, unsupported=False):
783
"""See BzrDir.open_branch."""
784
from bzrlib.branch import BranchFormat
785
format = BranchFormat.find_format(self)
786
self._check_supported(format, unsupported)
787
return format.open(self, _found=True)
789
def open_repository(self, unsupported=False):
790
"""See BzrDir.open_repository."""
791
from bzrlib.repository import RepositoryFormat
792
format = RepositoryFormat.find_format(self)
793
self._check_supported(format, unsupported)
794
return format.open(self, _found=True)
796
def open_workingtree(self, unsupported=False):
797
"""See BzrDir.open_workingtree."""
798
from bzrlib.workingtree import WorkingTreeFormat
799
format = WorkingTreeFormat.find_format(self)
800
self._check_supported(format, unsupported)
801
return format.open(self, _found=True)
804
class BzrDirFormat(object):
805
"""An encapsulation of the initialization and open routines for a format.
807
Formats provide three things:
808
* An initialization routine,
812
Formats are placed in an dict by their format string for reference
813
during bzrdir opening. These should be subclasses of BzrDirFormat
816
Once a format is deprecated, just deprecate the initialize and open
817
methods on the format class. Do not deprecate the object, as the
818
object will be created every system load.
821
_default_format = None
822
"""The default format used for new .bzr dirs."""
825
"""The known formats."""
828
def find_format(klass, transport):
829
"""Return the format registered for URL."""
831
format_string = transport.get(".bzr/branch-format").read()
832
return klass._formats[format_string]
833
except errors.NoSuchFile:
834
raise errors.NotBranchError(path=transport.base)
836
raise errors.UnknownFormatError(format_string)
839
def get_default_format(klass):
840
"""Return the current default format."""
841
return klass._default_format
843
def get_format_string(self):
844
"""Return the ASCII format string that identifies this format."""
845
raise NotImplementedError(self.get_format_string)
847
def get_converter(self, format=None):
848
"""Return the converter to use to convert bzrdirs needing converts.
850
This returns a bzrlib.bzrdir.Converter object.
852
This should return the best upgrader to step this format towards the
853
current default format. In the case of plugins we can/shouold provide
854
some means for them to extend the range of returnable converters.
856
:param format: Optional format to override the default foramt of the
859
raise NotImplementedError(self.get_converter)
861
def initialize(self, url):
862
"""Create a bzr control dir at this url and return an opened copy."""
863
# Since we don't have a .bzr directory, inherit the
864
# mode from the root directory
865
t = get_transport(url)
866
temp_control = LockableFiles(t, '')
867
temp_control._transport.mkdir('.bzr',
868
# FIXME: RBC 20060121 dont peek under
870
mode=temp_control._dir_mode)
871
file_mode = temp_control._file_mode
873
mutter('created control directory in ' + t.base)
874
control = t.clone('.bzr')
875
lock_file = 'branch-lock'
876
utf8_files = [('README',
877
"This is a Bazaar-NG control directory.\n"
878
"Do not change any files in this directory.\n"),
879
('branch-format', self.get_format_string()),
881
# NB: no need to escape relative paths that are url safe.
882
control.put(lock_file, StringIO(), mode=file_mode)
883
control_files = LockableFiles(control, lock_file)
884
control_files.lock_write()
886
for file, content in utf8_files:
887
control_files.put_utf8(file, content)
889
control_files.unlock()
890
return self.open(t, _found=True)
892
def is_supported(self):
893
"""Is this format supported?
895
Supported formats must be initializable and openable.
896
Unsupported formats may not support initialization or committing or
897
some other features depending on the reason for not being supported.
901
def open(self, transport, _found=False):
902
"""Return an instance of this format for the dir transport points at.
904
_found is a private parameter, do not use it.
907
assert isinstance(BzrDirFormat.find_format(transport),
909
return self._open(transport)
911
def _open(self, transport):
912
"""Template method helper for opening BzrDirectories.
914
This performs the actual open and any additional logic or parameter
917
raise NotImplementedError(self._open)
920
def register_format(klass, format):
921
klass._formats[format.get_format_string()] = format
924
def set_default_format(klass, format):
925
klass._default_format = format
928
return self.get_format_string()[:-1]
931
def unregister_format(klass, format):
932
assert klass._formats[format.get_format_string()] is format
933
del klass._formats[format.get_format_string()]
936
class BzrDirFormat4(BzrDirFormat):
939
This format is a combined format for working tree, branch and repository.
941
- Format 1 working trees [always]
942
- Format 4 branches [always]
943
- Format 4 repositories [always]
945
This format is deprecated: it indexes texts using a text it which is
946
removed in format 5; write support for this format has been removed.
949
def get_format_string(self):
950
"""See BzrDirFormat.get_format_string()."""
951
return "Bazaar-NG branch, format 0.0.4\n"
953
def get_converter(self, format=None):
954
"""See BzrDirFormat.get_converter()."""
955
# there is one and only one upgrade path here.
956
return ConvertBzrDir4To5()
958
def initialize(self, url):
959
"""Format 4 branches cannot be created."""
960
raise errors.UninitializableFormat(self)
962
def is_supported(self):
963
"""Format 4 is not supported.
965
It is not supported because the model changed from 4 to 5 and the
966
conversion logic is expensive - so doing it on the fly was not
971
def _open(self, transport):
972
"""See BzrDirFormat._open."""
973
return BzrDir4(transport, self)
975
def __return_repository_format(self):
976
"""Circular import protection."""
977
from bzrlib.repository import RepositoryFormat4
978
return RepositoryFormat4(self)
979
repository_format = property(__return_repository_format)
982
class BzrDirFormat5(BzrDirFormat):
983
"""Bzr control format 5.
985
This format is a combined format for working tree, branch and repository.
987
- Format 2 working trees [always]
988
- Format 4 branches [always]
989
- Format 5 repositories [always]
990
Unhashed stores in the repository.
993
def get_format_string(self):
994
"""See BzrDirFormat.get_format_string()."""
995
return "Bazaar-NG branch, format 5\n"
997
def get_converter(self, format=None):
998
"""See BzrDirFormat.get_converter()."""
999
# there is one and only one upgrade path here.
1000
return ConvertBzrDir5To6()
1002
def initialize(self, url, _cloning=False):
1003
"""Format 5 dirs always have working tree, branch and repository.
1005
Except when they are being cloned.
1007
from bzrlib.branch import BzrBranchFormat4
1008
from bzrlib.repository import RepositoryFormat5
1009
from bzrlib.workingtree import WorkingTreeFormat2
1010
result = super(BzrDirFormat5, self).initialize(url)
1011
RepositoryFormat5().initialize(result, _internal=True)
1013
BzrBranchFormat4().initialize(result)
1014
WorkingTreeFormat2().initialize(result)
1017
def _open(self, transport):
1018
"""See BzrDirFormat._open."""
1019
return BzrDir5(transport, self)
1021
def __return_repository_format(self):
1022
"""Circular import protection."""
1023
from bzrlib.repository import RepositoryFormat5
1024
return RepositoryFormat5(self)
1025
repository_format = property(__return_repository_format)
1028
class BzrDirFormat6(BzrDirFormat):
1029
"""Bzr control format 6.
1031
This format is a combined format for working tree, branch and repository.
1033
- Format 2 working trees [always]
1034
- Format 4 branches [always]
1035
- Format 6 repositories [always]
1038
def get_format_string(self):
1039
"""See BzrDirFormat.get_format_string()."""
1040
return "Bazaar-NG branch, format 6\n"
1042
def get_converter(self, format=None):
1043
"""See BzrDirFormat.get_converter()."""
1044
# there is one and only one upgrade path here.
1045
return ConvertBzrDir6ToMeta()
1047
def initialize(self, url, _cloning=False):
1048
"""Format 6 dirs always have working tree, branch and repository.
1050
Except when they are being cloned.
1052
from bzrlib.branch import BzrBranchFormat4
1053
from bzrlib.repository import RepositoryFormat6
1054
from bzrlib.workingtree import WorkingTreeFormat2
1055
result = super(BzrDirFormat6, self).initialize(url)
1056
RepositoryFormat6().initialize(result, _internal=True)
1058
BzrBranchFormat4().initialize(result)
1060
WorkingTreeFormat2().initialize(result)
1061
except errors.NotLocalUrl:
1062
# emulate pre-check behaviour for working tree and silently
1067
def _open(self, transport):
1068
"""See BzrDirFormat._open."""
1069
return BzrDir6(transport, self)
1071
def __return_repository_format(self):
1072
"""Circular import protection."""
1073
from bzrlib.repository import RepositoryFormat6
1074
return RepositoryFormat6(self)
1075
repository_format = property(__return_repository_format)
1078
class BzrDirMetaFormat1(BzrDirFormat):
1079
"""Bzr meta control format 1
1081
This is the first format with split out working tree, branch and repository
1084
- Format 3 working trees [optional]
1085
- Format 5 branches [optional]
1086
- Format 7 repositories [optional]
1089
def get_converter(self, format=None):
1090
"""See BzrDirFormat.get_converter()."""
1092
format = BzrDirFormat.get_default_format()
1093
if not isinstance(self, format.__class__):
1094
# converting away from metadir is not implemented
1095
raise NotImplementedError(self.get_converter)
1096
return ConvertMetaToMeta(format)
1098
def get_format_string(self):
1099
"""See BzrDirFormat.get_format_string()."""
1100
return "Bazaar-NG meta directory, format 1\n"
1102
def _open(self, transport):
1103
"""See BzrDirFormat._open."""
1104
return BzrDirMeta1(transport, self)
1106
def __return_repository_format(self):
1107
"""Circular import protection."""
1108
if getattr(self, '_repository_format', None):
1109
return self._repository_format
1110
from bzrlib.repository import RepositoryFormat
1111
return RepositoryFormat.get_default_format()
1113
def __set_repository_format(self, value):
1114
"""Allow changint the repository format for metadir formats."""
1115
self._repository_format = value
1116
repository_format = property(__return_repository_format, __set_repository_format)
1119
BzrDirFormat.register_format(BzrDirFormat4())
1120
BzrDirFormat.register_format(BzrDirFormat5())
1121
BzrDirFormat.register_format(BzrDirMetaFormat1())
1122
__default_format = BzrDirFormat6()
1123
BzrDirFormat.register_format(__default_format)
1124
BzrDirFormat.set_default_format(__default_format)
1127
class BzrDirTestProviderAdapter(object):
1128
"""A tool to generate a suite testing multiple bzrdir formats at once.
1130
This is done by copying the test once for each transport and injecting
1131
the transport_server, transport_readonly_server, and bzrdir_format
1132
classes into each copy. Each copy is also given a new id() to make it
1136
def __init__(self, transport_server, transport_readonly_server, formats):
1137
self._transport_server = transport_server
1138
self._transport_readonly_server = transport_readonly_server
1139
self._formats = formats
1141
def adapt(self, test):
1142
result = TestSuite()
1143
for format in self._formats:
1144
new_test = deepcopy(test)
1145
new_test.transport_server = self._transport_server
1146
new_test.transport_readonly_server = self._transport_readonly_server
1147
new_test.bzrdir_format = format
1148
def make_new_test_id():
1149
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1150
return lambda: new_id
1151
new_test.id = make_new_test_id()
1152
result.addTest(new_test)
1156
class ScratchDir(BzrDir6):
1157
"""Special test class: a bzrdir that cleans up itself..
1159
>>> d = ScratchDir()
1160
>>> base = d.transport.base
1163
>>> b.transport.__del__()
1168
def __init__(self, files=[], dirs=[], transport=None):
1169
"""Make a test branch.
1171
This creates a temporary directory and runs init-tree in it.
1173
If any files are listed, they are created in the working copy.
1175
if transport is None:
1176
transport = bzrlib.transport.local.ScratchTransport()
1177
# local import for scope restriction
1178
BzrDirFormat6().initialize(transport.base)
1179
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1180
self.create_repository()
1181
self.create_branch()
1182
self.create_workingtree()
1184
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1186
# BzrBranch creates a clone to .bzr and then forgets about the
1187
# original transport. A ScratchTransport() deletes itself and
1188
# everything underneath it when it goes away, so we need to
1189
# grab a local copy to prevent that from happening
1190
self._transport = transport
1193
self._transport.mkdir(d)
1196
self._transport.put(f, 'content of %s' % f)
1200
>>> orig = ScratchDir(files=["file1", "file2"])
1201
>>> os.listdir(orig.base)
1202
[u'.bzr', u'file1', u'file2']
1203
>>> clone = orig.clone()
1204
>>> if os.name != 'nt':
1205
... os.path.samefile(orig.base, clone.base)
1207
... orig.base == clone.base
1210
>>> os.listdir(clone.base)
1211
[u'.bzr', u'file1', u'file2']
1213
from shutil import copytree
1214
from bzrlib.osutils import mkdtemp
1217
copytree(self.base, base, symlinks=True)
1219
transport=bzrlib.transport.local.ScratchTransport(base))
1222
class Converter(object):
1223
"""Converts a disk format object from one format to another."""
1225
def convert(self, to_convert, pb):
1226
"""Perform the conversion of to_convert, giving feedback via pb.
1228
:param to_convert: The disk object to convert.
1229
:param pb: a progress bar to use for progress information.
1232
def step(self, message):
1233
"""Update the pb by a step."""
1235
self.pb.update(message, self.count, self.total)
1238
class ConvertBzrDir4To5(Converter):
1239
"""Converts format 4 bzr dirs to format 5."""
1242
super(ConvertBzrDir4To5, self).__init__()
1243
self.converted_revs = set()
1244
self.absent_revisions = set()
1248
def convert(self, to_convert, pb):
1249
"""See Converter.convert()."""
1250
self.bzrdir = to_convert
1252
self.pb.note('starting upgrade from format 4 to 5')
1253
if isinstance(self.bzrdir.transport, LocalTransport):
1254
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1255
self._convert_to_weaves()
1256
return BzrDir.open(self.bzrdir.root_transport.base)
1258
def _convert_to_weaves(self):
1259
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1262
stat = self.bzrdir.transport.stat('weaves')
1263
if not S_ISDIR(stat.st_mode):
1264
self.bzrdir.transport.delete('weaves')
1265
self.bzrdir.transport.mkdir('weaves')
1266
except errors.NoSuchFile:
1267
self.bzrdir.transport.mkdir('weaves')
1268
self.inv_weave = Weave('inventory')
1269
# holds in-memory weaves for all files
1270
self.text_weaves = {}
1271
self.bzrdir.transport.delete('branch-format')
1272
self.branch = self.bzrdir.open_branch()
1273
self._convert_working_inv()
1274
rev_history = self.branch.revision_history()
1275
# to_read is a stack holding the revisions we still need to process;
1276
# appending to it adds new highest-priority revisions
1277
self.known_revisions = set(rev_history)
1278
self.to_read = rev_history[-1:]
1280
rev_id = self.to_read.pop()
1281
if (rev_id not in self.revisions
1282
and rev_id not in self.absent_revisions):
1283
self._load_one_rev(rev_id)
1285
to_import = self._make_order()
1286
for i, rev_id in enumerate(to_import):
1287
self.pb.update('converting revision', i, len(to_import))
1288
self._convert_one_rev(rev_id)
1290
self._write_all_weaves()
1291
self._write_all_revs()
1292
self.pb.note('upgraded to weaves:')
1293
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1294
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1295
self.pb.note(' %6d texts', self.text_count)
1296
self._cleanup_spare_files_after_format4()
1297
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1299
def _cleanup_spare_files_after_format4(self):
1300
# FIXME working tree upgrade foo.
1301
for n in 'merged-patches', 'pending-merged-patches':
1303
## assert os.path.getsize(p) == 0
1304
self.bzrdir.transport.delete(n)
1305
except errors.NoSuchFile:
1307
self.bzrdir.transport.delete_tree('inventory-store')
1308
self.bzrdir.transport.delete_tree('text-store')
1310
def _convert_working_inv(self):
1311
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1312
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1313
# FIXME inventory is a working tree change.
1314
self.branch.control_files.put('inventory', new_inv_xml)
1316
def _write_all_weaves(self):
1317
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1318
weave_transport = self.bzrdir.transport.clone('weaves')
1319
weaves = WeaveStore(weave_transport, prefixed=False)
1320
transaction = PassThroughTransaction()
1322
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1325
for file_id, file_weave in self.text_weaves.items():
1326
self.pb.update('writing weave', i, len(self.text_weaves))
1327
weaves.put_weave(file_id, file_weave, transaction)
1332
def _write_all_revs(self):
1333
"""Write all revisions out in new form."""
1334
self.bzrdir.transport.delete_tree('revision-store')
1335
self.bzrdir.transport.mkdir('revision-store')
1336
revision_transport = self.bzrdir.transport.clone('revision-store')
1338
revision_store = TextStore(revision_transport,
1342
for i, rev_id in enumerate(self.converted_revs):
1343
self.pb.update('write revision', i, len(self.converted_revs))
1344
rev_tmp = StringIO()
1345
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1347
revision_store.add(rev_tmp, rev_id)
1351
def _load_one_rev(self, rev_id):
1352
"""Load a revision object into memory.
1354
Any parents not either loaded or abandoned get queued to be
1356
self.pb.update('loading revision',
1357
len(self.revisions),
1358
len(self.known_revisions))
1359
if not self.branch.repository.revision_store.has_id(rev_id):
1361
self.pb.note('revision {%s} not present in branch; '
1362
'will be converted as a ghost',
1364
self.absent_revisions.add(rev_id)
1366
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1367
rev = serializer_v4.read_revision_from_string(rev_xml)
1368
for parent_id in rev.parent_ids:
1369
self.known_revisions.add(parent_id)
1370
self.to_read.append(parent_id)
1371
self.revisions[rev_id] = rev
1373
def _load_old_inventory(self, rev_id):
1374
assert rev_id not in self.converted_revs
1375
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1376
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1377
rev = self.revisions[rev_id]
1378
if rev.inventory_sha1:
1379
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1380
'inventory sha mismatch for {%s}' % rev_id
1383
def _load_updated_inventory(self, rev_id):
1384
assert rev_id in self.converted_revs
1385
inv_xml = self.inv_weave.get_text(rev_id)
1386
inv = serializer_v5.read_inventory_from_string(inv_xml)
1389
def _convert_one_rev(self, rev_id):
1390
"""Convert revision and all referenced objects to new format."""
1391
rev = self.revisions[rev_id]
1392
inv = self._load_old_inventory(rev_id)
1393
present_parents = [p for p in rev.parent_ids
1394
if p not in self.absent_revisions]
1395
self._convert_revision_contents(rev, inv, present_parents)
1396
self._store_new_weave(rev, inv, present_parents)
1397
self.converted_revs.add(rev_id)
1399
def _store_new_weave(self, rev, inv, present_parents):
1400
# the XML is now updated with text versions
1404
if ie.kind == 'root_directory':
1406
assert hasattr(ie, 'revision'), \
1407
'no revision on {%s} in {%s}' % \
1408
(file_id, rev.revision_id)
1409
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1410
new_inv_sha1 = sha_string(new_inv_xml)
1411
self.inv_weave.add(rev.revision_id,
1413
new_inv_xml.splitlines(True),
1415
rev.inventory_sha1 = new_inv_sha1
1417
def _convert_revision_contents(self, rev, inv, present_parents):
1418
"""Convert all the files within a revision.
1420
Also upgrade the inventory to refer to the text revision ids."""
1421
rev_id = rev.revision_id
1422
mutter('converting texts of revision {%s}',
1424
parent_invs = map(self._load_updated_inventory, present_parents)
1427
self._convert_file_version(rev, ie, parent_invs)
1429
def _convert_file_version(self, rev, ie, parent_invs):
1430
"""Convert one version of one file.
1432
The file needs to be added into the weave if it is a merge
1433
of >=2 parents or if it's changed from its parent.
1435
if ie.kind == 'root_directory':
1437
file_id = ie.file_id
1438
rev_id = rev.revision_id
1439
w = self.text_weaves.get(file_id)
1442
self.text_weaves[file_id] = w
1443
text_changed = False
1444
previous_entries = ie.find_previous_heads(parent_invs, w)
1445
for old_revision in previous_entries:
1446
# if this fails, its a ghost ?
1447
assert old_revision in self.converted_revs
1448
self.snapshot_ie(previous_entries, ie, w, rev_id)
1450
assert getattr(ie, 'revision', None) is not None
1452
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1453
# TODO: convert this logic, which is ~= snapshot to
1454
# a call to:. This needs the path figured out. rather than a work_tree
1455
# a v4 revision_tree can be given, or something that looks enough like
1456
# one to give the file content to the entry if it needs it.
1457
# and we need something that looks like a weave store for snapshot to
1459
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1460
if len(previous_revisions) == 1:
1461
previous_ie = previous_revisions.values()[0]
1462
if ie._unchanged(previous_ie):
1463
ie.revision = previous_ie.revision
1465
parent_indexes = map(w.lookup, previous_revisions)
1467
text = self.branch.repository.text_store.get(ie.text_id)
1468
file_lines = text.readlines()
1469
assert sha_strings(file_lines) == ie.text_sha1
1470
assert sum(map(len, file_lines)) == ie.text_size
1471
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1472
self.text_count += 1
1474
w.add(rev_id, parent_indexes, [], None)
1475
ie.revision = rev_id
1477
def _make_order(self):
1478
"""Return a suitable order for importing revisions.
1480
The order must be such that an revision is imported after all
1481
its (present) parents.
1483
todo = set(self.revisions.keys())
1484
done = self.absent_revisions.copy()
1487
# scan through looking for a revision whose parents
1489
for rev_id in sorted(list(todo)):
1490
rev = self.revisions[rev_id]
1491
parent_ids = set(rev.parent_ids)
1492
if parent_ids.issubset(done):
1493
# can take this one now
1494
order.append(rev_id)
1500
class ConvertBzrDir5To6(Converter):
1501
"""Converts format 5 bzr dirs to format 6."""
1503
def convert(self, to_convert, pb):
1504
"""See Converter.convert()."""
1505
self.bzrdir = to_convert
1507
self.pb.note('starting upgrade from format 5 to 6')
1508
self._convert_to_prefixed()
1509
return BzrDir.open(self.bzrdir.root_transport.base)
1511
def _convert_to_prefixed(self):
1512
from bzrlib.store import hash_prefix
1513
self.bzrdir.transport.delete('branch-format')
1514
for store_name in ["weaves", "revision-store"]:
1515
self.pb.note("adding prefixes to %s" % store_name)
1516
store_transport = self.bzrdir.transport.clone(store_name)
1517
for filename in store_transport.list_dir('.'):
1518
if (filename.endswith(".weave") or
1519
filename.endswith(".gz") or
1520
filename.endswith(".sig")):
1521
file_id = os.path.splitext(filename)[0]
1524
prefix_dir = hash_prefix(file_id)
1525
# FIXME keep track of the dirs made RBC 20060121
1527
store_transport.move(filename, prefix_dir + '/' + filename)
1528
except errors.NoSuchFile: # catches missing dirs strangely enough
1529
store_transport.mkdir(prefix_dir)
1530
store_transport.move(filename, prefix_dir + '/' + filename)
1531
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1534
class ConvertBzrDir6ToMeta(Converter):
1535
"""Converts format 6 bzr dirs to metadirs."""
1537
def convert(self, to_convert, pb):
1538
"""See Converter.convert()."""
1539
self.bzrdir = to_convert
1542
self.total = 20 # the steps we know about
1543
self.garbage_inventories = []
1545
self.pb.note('starting upgrade from format 6 to metadir')
1546
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1547
# its faster to move specific files around than to open and use the apis...
1548
# first off, nuke ancestry.weave, it was never used.
1550
self.step('Removing ancestry.weave')
1551
self.bzrdir.transport.delete('ancestry.weave')
1552
except errors.NoSuchFile:
1554
# find out whats there
1555
self.step('Finding branch files')
1556
last_revision = self.bzrdir.open_workingtree().last_revision()
1557
bzrcontents = self.bzrdir.transport.list_dir('.')
1558
for name in bzrcontents:
1559
if name.startswith('basis-inventory.'):
1560
self.garbage_inventories.append(name)
1561
# create new directories for repository, working tree and branch
1562
dir_mode = self.bzrdir._control_files._dir_mode
1563
self.file_mode = self.bzrdir._control_files._file_mode
1564
repository_names = [('inventory.weave', True),
1565
('revision-store', True),
1567
self.step('Upgrading repository ')
1568
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1569
self.make_lock('repository')
1570
# we hard code the formats here because we are converting into
1571
# the meta format. The meta format upgrader can take this to a
1572
# future format within each component.
1573
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1574
for entry in repository_names:
1575
self.move_entry('repository', entry)
1577
self.step('Upgrading branch ')
1578
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1579
self.make_lock('branch')
1580
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1581
branch_files = [('revision-history', True),
1582
('branch-name', True),
1584
for entry in branch_files:
1585
self.move_entry('branch', entry)
1587
self.step('Upgrading working tree')
1588
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1589
self.make_lock('checkout')
1590
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1591
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1592
checkout_files = [('pending-merges', True),
1593
('inventory', True),
1594
('stat-cache', False)]
1595
for entry in checkout_files:
1596
self.move_entry('checkout', entry)
1597
if last_revision is not None:
1598
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1600
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1601
return BzrDir.open(self.bzrdir.root_transport.base)
1603
def make_lock(self, name):
1604
"""Make a lock for the new control dir name."""
1605
self.step('Make %s lock' % name)
1606
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1608
def move_entry(self, new_dir, entry):
1609
"""Move then entry name into new_dir."""
1611
mandatory = entry[1]
1612
self.step('Moving %s' % name)
1614
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1615
except errors.NoSuchFile:
1619
def put_format(self, dirname, format):
1620
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1623
class ConvertMetaToMeta(Converter):
1624
"""Converts the components of metadirs."""
1626
def __init__(self, target_format):
1627
"""Create a metadir to metadir converter.
1629
:param target_format: The final metadir format that is desired.
1631
self.target_format = target_format
1633
def convert(self, to_convert, pb):
1634
"""See Converter.convert()."""
1635
self.bzrdir = to_convert
1639
self.step('checking repository format')
1641
repo = self.bzrdir.open_repository()
1642
except errors.NoRepositoryPresent:
1645
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1646
from bzrlib.repository import CopyConverter
1647
self.pb.note('starting repository conversion')
1648
converter = CopyConverter(self.target_format.repository_format)
1649
converter.convert(repo, pb)