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.revision.text import TextRevisionStore
40
from bzrlib.store.text import TextStore
41
from bzrlib.store.versioned import WeaveStore
42
from bzrlib.symbol_versioning import *
43
from bzrlib.trace import mutter
44
from bzrlib.transactions import PassThroughTransaction
45
from bzrlib.transport import get_transport
46
from bzrlib.transport.local import LocalTransport
47
from bzrlib.weave import Weave
48
from bzrlib.xml4 import serializer_v4
49
from bzrlib.xml5 import serializer_v5
53
"""A .bzr control diretory.
55
BzrDir instances let you create or open any of the things that can be
56
found within .bzr - checkouts, branches and repositories.
59
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
61
a transport connected to the directory this bzr was opened from.
64
def can_convert_format(self):
65
"""Return true if this bzrdir is one whose format we can convert from."""
68
def _check_supported(self, format, allow_unsupported):
69
"""Check whether format is a supported format.
71
If allow_unsupported is True, this is a no-op.
73
if not allow_unsupported and not format.is_supported():
74
raise errors.UnsupportedFormatError(format)
76
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
77
"""Clone this bzrdir and its contents to url verbatim.
79
If urls last component does not exist, it will be created.
81
if revision_id is not None, then the clone operation may tune
82
itself to download less data.
83
:param force_new_repo: Do not use a shared repository for the target
84
even if one is available.
87
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
88
result = self._format.initialize(url)
90
local_repo = self.find_repository()
91
except errors.NoRepositoryPresent:
94
# may need to copy content in
96
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
99
result_repo = result.find_repository()
100
# fetch content this dir needs.
102
# XXX FIXME RBC 20060214 need tests for this when the basis
104
result_repo.fetch(basis_repo, revision_id=revision_id)
105
result_repo.fetch(local_repo, revision_id=revision_id)
106
except errors.NoRepositoryPresent:
107
# needed to make one anyway.
108
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
109
# 1 if there is a branch present
110
# make sure its content is available in the target repository
113
self.open_branch().clone(result, revision_id=revision_id)
114
except errors.NotBranchError:
117
self.open_workingtree().clone(result, basis=basis_tree)
118
except (errors.NoWorkingTree, errors.NotLocalUrl):
122
def _get_basis_components(self, basis):
123
"""Retrieve the basis components that are available at basis."""
125
return None, None, None
127
basis_tree = basis.open_workingtree()
128
basis_branch = basis_tree.branch
129
basis_repo = basis_branch.repository
130
except (errors.NoWorkingTree, errors.NotLocalUrl):
133
basis_branch = basis.open_branch()
134
basis_repo = basis_branch.repository
135
except errors.NotBranchError:
138
basis_repo = basis.open_repository()
139
except errors.NoRepositoryPresent:
141
return basis_repo, basis_branch, basis_tree
143
def _make_tail(self, url):
144
segments = url.split('/')
145
if segments and segments[-1] not in ('', '.'):
146
parent = '/'.join(segments[:-1])
147
t = bzrlib.transport.get_transport(parent)
149
t.mkdir(segments[-1])
150
except errors.FileExists:
155
"""Create a new BzrDir at the url 'base'.
157
This will call the current default formats initialize with base
158
as the only parameter.
160
If you need a specific format, consider creating an instance
161
of that and calling initialize().
163
segments = base.split('/')
164
if segments and segments[-1] not in ('', '.'):
165
parent = '/'.join(segments[:-1])
166
t = bzrlib.transport.get_transport(parent)
168
t.mkdir(segments[-1])
169
except errors.FileExists:
171
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
173
def create_branch(self):
174
"""Create a branch in this BzrDir.
176
The bzrdirs format will control what branch format is created.
177
For more control see BranchFormatXX.create(a_bzrdir).
179
raise NotImplementedError(self.create_branch)
182
def create_branch_and_repo(base, force_new_repo=False):
183
"""Create a new BzrDir, Branch and Repository at the url 'base'.
185
This will use the current default BzrDirFormat, and use whatever
186
repository format that that uses via bzrdir.create_branch and
187
create_repository. If a shared repository is available that is used
190
The created Branch object is returned.
192
:param base: The URL to create the branch at.
193
:param force_new_repo: If True a new repository is always created.
195
bzrdir = BzrDir.create(base)
196
bzrdir._find_or_create_repository(force_new_repo)
197
return bzrdir.create_branch()
199
def _find_or_create_repository(self, force_new_repo):
200
"""Create a new repository if needed, returning the repository."""
202
return self.create_repository()
204
return self.find_repository()
205
except errors.NoRepositoryPresent:
206
return self.create_repository()
209
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
210
"""Create a new BzrDir, Branch and Repository at the url 'base'.
212
This is a convenience function - it will use an existing repository
213
if possible, can be told explicitly whether to create a working tree or
216
This will use the current default BzrDirFormat, and use whatever
217
repository format that that uses via bzrdir.create_branch and
218
create_repository. If a shared repository is available that is used
219
preferentially. Whatever repository is used, its tree creation policy
222
The created Branch object is returned.
223
If a working tree cannot be made due to base not being a file:// url,
224
no error is raised unless force_new_tree is True, in which case no
225
data is created on disk and NotLocalUrl is raised.
227
:param base: The URL to create the branch at.
228
:param force_new_repo: If True a new repository is always created.
229
:param force_new_tree: If True or False force creation of a tree or
230
prevent such creation respectively.
233
# check for non local urls
234
t = get_transport(safe_unicode(base))
235
if not isinstance(t, LocalTransport):
236
raise errors.NotLocalUrl(base)
237
bzrdir = BzrDir.create(base)
238
repo = bzrdir._find_or_create_repository(force_new_repo)
239
result = bzrdir.create_branch()
240
if force_new_tree or (repo.make_working_trees() and
241
force_new_tree is None):
243
bzrdir.create_workingtree()
244
except errors.NotLocalUrl:
249
def create_repository(base, shared=False):
250
"""Create a new BzrDir and Repository at the url 'base'.
252
This will use the current default BzrDirFormat, and use whatever
253
repository format that that uses for bzrdirformat.create_repository.
255
;param shared: Create a shared repository rather than a standalone
257
The Repository object is returned.
259
This must be overridden as an instance method in child classes, where
260
it should take no parameters and construct whatever repository format
261
that child class desires.
263
bzrdir = BzrDir.create(base)
264
return bzrdir.create_repository()
267
def create_standalone_workingtree(base):
268
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
270
'base' must be a local path or a file:// url.
272
This will use the current default BzrDirFormat, and use whatever
273
repository format that that uses for bzrdirformat.create_workingtree,
274
create_branch and create_repository.
276
The WorkingTree object is returned.
278
t = get_transport(safe_unicode(base))
279
if not isinstance(t, LocalTransport):
280
raise errors.NotLocalUrl(base)
281
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
282
force_new_repo=True).bzrdir
283
return bzrdir.create_workingtree()
285
def create_workingtree(self, revision_id=None):
286
"""Create a working tree at this BzrDir.
288
revision_id: create it as of this revision id.
290
raise NotImplementedError(self.create_workingtree)
292
def find_repository(self):
293
"""Find the repository that should be used for a_bzrdir.
295
This does not require a branch as we use it to find the repo for
296
new branches as well as to hook existing branches up to their
300
return self.open_repository()
301
except errors.NoRepositoryPresent:
303
next_transport = self.root_transport.clone('..')
306
found_bzrdir = BzrDir.open_containing_from_transport(
308
except errors.NotBranchError:
309
raise errors.NoRepositoryPresent(self)
311
repository = found_bzrdir.open_repository()
312
except errors.NoRepositoryPresent:
313
next_transport = found_bzrdir.root_transport.clone('..')
315
if ((found_bzrdir.root_transport.base ==
316
self.root_transport.base) or repository.is_shared()):
319
raise errors.NoRepositoryPresent(self)
320
raise errors.NoRepositoryPresent(self)
322
def get_branch_transport(self, branch_format):
323
"""Get the transport for use by branch format in this BzrDir.
325
Note that bzr dirs that do not support format strings will raise
326
IncompatibleFormat if the branch format they are given has
327
a format string, and vice verca.
329
If branch_format is None, the transport is returned with no
330
checking. if it is not None, then the returned transport is
331
guaranteed to point to an existing directory ready for use.
333
raise NotImplementedError(self.get_branch_transport)
335
def get_repository_transport(self, repository_format):
336
"""Get the transport for use by repository format in this BzrDir.
338
Note that bzr dirs that do not support format strings will raise
339
IncompatibleFormat if the repository format they are given has
340
a format string, and vice verca.
342
If repository_format is None, the transport is returned with no
343
checking. if it is not None, then the returned transport is
344
guaranteed to point to an existing directory ready for use.
346
raise NotImplementedError(self.get_repository_transport)
348
def get_workingtree_transport(self, tree_format):
349
"""Get the transport for use by workingtree format in this BzrDir.
351
Note that bzr dirs that do not support format strings will raise
352
IncompatibleFormat if the workingtree format they are given has
353
a format string, and vice verca.
355
If workingtree_format is None, the transport is returned with no
356
checking. if it is not None, then the returned transport is
357
guaranteed to point to an existing directory ready for use.
359
raise NotImplementedError(self.get_workingtree_transport)
361
def __init__(self, _transport, _format):
362
"""Initialize a Bzr control dir object.
364
Only really common logic should reside here, concrete classes should be
365
made with varying behaviours.
367
:param _format: the format that is creating this BzrDir instance.
368
:param _transport: the transport this dir is based at.
370
self._format = _format
371
self.transport = _transport.clone('.bzr')
372
self.root_transport = _transport
374
def needs_format_conversion(self, format=None):
375
"""Return true if this bzrdir needs convert_format run on it.
377
For instance, if the repository format is out of date but the
378
branch and working tree are not, this should return True.
380
:param format: Optional parameter indicating a specific desired
381
format we plan to arrive at.
383
raise NotImplementedError(self.needs_format_conversion)
386
def open_unsupported(base):
387
"""Open a branch which is not supported."""
388
return BzrDir.open(base, _unsupported=True)
391
def open(base, _unsupported=False):
392
"""Open an existing bzrdir, rooted at 'base' (url)
394
_unsupported is a private parameter to the BzrDir class.
396
t = get_transport(base)
397
mutter("trying to open %r with transport %r", base, t)
398
format = BzrDirFormat.find_format(t)
399
if not _unsupported and not format.is_supported():
400
# see open_downlevel to open legacy branches.
401
raise errors.UnsupportedFormatError(
402
'sorry, format %s not supported' % format,
403
['use a different bzr version',
404
'or remove the .bzr directory'
405
' and "bzr init" again'])
406
return format.open(t, _found=True)
408
def open_branch(self, unsupported=False):
409
"""Open the branch object at this BzrDir if one is present.
411
If unsupported is True, then no longer supported branch formats can
414
TODO: static convenience version of this?
416
raise NotImplementedError(self.open_branch)
419
def open_containing(url):
420
"""Open an existing branch which contains url.
422
:param url: url to search from.
423
See open_containing_from_transport for more detail.
425
return BzrDir.open_containing_from_transport(get_transport(url))
428
def open_containing_from_transport(a_transport):
429
"""Open an existing branch which contains a_transport.base
431
This probes for a branch at a_transport, and searches upwards from there.
433
Basically we keep looking up until we find the control directory or
434
run into the root. If there isn't one, raises NotBranchError.
435
If there is one and it is either an unrecognised format or an unsupported
436
format, UnknownFormatError or UnsupportedFormatError are raised.
437
If there is one, it is returned, along with the unused portion of url.
439
# this gets the normalised url back. I.e. '.' -> the full path.
440
url = a_transport.base
443
format = BzrDirFormat.find_format(a_transport)
444
return format.open(a_transport), a_transport.relpath(url)
445
except errors.NotBranchError, e:
446
mutter('not a branch in: %r %s', a_transport.base, e)
447
new_t = a_transport.clone('..')
448
if new_t.base == a_transport.base:
449
# reached the root, whatever that may be
450
raise errors.NotBranchError(path=url)
453
def open_repository(self, _unsupported=False):
454
"""Open the repository object at this BzrDir if one is present.
456
This will not follow the Branch object pointer - its strictly a direct
457
open facility. Most client code should use open_branch().repository to
460
_unsupported is a private parameter, not part of the api.
461
TODO: static convenience version of this?
463
raise NotImplementedError(self.open_repository)
465
def open_workingtree(self, _unsupported=False):
466
"""Open the workingtree object at this BzrDir if one is present.
468
TODO: static convenience version of this?
470
raise NotImplementedError(self.open_workingtree)
472
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
473
"""Create a copy of this bzrdir prepared for use as a new line of
476
If urls last component does not exist, it will be created.
478
Attributes related to the identity of the source branch like
479
branch nickname will be cleaned, a working tree is created
480
whether one existed before or not; and a local branch is always
483
if revision_id is not None, then the clone operation may tune
484
itself to download less data.
487
result = self._format.initialize(url)
488
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
490
source_branch = self.open_branch()
491
source_repository = source_branch.repository
492
except errors.NotBranchError:
495
source_repository = self.open_repository()
496
except errors.NoRepositoryPresent:
497
# copy the entire basis one if there is one
498
# but there is no repository.
499
source_repository = basis_repo
504
result_repo = result.find_repository()
505
except errors.NoRepositoryPresent:
507
if source_repository is None and result_repo is not None:
509
elif source_repository is None and result_repo is None:
510
# no repo available, make a new one
511
result.create_repository()
512
elif source_repository is not None and result_repo is None:
513
# have soure, and want to make a new target repo
514
source_repository.clone(result,
515
revision_id=revision_id,
518
# fetch needed content into target.
520
# XXX FIXME RBC 20060214 need tests for this when the basis
522
result_repo.fetch(basis_repo, revision_id=revision_id)
523
result_repo.fetch(source_repository, revision_id=revision_id)
524
if source_branch is not None:
525
source_branch.sprout(result, revision_id=revision_id)
527
result.create_branch()
528
result.create_workingtree()
532
class BzrDirPreSplitOut(BzrDir):
533
"""A common class for the all-in-one formats."""
535
def __init__(self, _transport, _format):
536
"""See BzrDir.__init__."""
537
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
538
self._control_files = LockableFiles(self.get_branch_transport(None),
541
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
542
"""See BzrDir.clone()."""
543
from bzrlib.workingtree import WorkingTreeFormat2
545
result = self._format.initialize(url, _cloning=True)
546
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
547
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
548
self.open_branch().clone(result, revision_id=revision_id)
550
self.open_workingtree().clone(result, basis=basis_tree)
551
except errors.NotLocalUrl:
552
# make a new one, this format always has to have one.
553
WorkingTreeFormat2().initialize(result)
556
def create_branch(self):
557
"""See BzrDir.create_branch."""
558
return self.open_branch()
560
def create_repository(self, shared=False):
561
"""See BzrDir.create_repository."""
563
raise errors.IncompatibleFormat('shared repository', self._format)
564
return self.open_repository()
566
def create_workingtree(self, revision_id=None):
567
"""See BzrDir.create_workingtree."""
568
# this looks buggy but is not -really-
569
# clone and sprout will have set the revision_id
570
# and that will have set it for us, its only
571
# specific uses of create_workingtree in isolation
572
# that can do wonky stuff here, and that only
573
# happens for creating checkouts, which cannot be
574
# done on this format anyway. So - acceptable wart.
575
result = self.open_workingtree()
576
if revision_id is not None:
577
result.set_last_revision(revision_id)
580
def get_branch_transport(self, branch_format):
581
"""See BzrDir.get_branch_transport()."""
582
if branch_format is None:
583
return self.transport
585
branch_format.get_format_string()
586
except NotImplementedError:
587
return self.transport
588
raise errors.IncompatibleFormat(branch_format, self._format)
590
def get_repository_transport(self, repository_format):
591
"""See BzrDir.get_repository_transport()."""
592
if repository_format is None:
593
return self.transport
595
repository_format.get_format_string()
596
except NotImplementedError:
597
return self.transport
598
raise errors.IncompatibleFormat(repository_format, self._format)
600
def get_workingtree_transport(self, workingtree_format):
601
"""See BzrDir.get_workingtree_transport()."""
602
if workingtree_format is None:
603
return self.transport
605
workingtree_format.get_format_string()
606
except NotImplementedError:
607
return self.transport
608
raise errors.IncompatibleFormat(workingtree_format, self._format)
610
def needs_format_conversion(self, format=None):
611
"""See BzrDir.needs_format_conversion()."""
612
# if the format is not the same as the system default,
613
# an upgrade is needed.
615
format = BzrDirFormat.get_default_format()
616
return not isinstance(self._format, format.__class__)
618
def open_branch(self, unsupported=False):
619
"""See BzrDir.open_branch."""
620
from bzrlib.branch import BzrBranchFormat4
621
format = BzrBranchFormat4()
622
self._check_supported(format, unsupported)
623
return format.open(self, _found=True)
625
def sprout(self, url, revision_id=None, basis=None):
626
"""See BzrDir.sprout()."""
627
from bzrlib.workingtree import WorkingTreeFormat2
629
result = self._format.initialize(url, _cloning=True)
630
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
632
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
633
except errors.NoRepositoryPresent:
636
self.open_branch().sprout(result, revision_id=revision_id)
637
except errors.NotBranchError:
639
# we always want a working tree
640
WorkingTreeFormat2().initialize(result)
644
class BzrDir4(BzrDirPreSplitOut):
645
"""A .bzr version 4 control object.
647
This is a deprecated format and may be removed after sept 2006.
650
def create_repository(self, shared=False):
651
"""See BzrDir.create_repository."""
652
return self._format.repository_format.initialize(self, shared)
654
def needs_format_conversion(self, format=None):
655
"""Format 4 dirs are always in need of conversion."""
658
def open_repository(self):
659
"""See BzrDir.open_repository."""
660
from bzrlib.repository import RepositoryFormat4
661
return RepositoryFormat4().open(self, _found=True)
664
class BzrDir5(BzrDirPreSplitOut):
665
"""A .bzr version 5 control object.
667
This is a deprecated format and may be removed after sept 2006.
670
def open_repository(self):
671
"""See BzrDir.open_repository."""
672
from bzrlib.repository import RepositoryFormat5
673
return RepositoryFormat5().open(self, _found=True)
675
def open_workingtree(self, _unsupported=False):
676
"""See BzrDir.create_workingtree."""
677
from bzrlib.workingtree import WorkingTreeFormat2
678
return WorkingTreeFormat2().open(self, _found=True)
681
class BzrDir6(BzrDirPreSplitOut):
682
"""A .bzr version 6 control object.
684
This is a deprecated format and may be removed after sept 2006.
687
def open_repository(self):
688
"""See BzrDir.open_repository."""
689
from bzrlib.repository import RepositoryFormat6
690
return RepositoryFormat6().open(self, _found=True)
692
def open_workingtree(self, _unsupported=False):
693
"""See BzrDir.create_workingtree."""
694
from bzrlib.workingtree import WorkingTreeFormat2
695
return WorkingTreeFormat2().open(self, _found=True)
698
class BzrDirMeta1(BzrDir):
699
"""A .bzr meta version 1 control object.
701
This is the first control object where the
702
individual formats are really split out.
705
def can_convert_format(self):
706
"""See BzrDir.can_convert_format()."""
709
def create_branch(self):
710
"""See BzrDir.create_branch."""
711
from bzrlib.branch import BranchFormat
712
return BranchFormat.get_default_format().initialize(self)
714
def create_repository(self, shared=False):
715
"""See BzrDir.create_repository."""
716
return self._format.repository_format.initialize(self, shared)
718
def create_workingtree(self, revision_id=None):
719
"""See BzrDir.create_workingtree."""
720
from bzrlib.workingtree import WorkingTreeFormat
721
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
723
def get_branch_transport(self, branch_format):
724
"""See BzrDir.get_branch_transport()."""
725
if branch_format is None:
726
return self.transport.clone('branch')
728
branch_format.get_format_string()
729
except NotImplementedError:
730
raise errors.IncompatibleFormat(branch_format, self._format)
732
self.transport.mkdir('branch')
733
except errors.FileExists:
735
return self.transport.clone('branch')
737
def get_repository_transport(self, repository_format):
738
"""See BzrDir.get_repository_transport()."""
739
if repository_format is None:
740
return self.transport.clone('repository')
742
repository_format.get_format_string()
743
except NotImplementedError:
744
raise errors.IncompatibleFormat(repository_format, self._format)
746
self.transport.mkdir('repository')
747
except errors.FileExists:
749
return self.transport.clone('repository')
751
def get_workingtree_transport(self, workingtree_format):
752
"""See BzrDir.get_workingtree_transport()."""
753
if workingtree_format is None:
754
return self.transport.clone('checkout')
756
workingtree_format.get_format_string()
757
except NotImplementedError:
758
raise errors.IncompatibleFormat(workingtree_format, self._format)
760
self.transport.mkdir('checkout')
761
except errors.FileExists:
763
return self.transport.clone('checkout')
765
def needs_format_conversion(self, format=None):
766
"""See BzrDir.needs_format_conversion()."""
768
format = BzrDirFormat.get_default_format()
769
if not isinstance(self._format, format.__class__):
770
# it is not a meta dir format, conversion is needed.
772
# we might want to push this down to the repository?
774
if not isinstance(self.open_repository()._format,
775
format.repository_format.__class__):
776
# the repository needs an upgrade.
778
except errors.NoRepositoryPresent:
780
# currently there are no other possible conversions for meta1 formats.
783
def open_branch(self, unsupported=False):
784
"""See BzrDir.open_branch."""
785
from bzrlib.branch import BranchFormat
786
format = BranchFormat.find_format(self)
787
self._check_supported(format, unsupported)
788
return format.open(self, _found=True)
790
def open_repository(self, unsupported=False):
791
"""See BzrDir.open_repository."""
792
from bzrlib.repository import RepositoryFormat
793
format = RepositoryFormat.find_format(self)
794
self._check_supported(format, unsupported)
795
return format.open(self, _found=True)
797
def open_workingtree(self, unsupported=False):
798
"""See BzrDir.open_workingtree."""
799
from bzrlib.workingtree import WorkingTreeFormat
800
format = WorkingTreeFormat.find_format(self)
801
self._check_supported(format, unsupported)
802
return format.open(self, _found=True)
805
class BzrDirFormat(object):
806
"""An encapsulation of the initialization and open routines for a format.
808
Formats provide three things:
809
* An initialization routine,
813
Formats are placed in an dict by their format string for reference
814
during bzrdir opening. These should be subclasses of BzrDirFormat
817
Once a format is deprecated, just deprecate the initialize and open
818
methods on the format class. Do not deprecate the object, as the
819
object will be created every system load.
822
_default_format = None
823
"""The default format used for new .bzr dirs."""
826
"""The known formats."""
829
def find_format(klass, transport):
830
"""Return the format registered for URL."""
832
format_string = transport.get(".bzr/branch-format").read()
833
return klass._formats[format_string]
834
except errors.NoSuchFile:
835
raise errors.NotBranchError(path=transport.base)
837
raise errors.UnknownFormatError(format_string)
840
def get_default_format(klass):
841
"""Return the current default format."""
842
return klass._default_format
844
def get_format_string(self):
845
"""Return the ASCII format string that identifies this format."""
846
raise NotImplementedError(self.get_format_string)
848
def get_converter(self, format=None):
849
"""Return the converter to use to convert bzrdirs needing converts.
851
This returns a bzrlib.bzrdir.Converter object.
853
This should return the best upgrader to step this format towards the
854
current default format. In the case of plugins we can/shouold provide
855
some means for them to extend the range of returnable converters.
857
:param format: Optional format to override the default foramt of the
860
raise NotImplementedError(self.get_converter)
862
def initialize(self, url):
863
"""Create a bzr control dir at this url and return an opened copy."""
864
# Since we don't have a .bzr directory, inherit the
865
# mode from the root directory
866
t = get_transport(url)
867
temp_control = LockableFiles(t, '')
868
temp_control._transport.mkdir('.bzr',
869
# FIXME: RBC 20060121 dont peek under
871
mode=temp_control._dir_mode)
872
file_mode = temp_control._file_mode
874
mutter('created control directory in ' + t.base)
875
control = t.clone('.bzr')
876
lock_file = 'branch-lock'
877
utf8_files = [('README',
878
"This is a Bazaar-NG control directory.\n"
879
"Do not change any files in this directory.\n"),
880
('branch-format', self.get_format_string()),
882
# NB: no need to escape relative paths that are url safe.
883
control.put(lock_file, StringIO(), mode=file_mode)
884
control_files = LockableFiles(control, lock_file)
885
control_files.lock_write()
887
for file, content in utf8_files:
888
control_files.put_utf8(file, content)
890
control_files.unlock()
891
return self.open(t, _found=True)
893
def is_supported(self):
894
"""Is this format supported?
896
Supported formats must be initializable and openable.
897
Unsupported formats may not support initialization or committing or
898
some other features depending on the reason for not being supported.
902
def open(self, transport, _found=False):
903
"""Return an instance of this format for the dir transport points at.
905
_found is a private parameter, do not use it.
908
assert isinstance(BzrDirFormat.find_format(transport),
910
return self._open(transport)
912
def _open(self, transport):
913
"""Template method helper for opening BzrDirectories.
915
This performs the actual open and any additional logic or parameter
918
raise NotImplementedError(self._open)
921
def register_format(klass, format):
922
klass._formats[format.get_format_string()] = format
925
def set_default_format(klass, format):
926
klass._default_format = format
929
return self.get_format_string()[:-1]
932
def unregister_format(klass, format):
933
assert klass._formats[format.get_format_string()] is format
934
del klass._formats[format.get_format_string()]
937
class BzrDirFormat4(BzrDirFormat):
940
This format is a combined format for working tree, branch and repository.
942
- Format 1 working trees [always]
943
- Format 4 branches [always]
944
- Format 4 repositories [always]
946
This format is deprecated: it indexes texts using a text it which is
947
removed in format 5; write support for this format has been removed.
950
def get_format_string(self):
951
"""See BzrDirFormat.get_format_string()."""
952
return "Bazaar-NG branch, format 0.0.4\n"
954
def get_converter(self, format=None):
955
"""See BzrDirFormat.get_converter()."""
956
# there is one and only one upgrade path here.
957
return ConvertBzrDir4To5()
959
def initialize(self, url):
960
"""Format 4 branches cannot be created."""
961
raise errors.UninitializableFormat(self)
963
def is_supported(self):
964
"""Format 4 is not supported.
966
It is not supported because the model changed from 4 to 5 and the
967
conversion logic is expensive - so doing it on the fly was not
972
def _open(self, transport):
973
"""See BzrDirFormat._open."""
974
return BzrDir4(transport, self)
976
def __return_repository_format(self):
977
"""Circular import protection."""
978
from bzrlib.repository import RepositoryFormat4
979
return RepositoryFormat4(self)
980
repository_format = property(__return_repository_format)
983
class BzrDirFormat5(BzrDirFormat):
984
"""Bzr control format 5.
986
This format is a combined format for working tree, branch and repository.
988
- Format 2 working trees [always]
989
- Format 4 branches [always]
990
- Format 5 repositories [always]
991
Unhashed stores in the repository.
994
def get_format_string(self):
995
"""See BzrDirFormat.get_format_string()."""
996
return "Bazaar-NG branch, format 5\n"
998
def get_converter(self, format=None):
999
"""See BzrDirFormat.get_converter()."""
1000
# there is one and only one upgrade path here.
1001
return ConvertBzrDir5To6()
1003
def initialize(self, url, _cloning=False):
1004
"""Format 5 dirs always have working tree, branch and repository.
1006
Except when they are being cloned.
1008
from bzrlib.branch import BzrBranchFormat4
1009
from bzrlib.repository import RepositoryFormat5
1010
from bzrlib.workingtree import WorkingTreeFormat2
1011
result = super(BzrDirFormat5, self).initialize(url)
1012
RepositoryFormat5().initialize(result, _internal=True)
1014
BzrBranchFormat4().initialize(result)
1015
WorkingTreeFormat2().initialize(result)
1018
def _open(self, transport):
1019
"""See BzrDirFormat._open."""
1020
return BzrDir5(transport, self)
1022
def __return_repository_format(self):
1023
"""Circular import protection."""
1024
from bzrlib.repository import RepositoryFormat5
1025
return RepositoryFormat5(self)
1026
repository_format = property(__return_repository_format)
1029
class BzrDirFormat6(BzrDirFormat):
1030
"""Bzr control format 6.
1032
This format is a combined format for working tree, branch and repository.
1034
- Format 2 working trees [always]
1035
- Format 4 branches [always]
1036
- Format 6 repositories [always]
1039
def get_format_string(self):
1040
"""See BzrDirFormat.get_format_string()."""
1041
return "Bazaar-NG branch, format 6\n"
1043
def get_converter(self, format=None):
1044
"""See BzrDirFormat.get_converter()."""
1045
# there is one and only one upgrade path here.
1046
return ConvertBzrDir6ToMeta()
1048
def initialize(self, url, _cloning=False):
1049
"""Format 6 dirs always have working tree, branch and repository.
1051
Except when they are being cloned.
1053
from bzrlib.branch import BzrBranchFormat4
1054
from bzrlib.repository import RepositoryFormat6
1055
from bzrlib.workingtree import WorkingTreeFormat2
1056
result = super(BzrDirFormat6, self).initialize(url)
1057
RepositoryFormat6().initialize(result, _internal=True)
1059
BzrBranchFormat4().initialize(result)
1061
WorkingTreeFormat2().initialize(result)
1062
except errors.NotLocalUrl:
1063
# emulate pre-check behaviour for working tree and silently
1068
def _open(self, transport):
1069
"""See BzrDirFormat._open."""
1070
return BzrDir6(transport, self)
1072
def __return_repository_format(self):
1073
"""Circular import protection."""
1074
from bzrlib.repository import RepositoryFormat6
1075
return RepositoryFormat6(self)
1076
repository_format = property(__return_repository_format)
1079
class BzrDirMetaFormat1(BzrDirFormat):
1080
"""Bzr meta control format 1
1082
This is the first format with split out working tree, branch and repository
1085
- Format 3 working trees [optional]
1086
- Format 5 branches [optional]
1087
- Format 7 repositories [optional]
1090
def get_converter(self, format=None):
1091
"""See BzrDirFormat.get_converter()."""
1093
format = BzrDirFormat.get_default_format()
1094
if not isinstance(self, format.__class__):
1095
# converting away from metadir is not implemented
1096
raise NotImplementedError(self.get_converter)
1097
return ConvertMetaToMeta(format)
1099
def get_format_string(self):
1100
"""See BzrDirFormat.get_format_string()."""
1101
return "Bazaar-NG meta directory, format 1\n"
1103
def _open(self, transport):
1104
"""See BzrDirFormat._open."""
1105
return BzrDirMeta1(transport, self)
1107
def __return_repository_format(self):
1108
"""Circular import protection."""
1109
if getattr(self, '_repository_format', None):
1110
return self._repository_format
1111
from bzrlib.repository import RepositoryFormat
1112
return RepositoryFormat.get_default_format()
1114
def __set_repository_format(self, value):
1115
"""Allow changint the repository format for metadir formats."""
1116
self._repository_format = value
1117
repository_format = property(__return_repository_format, __set_repository_format)
1120
BzrDirFormat.register_format(BzrDirFormat4())
1121
BzrDirFormat.register_format(BzrDirFormat5())
1122
BzrDirFormat.register_format(BzrDirMetaFormat1())
1123
__default_format = BzrDirFormat6()
1124
BzrDirFormat.register_format(__default_format)
1125
BzrDirFormat.set_default_format(__default_format)
1128
class BzrDirTestProviderAdapter(object):
1129
"""A tool to generate a suite testing multiple bzrdir formats at once.
1131
This is done by copying the test once for each transport and injecting
1132
the transport_server, transport_readonly_server, and bzrdir_format
1133
classes into each copy. Each copy is also given a new id() to make it
1137
def __init__(self, transport_server, transport_readonly_server, formats):
1138
self._transport_server = transport_server
1139
self._transport_readonly_server = transport_readonly_server
1140
self._formats = formats
1142
def adapt(self, test):
1143
result = TestSuite()
1144
for format in self._formats:
1145
new_test = deepcopy(test)
1146
new_test.transport_server = self._transport_server
1147
new_test.transport_readonly_server = self._transport_readonly_server
1148
new_test.bzrdir_format = format
1149
def make_new_test_id():
1150
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1151
return lambda: new_id
1152
new_test.id = make_new_test_id()
1153
result.addTest(new_test)
1157
class ScratchDir(BzrDir6):
1158
"""Special test class: a bzrdir that cleans up itself..
1160
>>> d = ScratchDir()
1161
>>> base = d.transport.base
1164
>>> b.transport.__del__()
1169
def __init__(self, files=[], dirs=[], transport=None):
1170
"""Make a test branch.
1172
This creates a temporary directory and runs init-tree in it.
1174
If any files are listed, they are created in the working copy.
1176
if transport is None:
1177
transport = bzrlib.transport.local.ScratchTransport()
1178
# local import for scope restriction
1179
BzrDirFormat6().initialize(transport.base)
1180
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1181
self.create_repository()
1182
self.create_branch()
1183
self.create_workingtree()
1185
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1187
# BzrBranch creates a clone to .bzr and then forgets about the
1188
# original transport. A ScratchTransport() deletes itself and
1189
# everything underneath it when it goes away, so we need to
1190
# grab a local copy to prevent that from happening
1191
self._transport = transport
1194
self._transport.mkdir(d)
1197
self._transport.put(f, 'content of %s' % f)
1201
>>> orig = ScratchDir(files=["file1", "file2"])
1202
>>> os.listdir(orig.base)
1203
[u'.bzr', u'file1', u'file2']
1204
>>> clone = orig.clone()
1205
>>> if os.name != 'nt':
1206
... os.path.samefile(orig.base, clone.base)
1208
... orig.base == clone.base
1211
>>> os.listdir(clone.base)
1212
[u'.bzr', u'file1', u'file2']
1214
from shutil import copytree
1215
from bzrlib.osutils import mkdtemp
1218
copytree(self.base, base, symlinks=True)
1220
transport=bzrlib.transport.local.ScratchTransport(base))
1223
class Converter(object):
1224
"""Converts a disk format object from one format to another."""
1226
def convert(self, to_convert, pb):
1227
"""Perform the conversion of to_convert, giving feedback via pb.
1229
:param to_convert: The disk object to convert.
1230
:param pb: a progress bar to use for progress information.
1233
def step(self, message):
1234
"""Update the pb by a step."""
1236
self.pb.update(message, self.count, self.total)
1239
class ConvertBzrDir4To5(Converter):
1240
"""Converts format 4 bzr dirs to format 5."""
1243
super(ConvertBzrDir4To5, self).__init__()
1244
self.converted_revs = set()
1245
self.absent_revisions = set()
1249
def convert(self, to_convert, pb):
1250
"""See Converter.convert()."""
1251
self.bzrdir = to_convert
1253
self.pb.note('starting upgrade from format 4 to 5')
1254
if isinstance(self.bzrdir.transport, LocalTransport):
1255
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1256
self._convert_to_weaves()
1257
return BzrDir.open(self.bzrdir.root_transport.base)
1259
def _convert_to_weaves(self):
1260
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1263
stat = self.bzrdir.transport.stat('weaves')
1264
if not S_ISDIR(stat.st_mode):
1265
self.bzrdir.transport.delete('weaves')
1266
self.bzrdir.transport.mkdir('weaves')
1267
except errors.NoSuchFile:
1268
self.bzrdir.transport.mkdir('weaves')
1269
# deliberately not a WeaveFile as we want to build it up slowly.
1270
self.inv_weave = Weave('inventory')
1271
# holds in-memory weaves for all files
1272
self.text_weaves = {}
1273
self.bzrdir.transport.delete('branch-format')
1274
self.branch = self.bzrdir.open_branch()
1275
self._convert_working_inv()
1276
rev_history = self.branch.revision_history()
1277
# to_read is a stack holding the revisions we still need to process;
1278
# appending to it adds new highest-priority revisions
1279
self.known_revisions = set(rev_history)
1280
self.to_read = rev_history[-1:]
1282
rev_id = self.to_read.pop()
1283
if (rev_id not in self.revisions
1284
and rev_id not in self.absent_revisions):
1285
self._load_one_rev(rev_id)
1287
to_import = self._make_order()
1288
for i, rev_id in enumerate(to_import):
1289
self.pb.update('converting revision', i, len(to_import))
1290
self._convert_one_rev(rev_id)
1292
self._write_all_weaves()
1293
self._write_all_revs()
1294
self.pb.note('upgraded to weaves:')
1295
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1296
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1297
self.pb.note(' %6d texts', self.text_count)
1298
self._cleanup_spare_files_after_format4()
1299
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1301
def _cleanup_spare_files_after_format4(self):
1302
# FIXME working tree upgrade foo.
1303
for n in 'merged-patches', 'pending-merged-patches':
1305
## assert os.path.getsize(p) == 0
1306
self.bzrdir.transport.delete(n)
1307
except errors.NoSuchFile:
1309
self.bzrdir.transport.delete_tree('inventory-store')
1310
self.bzrdir.transport.delete_tree('text-store')
1312
def _convert_working_inv(self):
1313
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1314
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1315
# FIXME inventory is a working tree change.
1316
self.branch.control_files.put('inventory', new_inv_xml)
1318
def _write_all_weaves(self):
1319
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1320
weave_transport = self.bzrdir.transport.clone('weaves')
1321
weaves = WeaveStore(weave_transport, prefixed=False)
1322
transaction = PassThroughTransaction()
1326
for file_id, file_weave in self.text_weaves.items():
1327
self.pb.update('writing weave', i, len(self.text_weaves))
1328
weaves._put_weave(file_id, file_weave, transaction)
1330
self.pb.update('inventory', 0, 1)
1331
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1332
self.pb.update('inventory', 1, 1)
1336
def _write_all_revs(self):
1337
"""Write all revisions out in new form."""
1338
self.bzrdir.transport.delete_tree('revision-store')
1339
self.bzrdir.transport.mkdir('revision-store')
1340
revision_transport = self.bzrdir.transport.clone('revision-store')
1342
_revision_store = TextRevisionStore(TextStore(revision_transport,
1346
transaction = bzrlib.transactions.PassThroughTransaction()
1347
for i, rev_id in enumerate(self.converted_revs):
1348
self.pb.update('write revision', i, len(self.converted_revs))
1349
_revision_store.add_revision(self.revisions[rev_id], transaction)
1353
def _load_one_rev(self, rev_id):
1354
"""Load a revision object into memory.
1356
Any parents not either loaded or abandoned get queued to be
1358
self.pb.update('loading revision',
1359
len(self.revisions),
1360
len(self.known_revisions))
1361
if not self.branch.repository.has_revision(rev_id):
1363
self.pb.note('revision {%s} not present in branch; '
1364
'will be converted as a ghost',
1366
self.absent_revisions.add(rev_id)
1368
rev = self.branch.repository._revision_store.get_revision(rev_id,
1369
self.branch.repository.get_transaction())
1370
for parent_id in rev.parent_ids:
1371
self.known_revisions.add(parent_id)
1372
self.to_read.append(parent_id)
1373
self.revisions[rev_id] = rev
1375
def _load_old_inventory(self, rev_id):
1376
assert rev_id not in self.converted_revs
1377
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1378
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1379
rev = self.revisions[rev_id]
1380
if rev.inventory_sha1:
1381
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1382
'inventory sha mismatch for {%s}' % rev_id
1385
def _load_updated_inventory(self, rev_id):
1386
assert rev_id in self.converted_revs
1387
inv_xml = self.inv_weave.get_text(rev_id)
1388
inv = serializer_v5.read_inventory_from_string(inv_xml)
1391
def _convert_one_rev(self, rev_id):
1392
"""Convert revision and all referenced objects to new format."""
1393
rev = self.revisions[rev_id]
1394
inv = self._load_old_inventory(rev_id)
1395
present_parents = [p for p in rev.parent_ids
1396
if p not in self.absent_revisions]
1397
self._convert_revision_contents(rev, inv, present_parents)
1398
self._store_new_weave(rev, inv, present_parents)
1399
self.converted_revs.add(rev_id)
1401
def _store_new_weave(self, rev, inv, present_parents):
1402
# the XML is now updated with text versions
1406
if ie.kind == 'root_directory':
1408
assert hasattr(ie, 'revision'), \
1409
'no revision on {%s} in {%s}' % \
1410
(file_id, rev.revision_id)
1411
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1412
new_inv_sha1 = sha_string(new_inv_xml)
1413
self.inv_weave.add_lines(rev.revision_id,
1415
new_inv_xml.splitlines(True))
1416
rev.inventory_sha1 = new_inv_sha1
1418
def _convert_revision_contents(self, rev, inv, present_parents):
1419
"""Convert all the files within a revision.
1421
Also upgrade the inventory to refer to the text revision ids."""
1422
rev_id = rev.revision_id
1423
mutter('converting texts of revision {%s}',
1425
parent_invs = map(self._load_updated_inventory, present_parents)
1428
self._convert_file_version(rev, ie, parent_invs)
1430
def _convert_file_version(self, rev, ie, parent_invs):
1431
"""Convert one version of one file.
1433
The file needs to be added into the weave if it is a merge
1434
of >=2 parents or if it's changed from its parent.
1436
if ie.kind == 'root_directory':
1438
file_id = ie.file_id
1439
rev_id = rev.revision_id
1440
w = self.text_weaves.get(file_id)
1443
self.text_weaves[file_id] = w
1444
text_changed = False
1445
previous_entries = ie.find_previous_heads(parent_invs, w)
1446
for old_revision in previous_entries:
1447
# if this fails, its a ghost ?
1448
assert old_revision in self.converted_revs
1449
self.snapshot_ie(previous_entries, ie, w, rev_id)
1451
assert getattr(ie, 'revision', None) is not None
1453
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1454
# TODO: convert this logic, which is ~= snapshot to
1455
# a call to:. This needs the path figured out. rather than a work_tree
1456
# a v4 revision_tree can be given, or something that looks enough like
1457
# one to give the file content to the entry if it needs it.
1458
# and we need something that looks like a weave store for snapshot to
1460
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1461
if len(previous_revisions) == 1:
1462
previous_ie = previous_revisions.values()[0]
1463
if ie._unchanged(previous_ie):
1464
ie.revision = previous_ie.revision
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_lines(rev_id, previous_revisions, file_lines)
1472
self.text_count += 1
1474
w.add_lines(rev_id, previous_revisions, [])
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)