1
# Copyright (C) 2005, 2006 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
from copy import deepcopy
25
from cStringIO import StringIO
26
from unittest import TestSuite
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles, TransportLock
31
from bzrlib.lockdir import LockDir
32
from bzrlib.osutils import safe_unicode
33
from bzrlib.osutils import (
40
from bzrlib.store.text import TextStore
41
from bzrlib.store.weave 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.weavefile import read_weave, write_weave
49
from bzrlib.xml4 import serializer_v4
50
from bzrlib.xml5 import serializer_v5
54
"""A .bzr control diretory.
56
BzrDir instances let you create or open any of the things that can be
57
found within .bzr - checkouts, branches and repositories.
60
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
62
a transport connected to the directory this bzr was opened from.
65
def can_convert_format(self):
66
"""Return true if this bzrdir is one whose format we can convert from."""
69
def _check_supported(self, format, allow_unsupported):
70
"""Check whether format is a supported format.
72
If allow_unsupported is True, this is a no-op.
74
if not allow_unsupported and not format.is_supported():
75
raise errors.UnsupportedFormatError(format)
77
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
78
"""Clone this bzrdir and its contents to url verbatim.
80
If urls last component does not exist, it will be created.
82
if revision_id is not None, then the clone operation may tune
83
itself to download less data.
84
:param force_new_repo: Do not use a shared repository for the target
85
even if one is available.
88
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
89
result = self._format.initialize(url)
91
local_repo = self.find_repository()
92
except errors.NoRepositoryPresent:
95
# may need to copy content in
97
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
100
result_repo = result.find_repository()
101
# fetch content this dir needs.
103
# XXX FIXME RBC 20060214 need tests for this when the basis
105
result_repo.fetch(basis_repo, revision_id=revision_id)
106
result_repo.fetch(local_repo, revision_id=revision_id)
107
except errors.NoRepositoryPresent:
108
# needed to make one anyway.
109
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
110
# 1 if there is a branch present
111
# make sure its content is available in the target repository
114
self.open_branch().clone(result, revision_id=revision_id)
115
except errors.NotBranchError:
118
self.open_workingtree().clone(result, basis=basis_tree)
119
except (errors.NoWorkingTree, errors.NotLocalUrl):
123
def _get_basis_components(self, basis):
124
"""Retrieve the basis components that are available at basis."""
126
return None, None, None
128
basis_tree = basis.open_workingtree()
129
basis_branch = basis_tree.branch
130
basis_repo = basis_branch.repository
131
except (errors.NoWorkingTree, errors.NotLocalUrl):
134
basis_branch = basis.open_branch()
135
basis_repo = basis_branch.repository
136
except errors.NotBranchError:
139
basis_repo = basis.open_repository()
140
except errors.NoRepositoryPresent:
142
return basis_repo, basis_branch, basis_tree
144
def _make_tail(self, url):
145
segments = url.split('/')
146
if segments and segments[-1] not in ('', '.'):
147
parent = '/'.join(segments[:-1])
148
t = bzrlib.transport.get_transport(parent)
150
t.mkdir(segments[-1])
151
except errors.FileExists:
156
"""Create a new BzrDir at the url 'base'.
158
This will call the current default formats initialize with base
159
as the only parameter.
161
If you need a specific format, consider creating an instance
162
of that and calling initialize().
164
segments = base.split('/')
165
if segments and segments[-1] not in ('', '.'):
166
parent = '/'.join(segments[:-1])
167
t = bzrlib.transport.get_transport(parent)
169
t.mkdir(segments[-1])
170
except errors.FileExists:
172
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
174
def create_branch(self):
175
"""Create a branch in this BzrDir.
177
The bzrdirs format will control what branch format is created.
178
For more control see BranchFormatXX.create(a_bzrdir).
180
raise NotImplementedError(self.create_branch)
183
def create_branch_and_repo(base, force_new_repo=False):
184
"""Create a new BzrDir, Branch and Repository at the url 'base'.
186
This will use the current default BzrDirFormat, and use whatever
187
repository format that that uses via bzrdir.create_branch and
188
create_repository. If a shared repository is available that is used
191
The created Branch object is returned.
193
:param base: The URL to create the branch at.
194
:param force_new_repo: If True a new repository is always created.
196
bzrdir = BzrDir.create(base)
197
bzrdir._find_or_create_repository(force_new_repo)
198
return bzrdir.create_branch()
200
def _find_or_create_repository(self, force_new_repo):
201
"""Create a new repository if needed, returning the repository."""
203
return self.create_repository()
205
return self.find_repository()
206
except errors.NoRepositoryPresent:
207
return self.create_repository()
210
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
211
"""Create a new BzrDir, Branch and Repository at the url 'base'.
213
This is a convenience function - it will use an existing repository
214
if possible, can be told explicitly whether to create a working tree or
217
This will use the current default BzrDirFormat, and use whatever
218
repository format that that uses via bzrdir.create_branch and
219
create_repository. If a shared repository is available that is used
220
preferentially. Whatever repository is used, its tree creation policy
223
The created Branch object is returned.
224
If a working tree cannot be made due to base not being a file:// url,
225
no error is raised unless force_new_tree is True, in which case no
226
data is created on disk and NotLocalUrl is raised.
228
:param base: The URL to create the branch at.
229
:param force_new_repo: If True a new repository is always created.
230
:param force_new_tree: If True or False force creation of a tree or
231
prevent such creation respectively.
234
# check for non local urls
235
t = get_transport(safe_unicode(base))
236
if not isinstance(t, LocalTransport):
237
raise errors.NotLocalUrl(base)
238
bzrdir = BzrDir.create(base)
239
repo = bzrdir._find_or_create_repository(force_new_repo)
240
result = bzrdir.create_branch()
241
if force_new_tree or (repo.make_working_trees() and
242
force_new_tree is None):
244
bzrdir.create_workingtree()
245
except errors.NotLocalUrl:
250
def create_repository(base, shared=False):
251
"""Create a new BzrDir and Repository at the url 'base'.
253
This will use the current default BzrDirFormat, and use whatever
254
repository format that that uses for bzrdirformat.create_repository.
256
;param shared: Create a shared repository rather than a standalone
258
The Repository object is returned.
260
This must be overridden as an instance method in child classes, where
261
it should take no parameters and construct whatever repository format
262
that child class desires.
264
bzrdir = BzrDir.create(base)
265
return bzrdir.create_repository()
268
def create_standalone_workingtree(base):
269
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
271
'base' must be a local path or a file:// url.
273
This will use the current default BzrDirFormat, and use whatever
274
repository format that that uses for bzrdirformat.create_workingtree,
275
create_branch and create_repository.
277
The WorkingTree object is returned.
279
t = get_transport(safe_unicode(base))
280
if not isinstance(t, LocalTransport):
281
raise errors.NotLocalUrl(base)
282
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
283
force_new_repo=True).bzrdir
284
return bzrdir.create_workingtree()
286
def create_workingtree(self, revision_id=None):
287
"""Create a working tree at this BzrDir.
289
revision_id: create it as of this revision id.
291
raise NotImplementedError(self.create_workingtree)
293
def find_repository(self):
294
"""Find the repository that should be used for a_bzrdir.
296
This does not require a branch as we use it to find the repo for
297
new branches as well as to hook existing branches up to their
301
return self.open_repository()
302
except errors.NoRepositoryPresent:
304
next_transport = self.root_transport.clone('..')
307
found_bzrdir = BzrDir.open_containing_from_transport(
309
except errors.NotBranchError:
310
raise errors.NoRepositoryPresent(self)
312
repository = found_bzrdir.open_repository()
313
except errors.NoRepositoryPresent:
314
next_transport = found_bzrdir.root_transport.clone('..')
316
if ((found_bzrdir.root_transport.base ==
317
self.root_transport.base) or repository.is_shared()):
320
raise errors.NoRepositoryPresent(self)
321
raise errors.NoRepositoryPresent(self)
323
def get_branch_transport(self, branch_format):
324
"""Get the transport for use by branch format in this BzrDir.
326
Note that bzr dirs that do not support format strings will raise
327
IncompatibleFormat if the branch format they are given has
328
a format string, and vice verca.
330
If branch_format is None, the transport is returned with no
331
checking. if it is not None, then the returned transport is
332
guaranteed to point to an existing directory ready for use.
334
raise NotImplementedError(self.get_branch_transport)
336
def get_repository_transport(self, repository_format):
337
"""Get the transport for use by repository format in this BzrDir.
339
Note that bzr dirs that do not support format strings will raise
340
IncompatibleFormat if the repository format they are given has
341
a format string, and vice verca.
343
If repository_format is None, the transport is returned with no
344
checking. if it is not None, then the returned transport is
345
guaranteed to point to an existing directory ready for use.
347
raise NotImplementedError(self.get_repository_transport)
349
def get_workingtree_transport(self, tree_format):
350
"""Get the transport for use by workingtree format in this BzrDir.
352
Note that bzr dirs that do not support format strings will raise
353
IncompatibleFormat if the workingtree format they are given has
354
a format string, and vice verca.
356
If workingtree_format is None, the transport is returned with no
357
checking. if it is not None, then the returned transport is
358
guaranteed to point to an existing directory ready for use.
360
raise NotImplementedError(self.get_workingtree_transport)
362
def __init__(self, _transport, _format):
363
"""Initialize a Bzr control dir object.
365
Only really common logic should reside here, concrete classes should be
366
made with varying behaviours.
368
:param _format: the format that is creating this BzrDir instance.
369
:param _transport: the transport this dir is based at.
371
self._format = _format
372
self.transport = _transport.clone('.bzr')
373
self.root_transport = _transport
375
def needs_format_conversion(self, format=None):
376
"""Return true if this bzrdir needs convert_format run on it.
378
For instance, if the repository format is out of date but the
379
branch and working tree are not, this should return True.
381
:param format: Optional parameter indicating a specific desired
382
format we plan to arrive at.
384
raise NotImplementedError(self.needs_format_conversion)
387
def open_unsupported(base):
388
"""Open a branch which is not supported."""
389
return BzrDir.open(base, _unsupported=True)
392
def open(base, _unsupported=False):
393
"""Open an existing bzrdir, rooted at 'base' (url)
395
_unsupported is a private parameter to the BzrDir class.
397
t = get_transport(base)
398
mutter("trying to open %r with transport %r", base, t)
399
format = BzrDirFormat.find_format(t)
400
if not _unsupported and not format.is_supported():
401
# see open_downlevel to open legacy branches.
402
raise errors.UnsupportedFormatError(
403
'sorry, format %s not supported' % format,
404
['use a different bzr version',
405
'or remove the .bzr directory'
406
' and "bzr init" again'])
407
return format.open(t, _found=True)
409
def open_branch(self, unsupported=False):
410
"""Open the branch object at this BzrDir if one is present.
412
If unsupported is True, then no longer supported branch formats can
415
TODO: static convenience version of this?
417
raise NotImplementedError(self.open_branch)
420
def open_containing(url):
421
"""Open an existing branch which contains url.
423
:param url: url to search from.
424
See open_containing_from_transport for more detail.
426
return BzrDir.open_containing_from_transport(get_transport(url))
429
def open_containing_from_transport(a_transport):
430
"""Open an existing branch which contains a_transport.base
432
This probes for a branch at a_transport, and searches upwards from there.
434
Basically we keep looking up until we find the control directory or
435
run into the root. If there isn't one, raises NotBranchError.
436
If there is one and it is either an unrecognised format or an unsupported
437
format, UnknownFormatError or UnsupportedFormatError are raised.
438
If there is one, it is returned, along with the unused portion of url.
440
# this gets the normalised url back. I.e. '.' -> the full path.
441
url = a_transport.base
444
format = BzrDirFormat.find_format(a_transport)
445
return format.open(a_transport), a_transport.relpath(url)
446
except errors.NotBranchError, e:
447
mutter('not a branch in: %r %s', a_transport.base, e)
448
new_t = a_transport.clone('..')
449
if new_t.base == a_transport.base:
450
# reached the root, whatever that may be
451
raise errors.NotBranchError(path=url)
454
def open_repository(self, _unsupported=False):
455
"""Open the repository object at this BzrDir if one is present.
457
This will not follow the Branch object pointer - its strictly a direct
458
open facility. Most client code should use open_branch().repository to
461
_unsupported is a private parameter, not part of the api.
462
TODO: static convenience version of this?
464
raise NotImplementedError(self.open_repository)
466
def open_workingtree(self, _unsupported=False):
467
"""Open the workingtree object at this BzrDir if one is present.
469
TODO: static convenience version of this?
471
raise NotImplementedError(self.open_workingtree)
473
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
474
"""Create a copy of this bzrdir prepared for use as a new line of
477
If urls last component does not exist, it will be created.
479
Attributes related to the identity of the source branch like
480
branch nickname will be cleaned, a working tree is created
481
whether one existed before or not; and a local branch is always
484
if revision_id is not None, then the clone operation may tune
485
itself to download less data.
488
result = self._format.initialize(url)
489
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
491
source_branch = self.open_branch()
492
source_repository = source_branch.repository
493
except errors.NotBranchError:
496
source_repository = self.open_repository()
497
except errors.NoRepositoryPresent:
498
# copy the entire basis one if there is one
499
# but there is no repository.
500
source_repository = basis_repo
505
result_repo = result.find_repository()
506
except errors.NoRepositoryPresent:
508
if source_repository is None and result_repo is not None:
510
elif source_repository is None and result_repo is None:
511
# no repo available, make a new one
512
result.create_repository()
513
elif source_repository is not None and result_repo is None:
514
# have soure, and want to make a new target repo
515
source_repository.clone(result,
516
revision_id=revision_id,
519
# fetch needed content into target.
521
# XXX FIXME RBC 20060214 need tests for this when the basis
523
result_repo.fetch(basis_repo, revision_id=revision_id)
524
result_repo.fetch(source_repository, revision_id=revision_id)
525
if source_branch is not None:
526
source_branch.sprout(result, revision_id=revision_id)
528
result.create_branch()
529
result.create_workingtree()
533
class BzrDirPreSplitOut(BzrDir):
534
"""A common class for the all-in-one formats."""
536
def __init__(self, _transport, _format):
537
"""See BzrDir.__init__."""
538
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
539
assert self._format._lock_class == TransportLock
540
assert self._format._lock_file_name == 'branch-lock'
541
self._control_files = LockableFiles(self.get_branch_transport(None),
542
self._format._lock_file_name,
543
self._format._lock_class)
545
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
546
"""See BzrDir.clone()."""
547
from bzrlib.workingtree import WorkingTreeFormat2
549
result = self._format.initialize(url, _cloning=True)
550
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
551
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
552
self.open_branch().clone(result, revision_id=revision_id)
554
self.open_workingtree().clone(result, basis=basis_tree)
555
except errors.NotLocalUrl:
556
# make a new one, this format always has to have one.
557
WorkingTreeFormat2().initialize(result)
560
def create_branch(self):
561
"""See BzrDir.create_branch."""
562
return self.open_branch()
564
def create_repository(self, shared=False):
565
"""See BzrDir.create_repository."""
567
raise errors.IncompatibleFormat('shared repository', self._format)
568
return self.open_repository()
570
def create_workingtree(self, revision_id=None):
571
"""See BzrDir.create_workingtree."""
572
# this looks buggy but is not -really-
573
# clone and sprout will have set the revision_id
574
# and that will have set it for us, its only
575
# specific uses of create_workingtree in isolation
576
# that can do wonky stuff here, and that only
577
# happens for creating checkouts, which cannot be
578
# done on this format anyway. So - acceptable wart.
579
result = self.open_workingtree()
580
if revision_id is not None:
581
result.set_last_revision(revision_id)
584
def get_branch_transport(self, branch_format):
585
"""See BzrDir.get_branch_transport()."""
586
if branch_format is None:
587
return self.transport
589
branch_format.get_format_string()
590
except NotImplementedError:
591
return self.transport
592
raise errors.IncompatibleFormat(branch_format, self._format)
594
def get_repository_transport(self, repository_format):
595
"""See BzrDir.get_repository_transport()."""
596
if repository_format is None:
597
return self.transport
599
repository_format.get_format_string()
600
except NotImplementedError:
601
return self.transport
602
raise errors.IncompatibleFormat(repository_format, self._format)
604
def get_workingtree_transport(self, workingtree_format):
605
"""See BzrDir.get_workingtree_transport()."""
606
if workingtree_format is None:
607
return self.transport
609
workingtree_format.get_format_string()
610
except NotImplementedError:
611
return self.transport
612
raise errors.IncompatibleFormat(workingtree_format, self._format)
614
def needs_format_conversion(self, format=None):
615
"""See BzrDir.needs_format_conversion()."""
616
# if the format is not the same as the system default,
617
# an upgrade is needed.
619
format = BzrDirFormat.get_default_format()
620
return not isinstance(self._format, format.__class__)
622
def open_branch(self, unsupported=False):
623
"""See BzrDir.open_branch."""
624
from bzrlib.branch import BzrBranchFormat4
625
format = BzrBranchFormat4()
626
self._check_supported(format, unsupported)
627
return format.open(self, _found=True)
629
def sprout(self, url, revision_id=None, basis=None):
630
"""See BzrDir.sprout()."""
631
from bzrlib.workingtree import WorkingTreeFormat2
633
result = self._format.initialize(url, _cloning=True)
634
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
636
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
637
except errors.NoRepositoryPresent:
640
self.open_branch().sprout(result, revision_id=revision_id)
641
except errors.NotBranchError:
643
# we always want a working tree
644
WorkingTreeFormat2().initialize(result)
648
class BzrDir4(BzrDirPreSplitOut):
649
"""A .bzr version 4 control object.
651
This is a deprecated format and may be removed after sept 2006.
654
def create_repository(self, shared=False):
655
"""See BzrDir.create_repository."""
656
return self._format.repository_format.initialize(self, shared)
658
def needs_format_conversion(self, format=None):
659
"""Format 4 dirs are always in need of conversion."""
662
def open_repository(self):
663
"""See BzrDir.open_repository."""
664
from bzrlib.repository import RepositoryFormat4
665
return RepositoryFormat4().open(self, _found=True)
668
class BzrDir5(BzrDirPreSplitOut):
669
"""A .bzr version 5 control object.
671
This is a deprecated format and may be removed after sept 2006.
674
def open_repository(self):
675
"""See BzrDir.open_repository."""
676
from bzrlib.repository import RepositoryFormat5
677
return RepositoryFormat5().open(self, _found=True)
679
def open_workingtree(self, _unsupported=False):
680
"""See BzrDir.create_workingtree."""
681
from bzrlib.workingtree import WorkingTreeFormat2
682
return WorkingTreeFormat2().open(self, _found=True)
685
class BzrDir6(BzrDirPreSplitOut):
686
"""A .bzr version 6 control object.
688
This is a deprecated format and may be removed after sept 2006.
691
def open_repository(self):
692
"""See BzrDir.open_repository."""
693
from bzrlib.repository import RepositoryFormat6
694
return RepositoryFormat6().open(self, _found=True)
696
def open_workingtree(self, _unsupported=False):
697
"""See BzrDir.create_workingtree."""
698
from bzrlib.workingtree import WorkingTreeFormat2
699
return WorkingTreeFormat2().open(self, _found=True)
702
class BzrDirMeta1(BzrDir):
703
"""A .bzr meta version 1 control object.
705
This is the first control object where the
706
individual aspects are really split out: there are separate repository,
707
workingtree and branch subdirectories and any subset of the three can be
708
present within a BzrDir.
711
def can_convert_format(self):
712
"""See BzrDir.can_convert_format()."""
715
def create_branch(self):
716
"""See BzrDir.create_branch."""
717
from bzrlib.branch import BranchFormat
718
return BranchFormat.get_default_format().initialize(self)
720
def create_repository(self, shared=False):
721
"""See BzrDir.create_repository."""
722
return self._format.repository_format.initialize(self, shared)
724
def create_workingtree(self, revision_id=None):
725
"""See BzrDir.create_workingtree."""
726
from bzrlib.workingtree import WorkingTreeFormat
727
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
729
def get_branch_transport(self, branch_format):
730
"""See BzrDir.get_branch_transport()."""
731
if branch_format is None:
732
return self.transport.clone('branch')
734
branch_format.get_format_string()
735
except NotImplementedError:
736
raise errors.IncompatibleFormat(branch_format, self._format)
738
self.transport.mkdir('branch')
739
except errors.FileExists:
741
return self.transport.clone('branch')
743
def get_repository_transport(self, repository_format):
744
"""See BzrDir.get_repository_transport()."""
745
if repository_format is None:
746
return self.transport.clone('repository')
748
repository_format.get_format_string()
749
except NotImplementedError:
750
raise errors.IncompatibleFormat(repository_format, self._format)
752
self.transport.mkdir('repository')
753
except errors.FileExists:
755
return self.transport.clone('repository')
757
def get_workingtree_transport(self, workingtree_format):
758
"""See BzrDir.get_workingtree_transport()."""
759
if workingtree_format is None:
760
return self.transport.clone('checkout')
762
workingtree_format.get_format_string()
763
except NotImplementedError:
764
raise errors.IncompatibleFormat(workingtree_format, self._format)
766
self.transport.mkdir('checkout')
767
except errors.FileExists:
769
return self.transport.clone('checkout')
771
def needs_format_conversion(self, format=None):
772
"""See BzrDir.needs_format_conversion()."""
774
format = BzrDirFormat.get_default_format()
775
if not isinstance(self._format, format.__class__):
776
# it is not a meta dir format, conversion is needed.
778
# we might want to push this down to the repository?
780
if not isinstance(self.open_repository()._format,
781
format.repository_format.__class__):
782
# the repository needs an upgrade.
784
except errors.NoRepositoryPresent:
786
# currently there are no other possible conversions for meta1 formats.
789
def open_branch(self, unsupported=False):
790
"""See BzrDir.open_branch."""
791
from bzrlib.branch import BranchFormat
792
format = BranchFormat.find_format(self)
793
self._check_supported(format, unsupported)
794
return format.open(self, _found=True)
796
def open_repository(self, unsupported=False):
797
"""See BzrDir.open_repository."""
798
from bzrlib.repository import RepositoryFormat
799
format = RepositoryFormat.find_format(self)
800
self._check_supported(format, unsupported)
801
return format.open(self, _found=True)
803
def open_workingtree(self, unsupported=False):
804
"""See BzrDir.open_workingtree."""
805
from bzrlib.workingtree import WorkingTreeFormat
806
format = WorkingTreeFormat.find_format(self)
807
self._check_supported(format, unsupported)
808
return format.open(self, _found=True)
811
class BzrDirFormat(object):
812
"""An encapsulation of the initialization and open routines for a format.
814
Formats provide three things:
815
* An initialization routine,
819
Formats are placed in an dict by their format string for reference
820
during bzrdir opening. These should be subclasses of BzrDirFormat
823
Once a format is deprecated, just deprecate the initialize and open
824
methods on the format class. Do not deprecate the object, as the
825
object will be created every system load.
828
_default_format = None
829
"""The default format used for new .bzr dirs."""
832
"""The known formats."""
834
_lock_file_name = 'branch-lock'
836
# _lock_class must be set in subclasses to the lock type, typ.
837
# TransportLock or LockDir
840
def find_format(klass, transport):
841
"""Return the format registered for URL."""
843
format_string = transport.get(".bzr/branch-format").read()
844
return klass._formats[format_string]
845
except errors.NoSuchFile:
846
raise errors.NotBranchError(path=transport.base)
848
raise errors.UnknownFormatError(format_string)
851
def get_default_format(klass):
852
"""Return the current default format."""
853
return klass._default_format
855
def get_format_string(self):
856
"""Return the ASCII format string that identifies this format."""
857
raise NotImplementedError(self.get_format_string)
859
def get_converter(self, format=None):
860
"""Return the converter to use to convert bzrdirs needing converts.
862
This returns a bzrlib.bzrdir.Converter object.
864
This should return the best upgrader to step this format towards the
865
current default format. In the case of plugins we can/shouold provide
866
some means for them to extend the range of returnable converters.
868
:param format: Optional format to override the default foramt of the
871
raise NotImplementedError(self.get_converter)
873
def initialize(self, url):
874
"""Create a bzr control dir at this url and return an opened copy."""
875
# Since we don't have a .bzr directory, inherit the
876
# mode from the root directory
877
t = get_transport(url)
878
temp_control = LockableFiles(t, '', TransportLock)
879
temp_control._transport.mkdir('.bzr',
880
# FIXME: RBC 20060121 dont peek under
882
mode=temp_control._dir_mode)
883
file_mode = temp_control._file_mode
885
mutter('created control directory in ' + t.base)
886
control = t.clone('.bzr')
887
utf8_files = [('README',
888
"This is a Bazaar-NG control directory.\n"
889
"Do not change any files in this directory.\n"),
890
('branch-format', self.get_format_string()),
892
# NB: no need to escape relative paths that are url safe.
893
control_files = LockableFiles(control, self._lock_file_name, self._lock_class)
894
control_files.create_lock()
895
control_files.lock_write()
897
for file, content in utf8_files:
898
control_files.put_utf8(file, content)
900
control_files.unlock()
901
return self.open(t, _found=True)
903
def is_supported(self):
904
"""Is this format supported?
906
Supported formats must be initializable and openable.
907
Unsupported formats may not support initialization or committing or
908
some other features depending on the reason for not being supported.
912
def open(self, transport, _found=False):
913
"""Return an instance of this format for the dir transport points at.
915
_found is a private parameter, do not use it.
918
assert isinstance(BzrDirFormat.find_format(transport),
920
return self._open(transport)
922
def _open(self, transport):
923
"""Template method helper for opening BzrDirectories.
925
This performs the actual open and any additional logic or parameter
928
raise NotImplementedError(self._open)
931
def register_format(klass, format):
932
klass._formats[format.get_format_string()] = format
935
def set_default_format(klass, format):
936
klass._default_format = format
939
return self.get_format_string()[:-1]
942
def unregister_format(klass, format):
943
assert klass._formats[format.get_format_string()] is format
944
del klass._formats[format.get_format_string()]
947
class BzrDirFormat4(BzrDirFormat):
950
This format is a combined format for working tree, branch and repository.
952
- Format 1 working trees [always]
953
- Format 4 branches [always]
954
- Format 4 repositories [always]
956
This format is deprecated: it indexes texts using a text it which is
957
removed in format 5; write support for this format has been removed.
960
_lock_class = TransportLock
962
def get_format_string(self):
963
"""See BzrDirFormat.get_format_string()."""
964
return "Bazaar-NG branch, format 0.0.4\n"
966
def get_converter(self, format=None):
967
"""See BzrDirFormat.get_converter()."""
968
# there is one and only one upgrade path here.
969
return ConvertBzrDir4To5()
971
def initialize(self, url):
972
"""Format 4 branches cannot be created."""
973
raise errors.UninitializableFormat(self)
975
def is_supported(self):
976
"""Format 4 is not supported.
978
It is not supported because the model changed from 4 to 5 and the
979
conversion logic is expensive - so doing it on the fly was not
984
def _open(self, transport):
985
"""See BzrDirFormat._open."""
986
return BzrDir4(transport, self)
988
def __return_repository_format(self):
989
"""Circular import protection."""
990
from bzrlib.repository import RepositoryFormat4
991
return RepositoryFormat4(self)
992
repository_format = property(__return_repository_format)
995
class BzrDirFormat5(BzrDirFormat):
996
"""Bzr control format 5.
998
This format is a combined format for working tree, branch and repository.
1000
- Format 2 working trees [always]
1001
- Format 4 branches [always]
1002
- Format 5 repositories [always]
1003
Unhashed stores in the repository.
1006
_lock_class = TransportLock
1008
def get_format_string(self):
1009
"""See BzrDirFormat.get_format_string()."""
1010
return "Bazaar-NG branch, format 5\n"
1012
def get_converter(self, format=None):
1013
"""See BzrDirFormat.get_converter()."""
1014
# there is one and only one upgrade path here.
1015
return ConvertBzrDir5To6()
1017
def initialize(self, url, _cloning=False):
1018
"""Format 5 dirs always have working tree, branch and repository.
1020
Except when they are being cloned.
1022
from bzrlib.branch import BzrBranchFormat4
1023
from bzrlib.repository import RepositoryFormat5
1024
from bzrlib.workingtree import WorkingTreeFormat2
1025
result = super(BzrDirFormat5, self).initialize(url)
1026
RepositoryFormat5().initialize(result, _internal=True)
1028
BzrBranchFormat4().initialize(result)
1029
WorkingTreeFormat2().initialize(result)
1032
def _open(self, transport):
1033
"""See BzrDirFormat._open."""
1034
return BzrDir5(transport, self)
1036
def __return_repository_format(self):
1037
"""Circular import protection."""
1038
from bzrlib.repository import RepositoryFormat5
1039
return RepositoryFormat5(self)
1040
repository_format = property(__return_repository_format)
1043
class BzrDirFormat6(BzrDirFormat):
1044
"""Bzr control format 6.
1046
This format is a combined format for working tree, branch and repository.
1048
- Format 2 working trees [always]
1049
- Format 4 branches [always]
1050
- Format 6 repositories [always]
1053
_lock_class = TransportLock
1055
def get_format_string(self):
1056
"""See BzrDirFormat.get_format_string()."""
1057
return "Bazaar-NG branch, format 6\n"
1059
def get_converter(self, format=None):
1060
"""See BzrDirFormat.get_converter()."""
1061
# there is one and only one upgrade path here.
1062
return ConvertBzrDir6ToMeta()
1064
def initialize(self, url, _cloning=False):
1065
"""Format 6 dirs always have working tree, branch and repository.
1067
Except when they are being cloned.
1069
from bzrlib.branch import BzrBranchFormat4
1070
from bzrlib.repository import RepositoryFormat6
1071
from bzrlib.workingtree import WorkingTreeFormat2
1072
result = super(BzrDirFormat6, self).initialize(url)
1073
RepositoryFormat6().initialize(result, _internal=True)
1075
BzrBranchFormat4().initialize(result)
1077
WorkingTreeFormat2().initialize(result)
1078
except errors.NotLocalUrl:
1079
# emulate pre-check behaviour for working tree and silently
1084
def _open(self, transport):
1085
"""See BzrDirFormat._open."""
1086
return BzrDir6(transport, self)
1088
def __return_repository_format(self):
1089
"""Circular import protection."""
1090
from bzrlib.repository import RepositoryFormat6
1091
return RepositoryFormat6(self)
1092
repository_format = property(__return_repository_format)
1095
class BzrDirMetaFormat1(BzrDirFormat):
1096
"""Bzr meta control format 1
1098
This is the first format with split out working tree, branch and repository
1101
- Format 3 working trees [optional]
1102
- Format 5 branches [optional]
1103
- Format 7 repositories [optional]
1106
_lock_class = LockDir
1108
def get_converter(self, format=None):
1109
"""See BzrDirFormat.get_converter()."""
1111
format = BzrDirFormat.get_default_format()
1112
if not isinstance(self, format.__class__):
1113
# converting away from metadir is not implemented
1114
raise NotImplementedError(self.get_converter)
1115
return ConvertMetaToMeta(format)
1117
def get_format_string(self):
1118
"""See BzrDirFormat.get_format_string()."""
1119
return "Bazaar-NG meta directory, format 1\n"
1121
def _open(self, transport):
1122
"""See BzrDirFormat._open."""
1123
return BzrDirMeta1(transport, self)
1125
def __return_repository_format(self):
1126
"""Circular import protection."""
1127
if getattr(self, '_repository_format', None):
1128
return self._repository_format
1129
from bzrlib.repository import RepositoryFormat
1130
return RepositoryFormat.get_default_format()
1132
def __set_repository_format(self, value):
1133
"""Allow changint the repository format for metadir formats."""
1134
self._repository_format = value
1135
repository_format = property(__return_repository_format, __set_repository_format)
1138
BzrDirFormat.register_format(BzrDirFormat4())
1139
BzrDirFormat.register_format(BzrDirFormat5())
1140
BzrDirFormat.register_format(BzrDirMetaFormat1())
1141
__default_format = BzrDirFormat6()
1142
BzrDirFormat.register_format(__default_format)
1143
BzrDirFormat.set_default_format(__default_format)
1146
class BzrDirTestProviderAdapter(object):
1147
"""A tool to generate a suite testing multiple bzrdir formats at once.
1149
This is done by copying the test once for each transport and injecting
1150
the transport_server, transport_readonly_server, and bzrdir_format
1151
classes into each copy. Each copy is also given a new id() to make it
1155
def __init__(self, transport_server, transport_readonly_server, formats):
1156
self._transport_server = transport_server
1157
self._transport_readonly_server = transport_readonly_server
1158
self._formats = formats
1160
def adapt(self, test):
1161
result = TestSuite()
1162
for format in self._formats:
1163
new_test = deepcopy(test)
1164
new_test.transport_server = self._transport_server
1165
new_test.transport_readonly_server = self._transport_readonly_server
1166
new_test.bzrdir_format = format
1167
def make_new_test_id():
1168
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1169
return lambda: new_id
1170
new_test.id = make_new_test_id()
1171
result.addTest(new_test)
1175
class ScratchDir(BzrDir6):
1176
"""Special test class: a bzrdir that cleans up itself..
1178
>>> d = ScratchDir()
1179
>>> base = d.transport.base
1182
>>> b.transport.__del__()
1187
def __init__(self, files=[], dirs=[], transport=None):
1188
"""Make a test branch.
1190
This creates a temporary directory and runs init-tree in it.
1192
If any files are listed, they are created in the working copy.
1194
if transport is None:
1195
transport = bzrlib.transport.local.ScratchTransport()
1196
# local import for scope restriction
1197
BzrDirFormat6().initialize(transport.base)
1198
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1199
self.create_repository()
1200
self.create_branch()
1201
self.create_workingtree()
1203
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1205
# BzrBranch creates a clone to .bzr and then forgets about the
1206
# original transport. A ScratchTransport() deletes itself and
1207
# everything underneath it when it goes away, so we need to
1208
# grab a local copy to prevent that from happening
1209
self._transport = transport
1212
self._transport.mkdir(d)
1215
self._transport.put(f, 'content of %s' % f)
1219
>>> orig = ScratchDir(files=["file1", "file2"])
1220
>>> os.listdir(orig.base)
1221
[u'.bzr', u'file1', u'file2']
1222
>>> clone = orig.clone()
1223
>>> if os.name != 'nt':
1224
... os.path.samefile(orig.base, clone.base)
1226
... orig.base == clone.base
1229
>>> os.listdir(clone.base)
1230
[u'.bzr', u'file1', u'file2']
1232
from shutil import copytree
1233
from bzrlib.osutils import mkdtemp
1236
copytree(self.base, base, symlinks=True)
1238
transport=bzrlib.transport.local.ScratchTransport(base))
1241
class Converter(object):
1242
"""Converts a disk format object from one format to another."""
1244
def convert(self, to_convert, pb):
1245
"""Perform the conversion of to_convert, giving feedback via pb.
1247
:param to_convert: The disk object to convert.
1248
:param pb: a progress bar to use for progress information.
1251
def step(self, message):
1252
"""Update the pb by a step."""
1254
self.pb.update(message, self.count, self.total)
1257
class ConvertBzrDir4To5(Converter):
1258
"""Converts format 4 bzr dirs to format 5."""
1261
super(ConvertBzrDir4To5, self).__init__()
1262
self.converted_revs = set()
1263
self.absent_revisions = set()
1267
def convert(self, to_convert, pb):
1268
"""See Converter.convert()."""
1269
self.bzrdir = to_convert
1271
self.pb.note('starting upgrade from format 4 to 5')
1272
if isinstance(self.bzrdir.transport, LocalTransport):
1273
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1274
self._convert_to_weaves()
1275
return BzrDir.open(self.bzrdir.root_transport.base)
1277
def _convert_to_weaves(self):
1278
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1281
stat = self.bzrdir.transport.stat('weaves')
1282
if not S_ISDIR(stat.st_mode):
1283
self.bzrdir.transport.delete('weaves')
1284
self.bzrdir.transport.mkdir('weaves')
1285
except errors.NoSuchFile:
1286
self.bzrdir.transport.mkdir('weaves')
1287
self.inv_weave = Weave('inventory')
1288
# holds in-memory weaves for all files
1289
self.text_weaves = {}
1290
self.bzrdir.transport.delete('branch-format')
1291
self.branch = self.bzrdir.open_branch()
1292
self._convert_working_inv()
1293
rev_history = self.branch.revision_history()
1294
# to_read is a stack holding the revisions we still need to process;
1295
# appending to it adds new highest-priority revisions
1296
self.known_revisions = set(rev_history)
1297
self.to_read = rev_history[-1:]
1299
rev_id = self.to_read.pop()
1300
if (rev_id not in self.revisions
1301
and rev_id not in self.absent_revisions):
1302
self._load_one_rev(rev_id)
1304
to_import = self._make_order()
1305
for i, rev_id in enumerate(to_import):
1306
self.pb.update('converting revision', i, len(to_import))
1307
self._convert_one_rev(rev_id)
1309
self._write_all_weaves()
1310
self._write_all_revs()
1311
self.pb.note('upgraded to weaves:')
1312
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1313
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1314
self.pb.note(' %6d texts', self.text_count)
1315
self._cleanup_spare_files_after_format4()
1316
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1318
def _cleanup_spare_files_after_format4(self):
1319
# FIXME working tree upgrade foo.
1320
for n in 'merged-patches', 'pending-merged-patches':
1322
## assert os.path.getsize(p) == 0
1323
self.bzrdir.transport.delete(n)
1324
except errors.NoSuchFile:
1326
self.bzrdir.transport.delete_tree('inventory-store')
1327
self.bzrdir.transport.delete_tree('text-store')
1329
def _convert_working_inv(self):
1330
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1331
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1332
# FIXME inventory is a working tree change.
1333
self.branch.control_files.put('inventory', new_inv_xml)
1335
def _write_all_weaves(self):
1336
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1337
weave_transport = self.bzrdir.transport.clone('weaves')
1338
weaves = WeaveStore(weave_transport, prefixed=False)
1339
transaction = PassThroughTransaction()
1341
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1344
for file_id, file_weave in self.text_weaves.items():
1345
self.pb.update('writing weave', i, len(self.text_weaves))
1346
weaves.put_weave(file_id, file_weave, transaction)
1351
def _write_all_revs(self):
1352
"""Write all revisions out in new form."""
1353
self.bzrdir.transport.delete_tree('revision-store')
1354
self.bzrdir.transport.mkdir('revision-store')
1355
revision_transport = self.bzrdir.transport.clone('revision-store')
1357
revision_store = TextStore(revision_transport,
1361
for i, rev_id in enumerate(self.converted_revs):
1362
self.pb.update('write revision', i, len(self.converted_revs))
1363
rev_tmp = StringIO()
1364
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1366
revision_store.add(rev_tmp, rev_id)
1370
def _load_one_rev(self, rev_id):
1371
"""Load a revision object into memory.
1373
Any parents not either loaded or abandoned get queued to be
1375
self.pb.update('loading revision',
1376
len(self.revisions),
1377
len(self.known_revisions))
1378
if not self.branch.repository.revision_store.has_id(rev_id):
1380
self.pb.note('revision {%s} not present in branch; '
1381
'will be converted as a ghost',
1383
self.absent_revisions.add(rev_id)
1385
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1386
rev = serializer_v4.read_revision_from_string(rev_xml)
1387
for parent_id in rev.parent_ids:
1388
self.known_revisions.add(parent_id)
1389
self.to_read.append(parent_id)
1390
self.revisions[rev_id] = rev
1392
def _load_old_inventory(self, rev_id):
1393
assert rev_id not in self.converted_revs
1394
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1395
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1396
rev = self.revisions[rev_id]
1397
if rev.inventory_sha1:
1398
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1399
'inventory sha mismatch for {%s}' % rev_id
1402
def _load_updated_inventory(self, rev_id):
1403
assert rev_id in self.converted_revs
1404
inv_xml = self.inv_weave.get_text(rev_id)
1405
inv = serializer_v5.read_inventory_from_string(inv_xml)
1408
def _convert_one_rev(self, rev_id):
1409
"""Convert revision and all referenced objects to new format."""
1410
rev = self.revisions[rev_id]
1411
inv = self._load_old_inventory(rev_id)
1412
present_parents = [p for p in rev.parent_ids
1413
if p not in self.absent_revisions]
1414
self._convert_revision_contents(rev, inv, present_parents)
1415
self._store_new_weave(rev, inv, present_parents)
1416
self.converted_revs.add(rev_id)
1418
def _store_new_weave(self, rev, inv, present_parents):
1419
# the XML is now updated with text versions
1423
if ie.kind == 'root_directory':
1425
assert hasattr(ie, 'revision'), \
1426
'no revision on {%s} in {%s}' % \
1427
(file_id, rev.revision_id)
1428
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1429
new_inv_sha1 = sha_string(new_inv_xml)
1430
self.inv_weave.add(rev.revision_id,
1432
new_inv_xml.splitlines(True),
1434
rev.inventory_sha1 = new_inv_sha1
1436
def _convert_revision_contents(self, rev, inv, present_parents):
1437
"""Convert all the files within a revision.
1439
Also upgrade the inventory to refer to the text revision ids."""
1440
rev_id = rev.revision_id
1441
mutter('converting texts of revision {%s}',
1443
parent_invs = map(self._load_updated_inventory, present_parents)
1446
self._convert_file_version(rev, ie, parent_invs)
1448
def _convert_file_version(self, rev, ie, parent_invs):
1449
"""Convert one version of one file.
1451
The file needs to be added into the weave if it is a merge
1452
of >=2 parents or if it's changed from its parent.
1454
if ie.kind == 'root_directory':
1456
file_id = ie.file_id
1457
rev_id = rev.revision_id
1458
w = self.text_weaves.get(file_id)
1461
self.text_weaves[file_id] = w
1462
text_changed = False
1463
previous_entries = ie.find_previous_heads(parent_invs, w)
1464
for old_revision in previous_entries:
1465
# if this fails, its a ghost ?
1466
assert old_revision in self.converted_revs
1467
self.snapshot_ie(previous_entries, ie, w, rev_id)
1469
assert getattr(ie, 'revision', None) is not None
1471
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1472
# TODO: convert this logic, which is ~= snapshot to
1473
# a call to:. This needs the path figured out. rather than a work_tree
1474
# a v4 revision_tree can be given, or something that looks enough like
1475
# one to give the file content to the entry if it needs it.
1476
# and we need something that looks like a weave store for snapshot to
1478
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1479
if len(previous_revisions) == 1:
1480
previous_ie = previous_revisions.values()[0]
1481
if ie._unchanged(previous_ie):
1482
ie.revision = previous_ie.revision
1484
parent_indexes = map(w.lookup, previous_revisions)
1486
text = self.branch.repository.text_store.get(ie.text_id)
1487
file_lines = text.readlines()
1488
assert sha_strings(file_lines) == ie.text_sha1
1489
assert sum(map(len, file_lines)) == ie.text_size
1490
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1491
self.text_count += 1
1493
w.add(rev_id, parent_indexes, [], None)
1494
ie.revision = rev_id
1496
def _make_order(self):
1497
"""Return a suitable order for importing revisions.
1499
The order must be such that an revision is imported after all
1500
its (present) parents.
1502
todo = set(self.revisions.keys())
1503
done = self.absent_revisions.copy()
1506
# scan through looking for a revision whose parents
1508
for rev_id in sorted(list(todo)):
1509
rev = self.revisions[rev_id]
1510
parent_ids = set(rev.parent_ids)
1511
if parent_ids.issubset(done):
1512
# can take this one now
1513
order.append(rev_id)
1519
class ConvertBzrDir5To6(Converter):
1520
"""Converts format 5 bzr dirs to format 6."""
1522
def convert(self, to_convert, pb):
1523
"""See Converter.convert()."""
1524
self.bzrdir = to_convert
1526
self.pb.note('starting upgrade from format 5 to 6')
1527
self._convert_to_prefixed()
1528
return BzrDir.open(self.bzrdir.root_transport.base)
1530
def _convert_to_prefixed(self):
1531
from bzrlib.store import hash_prefix
1532
self.bzrdir.transport.delete('branch-format')
1533
for store_name in ["weaves", "revision-store"]:
1534
self.pb.note("adding prefixes to %s" % store_name)
1535
store_transport = self.bzrdir.transport.clone(store_name)
1536
for filename in store_transport.list_dir('.'):
1537
if (filename.endswith(".weave") or
1538
filename.endswith(".gz") or
1539
filename.endswith(".sig")):
1540
file_id = os.path.splitext(filename)[0]
1543
prefix_dir = hash_prefix(file_id)
1544
# FIXME keep track of the dirs made RBC 20060121
1546
store_transport.move(filename, prefix_dir + '/' + filename)
1547
except errors.NoSuchFile: # catches missing dirs strangely enough
1548
store_transport.mkdir(prefix_dir)
1549
store_transport.move(filename, prefix_dir + '/' + filename)
1550
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1553
class ConvertBzrDir6ToMeta(Converter):
1554
"""Converts format 6 bzr dirs to metadirs."""
1556
def convert(self, to_convert, pb):
1557
"""See Converter.convert()."""
1558
self.bzrdir = to_convert
1561
self.total = 20 # the steps we know about
1562
self.garbage_inventories = []
1564
self.pb.note('starting upgrade from format 6 to metadir')
1565
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1566
# its faster to move specific files around than to open and use the apis...
1567
# first off, nuke ancestry.weave, it was never used.
1569
self.step('Removing ancestry.weave')
1570
self.bzrdir.transport.delete('ancestry.weave')
1571
except errors.NoSuchFile:
1573
# find out whats there
1574
self.step('Finding branch files')
1575
last_revision = self.bzrdir.open_workingtree().last_revision()
1576
bzrcontents = self.bzrdir.transport.list_dir('.')
1577
for name in bzrcontents:
1578
if name.startswith('basis-inventory.'):
1579
self.garbage_inventories.append(name)
1580
# create new directories for repository, working tree and branch
1581
dir_mode = self.bzrdir._control_files._dir_mode
1582
self.file_mode = self.bzrdir._control_files._file_mode
1583
repository_names = [('inventory.weave', True),
1584
('revision-store', True),
1586
self.step('Upgrading repository ')
1587
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1588
self.make_lock('repository')
1589
# we hard code the formats here because we are converting into
1590
# the meta format. The meta format upgrader can take this to a
1591
# future format within each component.
1592
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1593
for entry in repository_names:
1594
self.move_entry('repository', entry)
1596
self.step('Upgrading branch ')
1597
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1598
self.make_lock('branch')
1599
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1600
branch_files = [('revision-history', True),
1601
('branch-name', True),
1603
for entry in branch_files:
1604
self.move_entry('branch', entry)
1606
self.step('Upgrading working tree')
1607
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1608
self.make_lock('checkout')
1609
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1610
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1611
checkout_files = [('pending-merges', True),
1612
('inventory', True),
1613
('stat-cache', False)]
1614
for entry in checkout_files:
1615
self.move_entry('checkout', entry)
1616
if last_revision is not None:
1617
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1619
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1620
return BzrDir.open(self.bzrdir.root_transport.base)
1622
def make_lock(self, name):
1623
"""Make a lock for the new control dir name."""
1624
self.step('Make %s lock' % name)
1625
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1627
def move_entry(self, new_dir, entry):
1628
"""Move then entry name into new_dir."""
1630
mandatory = entry[1]
1631
self.step('Moving %s' % name)
1633
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1634
except errors.NoSuchFile:
1638
def put_format(self, dirname, format):
1639
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1642
class ConvertMetaToMeta(Converter):
1643
"""Converts the components of metadirs."""
1645
def __init__(self, target_format):
1646
"""Create a metadir to metadir converter.
1648
:param target_format: The final metadir format that is desired.
1650
self.target_format = target_format
1652
def convert(self, to_convert, pb):
1653
"""See Converter.convert()."""
1654
self.bzrdir = to_convert
1658
self.step('checking repository format')
1660
repo = self.bzrdir.open_repository()
1661
except errors.NoRepositoryPresent:
1664
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1665
from bzrlib.repository import CopyConverter
1666
self.pb.note('starting repository conversion')
1667
converter = CopyConverter(self.target_format.repository_format)
1668
converter.convert(repo, pb)