1
# Copyright (C) 2005, 2006 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
from copy import deepcopy
25
from cStringIO import StringIO
26
from unittest import TestSuite
29
import bzrlib.errors as errors
30
from bzrlib.lockable_files import LockableFiles, TransportLock
31
from bzrlib.osutils import safe_unicode
32
from bzrlib.osutils import (
39
from bzrlib.store.revision.text import TextRevisionStore
40
from bzrlib.store.text import TextStore
41
from bzrlib.store.versioned import WeaveStore
42
from bzrlib.symbol_versioning import *
43
from bzrlib.trace import mutter
44
from bzrlib.transactions import WriteTransaction
45
from bzrlib.transport import get_transport
46
from bzrlib.transport.local import LocalTransport
47
from bzrlib.weave import Weave
48
from bzrlib.xml4 import serializer_v4
49
from bzrlib.xml5 import serializer_v5
53
"""A .bzr control diretory.
55
BzrDir instances let you create or open any of the things that can be
56
found within .bzr - checkouts, branches and repositories.
59
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
61
a transport connected to the directory this bzr was opened from.
64
def can_convert_format(self):
65
"""Return true if this bzrdir is one whose format we can convert from."""
69
def _check_supported(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
# see open_downlevel to open legacy branches.
76
raise errors.UnsupportedFormatError(
77
'sorry, format %s not supported' % format,
78
['use a different bzr version',
79
'or remove the .bzr directory'
80
' and "bzr init" again'])
82
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
83
"""Clone this bzrdir and its contents to url verbatim.
85
If urls last component does not exist, it will be created.
87
if revision_id is not None, then the clone operation may tune
88
itself to download less data.
89
:param force_new_repo: Do not use a shared repository for the target
90
even if one is available.
93
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
94
result = self._format.initialize(url)
96
local_repo = self.find_repository()
97
except errors.NoRepositoryPresent:
100
# may need to copy content in
102
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
105
result_repo = result.find_repository()
106
# fetch content this dir needs.
108
# XXX FIXME RBC 20060214 need tests for this when the basis
110
result_repo.fetch(basis_repo, revision_id=revision_id)
111
result_repo.fetch(local_repo, revision_id=revision_id)
112
except errors.NoRepositoryPresent:
113
# needed to make one anyway.
114
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
115
# 1 if there is a branch present
116
# make sure its content is available in the target repository
119
self.open_branch().clone(result, revision_id=revision_id)
120
except errors.NotBranchError:
123
self.open_workingtree().clone(result, basis=basis_tree)
124
except (errors.NoWorkingTree, errors.NotLocalUrl):
128
def _get_basis_components(self, basis):
129
"""Retrieve the basis components that are available at basis."""
131
return None, None, None
133
basis_tree = basis.open_workingtree()
134
basis_branch = basis_tree.branch
135
basis_repo = basis_branch.repository
136
except (errors.NoWorkingTree, errors.NotLocalUrl):
139
basis_branch = basis.open_branch()
140
basis_repo = basis_branch.repository
141
except errors.NotBranchError:
144
basis_repo = basis.open_repository()
145
except errors.NoRepositoryPresent:
147
return basis_repo, basis_branch, basis_tree
149
def _make_tail(self, url):
150
segments = url.split('/')
151
if segments and segments[-1] not in ('', '.'):
152
parent = '/'.join(segments[:-1])
153
t = bzrlib.transport.get_transport(parent)
155
t.mkdir(segments[-1])
156
except errors.FileExists:
161
"""Create a new BzrDir at the url 'base'.
163
This will call the current default formats initialize with base
164
as the only parameter.
166
If you need a specific format, consider creating an instance
167
of that and calling initialize().
169
segments = base.split('/')
170
if segments and segments[-1] not in ('', '.'):
171
parent = '/'.join(segments[:-1])
172
t = bzrlib.transport.get_transport(parent)
174
t.mkdir(segments[-1])
175
except errors.FileExists:
177
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
179
def create_branch(self):
180
"""Create a branch in this BzrDir.
182
The bzrdirs format will control what branch format is created.
183
For more control see BranchFormatXX.create(a_bzrdir).
185
raise NotImplementedError(self.create_branch)
188
def create_branch_and_repo(base, force_new_repo=False):
189
"""Create a new BzrDir, Branch and Repository at the url 'base'.
191
This will use the current default BzrDirFormat, and use whatever
192
repository format that that uses via bzrdir.create_branch and
193
create_repository. If a shared repository is available that is used
196
The created Branch object is returned.
198
:param base: The URL to create the branch at.
199
:param force_new_repo: If True a new repository is always created.
201
bzrdir = BzrDir.create(base)
202
bzrdir._find_or_create_repository(force_new_repo)
203
return bzrdir.create_branch()
205
def _find_or_create_repository(self, force_new_repo):
206
"""Create a new repository if needed, returning the repository."""
208
return self.create_repository()
210
return self.find_repository()
211
except errors.NoRepositoryPresent:
212
return self.create_repository()
215
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
216
"""Create a new BzrDir, Branch and Repository at the url 'base'.
218
This is a convenience function - it will use an existing repository
219
if possible, can be told explicitly whether to create a working tree or
222
This will use the current default BzrDirFormat, and use whatever
223
repository format that that uses via bzrdir.create_branch and
224
create_repository. If a shared repository is available that is used
225
preferentially. Whatever repository is used, its tree creation policy
228
The created Branch object is returned.
229
If a working tree cannot be made due to base not being a file:// url,
230
no error is raised unless force_new_tree is True, in which case no
231
data is created on disk and NotLocalUrl is raised.
233
:param base: The URL to create the branch at.
234
:param force_new_repo: If True a new repository is always created.
235
:param force_new_tree: If True or False force creation of a tree or
236
prevent such creation respectively.
239
# check for non local urls
240
t = get_transport(safe_unicode(base))
241
if not isinstance(t, LocalTransport):
242
raise errors.NotLocalUrl(base)
243
bzrdir = BzrDir.create(base)
244
repo = bzrdir._find_or_create_repository(force_new_repo)
245
result = bzrdir.create_branch()
246
if force_new_tree or (repo.make_working_trees() and
247
force_new_tree is None):
249
bzrdir.create_workingtree()
250
except errors.NotLocalUrl:
255
def create_repository(base, shared=False):
256
"""Create a new BzrDir and Repository at the url 'base'.
258
This will use the current default BzrDirFormat, and use whatever
259
repository format that that uses for bzrdirformat.create_repository.
261
;param shared: Create a shared repository rather than a standalone
263
The Repository object is returned.
265
This must be overridden as an instance method in child classes, where
266
it should take no parameters and construct whatever repository format
267
that child class desires.
269
bzrdir = BzrDir.create(base)
270
return bzrdir.create_repository()
273
def create_standalone_workingtree(base):
274
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
276
'base' must be a local path or a file:// url.
278
This will use the current default BzrDirFormat, and use whatever
279
repository format that that uses for bzrdirformat.create_workingtree,
280
create_branch and create_repository.
282
The WorkingTree object is returned.
284
t = get_transport(safe_unicode(base))
285
if not isinstance(t, LocalTransport):
286
raise errors.NotLocalUrl(base)
287
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
288
force_new_repo=True).bzrdir
289
return bzrdir.create_workingtree()
291
def create_workingtree(self, revision_id=None):
292
"""Create a working tree at this BzrDir.
294
revision_id: create it as of this revision id.
296
raise NotImplementedError(self.create_workingtree)
298
def find_repository(self):
299
"""Find the repository that should be used for a_bzrdir.
301
This does not require a branch as we use it to find the repo for
302
new branches as well as to hook existing branches up to their
306
return self.open_repository()
307
except errors.NoRepositoryPresent:
309
next_transport = self.root_transport.clone('..')
312
found_bzrdir = BzrDir.open_containing_from_transport(
314
except errors.NotBranchError:
315
raise errors.NoRepositoryPresent(self)
317
repository = found_bzrdir.open_repository()
318
except errors.NoRepositoryPresent:
319
next_transport = found_bzrdir.root_transport.clone('..')
321
if ((found_bzrdir.root_transport.base ==
322
self.root_transport.base) or repository.is_shared()):
325
raise errors.NoRepositoryPresent(self)
326
raise errors.NoRepositoryPresent(self)
328
def get_branch_transport(self, branch_format):
329
"""Get the transport for use by branch format in this BzrDir.
331
Note that bzr dirs that do not support format strings will raise
332
IncompatibleFormat if the branch format they are given has
333
a format string, and vice verca.
335
If branch_format is None, the transport is returned with no
336
checking. if it is not None, then the returned transport is
337
guaranteed to point to an existing directory ready for use.
339
raise NotImplementedError(self.get_branch_transport)
341
def get_repository_transport(self, repository_format):
342
"""Get the transport for use by repository format in this BzrDir.
344
Note that bzr dirs that do not support format strings will raise
345
IncompatibleFormat if the repository format they are given has
346
a format string, and vice verca.
348
If repository_format is None, the transport is returned with no
349
checking. if it is not None, then the returned transport is
350
guaranteed to point to an existing directory ready for use.
352
raise NotImplementedError(self.get_repository_transport)
354
def get_workingtree_transport(self, tree_format):
355
"""Get the transport for use by workingtree format in this BzrDir.
357
Note that bzr dirs that do not support format strings will raise
358
IncompatibleFormat if the workingtree format they are given has
359
a format string, and vice verca.
361
If workingtree_format is None, the transport is returned with no
362
checking. if it is not None, then the returned transport is
363
guaranteed to point to an existing directory ready for use.
365
raise NotImplementedError(self.get_workingtree_transport)
367
def __init__(self, _transport, _format):
368
"""Initialize a Bzr control dir object.
370
Only really common logic should reside here, concrete classes should be
371
made with varying behaviours.
373
:param _format: the format that is creating this BzrDir instance.
374
:param _transport: the transport this dir is based at.
376
self._format = _format
377
self.transport = _transport.clone('.bzr')
378
self.root_transport = _transport
380
def needs_format_conversion(self, format=None):
381
"""Return true if this bzrdir needs convert_format run on it.
383
For instance, if the repository format is out of date but the
384
branch and working tree are not, this should return True.
386
:param format: Optional parameter indicating a specific desired
387
format we plan to arrive at.
389
raise NotImplementedError(self.needs_format_conversion)
392
def open_unsupported(base):
393
"""Open a branch which is not supported."""
394
return BzrDir.open(base, _unsupported=True)
397
def open(base, _unsupported=False):
398
"""Open an existing bzrdir, rooted at 'base' (url)
400
_unsupported is a private parameter to the BzrDir class.
402
t = get_transport(base)
403
mutter("trying to open %r with transport %r", base, t)
404
format = BzrDirFormat.find_format(t)
405
BzrDir._check_supported(format, _unsupported)
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
BzrDir._check_supported(format, False)
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
self._control_files = LockableFiles(self.get_branch_transport(None),
543
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
544
"""See BzrDir.clone()."""
545
from bzrlib.workingtree import WorkingTreeFormat2
547
result = self._format.initialize(url, _cloning=True)
548
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
549
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
550
self.open_branch().clone(result, revision_id=revision_id)
552
self.open_workingtree().clone(result, basis=basis_tree)
553
except errors.NotLocalUrl:
554
# make a new one, this format always has to have one.
556
WorkingTreeFormat2().initialize(result)
557
except errors.NotLocalUrl:
558
# but we canot do it for remote trees.
562
def create_branch(self):
563
"""See BzrDir.create_branch."""
564
return self.open_branch()
566
def create_repository(self, shared=False):
567
"""See BzrDir.create_repository."""
569
raise errors.IncompatibleFormat('shared repository', self._format)
570
return self.open_repository()
572
def create_workingtree(self, revision_id=None):
573
"""See BzrDir.create_workingtree."""
574
# this looks buggy but is not -really-
575
# clone and sprout will have set the revision_id
576
# and that will have set it for us, its only
577
# specific uses of create_workingtree in isolation
578
# that can do wonky stuff here, and that only
579
# happens for creating checkouts, which cannot be
580
# done on this format anyway. So - acceptable wart.
581
result = self.open_workingtree()
582
if revision_id is not None:
583
result.set_last_revision(revision_id)
586
def get_branch_transport(self, branch_format):
587
"""See BzrDir.get_branch_transport()."""
588
if branch_format is None:
589
return self.transport
591
branch_format.get_format_string()
592
except NotImplementedError:
593
return self.transport
594
raise errors.IncompatibleFormat(branch_format, self._format)
596
def get_repository_transport(self, repository_format):
597
"""See BzrDir.get_repository_transport()."""
598
if repository_format is None:
599
return self.transport
601
repository_format.get_format_string()
602
except NotImplementedError:
603
return self.transport
604
raise errors.IncompatibleFormat(repository_format, self._format)
606
def get_workingtree_transport(self, workingtree_format):
607
"""See BzrDir.get_workingtree_transport()."""
608
if workingtree_format is None:
609
return self.transport
611
workingtree_format.get_format_string()
612
except NotImplementedError:
613
return self.transport
614
raise errors.IncompatibleFormat(workingtree_format, self._format)
616
def needs_format_conversion(self, format=None):
617
"""See BzrDir.needs_format_conversion()."""
618
# if the format is not the same as the system default,
619
# an upgrade is needed.
621
format = BzrDirFormat.get_default_format()
622
return not isinstance(self._format, format.__class__)
624
def open_branch(self, unsupported=False):
625
"""See BzrDir.open_branch."""
626
from bzrlib.branch import BzrBranchFormat4
627
format = BzrBranchFormat4()
628
self._check_supported(format, unsupported)
629
return format.open(self, _found=True)
631
def sprout(self, url, revision_id=None, basis=None):
632
"""See BzrDir.sprout()."""
633
from bzrlib.workingtree import WorkingTreeFormat2
635
result = self._format.initialize(url, _cloning=True)
636
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
638
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
639
except errors.NoRepositoryPresent:
642
self.open_branch().sprout(result, revision_id=revision_id)
643
except errors.NotBranchError:
645
# we always want a working tree
646
WorkingTreeFormat2().initialize(result)
650
class BzrDir4(BzrDirPreSplitOut):
651
"""A .bzr version 4 control object.
653
This is a deprecated format and may be removed after sept 2006.
656
def create_repository(self, shared=False):
657
"""See BzrDir.create_repository."""
658
return self._format.repository_format.initialize(self, shared)
660
def needs_format_conversion(self, format=None):
661
"""Format 4 dirs are always in need of conversion."""
664
def open_repository(self):
665
"""See BzrDir.open_repository."""
666
from bzrlib.repository import RepositoryFormat4
667
return RepositoryFormat4().open(self, _found=True)
670
class BzrDir5(BzrDirPreSplitOut):
671
"""A .bzr version 5 control object.
673
This is a deprecated format and may be removed after sept 2006.
676
def open_repository(self):
677
"""See BzrDir.open_repository."""
678
from bzrlib.repository import RepositoryFormat5
679
return RepositoryFormat5().open(self, _found=True)
681
def open_workingtree(self, _unsupported=False):
682
"""See BzrDir.create_workingtree."""
683
from bzrlib.workingtree import WorkingTreeFormat2
684
return WorkingTreeFormat2().open(self, _found=True)
687
class BzrDir6(BzrDirPreSplitOut):
688
"""A .bzr version 6 control object.
690
This is a deprecated format and may be removed after sept 2006.
693
def open_repository(self):
694
"""See BzrDir.open_repository."""
695
from bzrlib.repository import RepositoryFormat6
696
return RepositoryFormat6().open(self, _found=True)
698
def open_workingtree(self, _unsupported=False):
699
"""See BzrDir.create_workingtree."""
700
from bzrlib.workingtree import WorkingTreeFormat2
701
return WorkingTreeFormat2().open(self, _found=True)
704
class BzrDirMeta1(BzrDir):
705
"""A .bzr meta version 1 control object.
707
This is the first control object where the
708
individual formats are really split out.
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."""
835
def find_format(klass, transport):
836
"""Return the format registered for URL."""
838
format_string = transport.get(".bzr/branch-format").read()
839
return klass._formats[format_string]
840
except errors.NoSuchFile:
841
raise errors.NotBranchError(path=transport.base)
843
raise errors.UnknownFormatError(format_string)
846
def get_default_format(klass):
847
"""Return the current default format."""
848
return klass._default_format
850
def get_format_string(self):
851
"""Return the ASCII format string that identifies this format."""
852
raise NotImplementedError(self.get_format_string)
854
def get_converter(self, format=None):
855
"""Return the converter to use to convert bzrdirs needing converts.
857
This returns a bzrlib.bzrdir.Converter object.
859
This should return the best upgrader to step this format towards the
860
current default format. In the case of plugins we can/shouold provide
861
some means for them to extend the range of returnable converters.
863
:param format: Optional format to override the default foramt of the
866
raise NotImplementedError(self.get_converter)
868
def initialize(self, url):
869
"""Create a bzr control dir at this url and return an opened copy."""
870
# Since we don't have a .bzr directory, inherit the
871
# mode from the root directory
872
t = get_transport(url)
873
temp_control = LockableFiles(t, '', TransportLock)
874
temp_control._transport.mkdir('.bzr',
875
# FIXME: RBC 20060121 dont peek under
877
mode=temp_control._dir_mode)
878
file_mode = temp_control._file_mode
880
mutter('created control directory in ' + t.base)
881
control = t.clone('.bzr')
882
lock_file = 'branch-lock'
883
utf8_files = [('README',
884
"This is a Bazaar-NG control directory.\n"
885
"Do not change any files in this directory.\n"),
886
('branch-format', self.get_format_string()),
888
# NB: no need to escape relative paths that are url safe.
889
control_files = LockableFiles(control, lock_file, TransportLock)
890
control_files.create_lock()
891
control_files.lock_write()
893
for file, content in utf8_files:
894
control_files.put_utf8(file, content)
896
control_files.unlock()
897
return self.open(t, _found=True)
899
def is_supported(self):
900
"""Is this format supported?
902
Supported formats must be initializable and openable.
903
Unsupported formats may not support initialization or committing or
904
some other features depending on the reason for not being supported.
908
def open(self, transport, _found=False):
909
"""Return an instance of this format for the dir transport points at.
911
_found is a private parameter, do not use it.
914
assert isinstance(BzrDirFormat.find_format(transport),
916
return self._open(transport)
918
def _open(self, transport):
919
"""Template method helper for opening BzrDirectories.
921
This performs the actual open and any additional logic or parameter
924
raise NotImplementedError(self._open)
927
def register_format(klass, format):
928
klass._formats[format.get_format_string()] = format
931
def set_default_format(klass, format):
932
klass._default_format = format
935
return self.get_format_string()[:-1]
938
def unregister_format(klass, format):
939
assert klass._formats[format.get_format_string()] is format
940
del klass._formats[format.get_format_string()]
943
class BzrDirFormat4(BzrDirFormat):
946
This format is a combined format for working tree, branch and repository.
948
- Format 1 working trees [always]
949
- Format 4 branches [always]
950
- Format 4 repositories [always]
952
This format is deprecated: it indexes texts using a text it which is
953
removed in format 5; write support for this format has been removed.
956
def get_format_string(self):
957
"""See BzrDirFormat.get_format_string()."""
958
return "Bazaar-NG branch, format 0.0.4\n"
960
def get_converter(self, format=None):
961
"""See BzrDirFormat.get_converter()."""
962
# there is one and only one upgrade path here.
963
return ConvertBzrDir4To5()
965
def initialize(self, url):
966
"""Format 4 branches cannot be created."""
967
raise errors.UninitializableFormat(self)
969
def is_supported(self):
970
"""Format 4 is not supported.
972
It is not supported because the model changed from 4 to 5 and the
973
conversion logic is expensive - so doing it on the fly was not
978
def _open(self, transport):
979
"""See BzrDirFormat._open."""
980
return BzrDir4(transport, self)
982
def __return_repository_format(self):
983
"""Circular import protection."""
984
from bzrlib.repository import RepositoryFormat4
985
return RepositoryFormat4(self)
986
repository_format = property(__return_repository_format)
989
class BzrDirFormat5(BzrDirFormat):
990
"""Bzr control format 5.
992
This format is a combined format for working tree, branch and repository.
994
- Format 2 working trees [always]
995
- Format 4 branches [always]
996
- Format 5 repositories [always]
997
Unhashed stores in the repository.
1000
def get_format_string(self):
1001
"""See BzrDirFormat.get_format_string()."""
1002
return "Bazaar-NG branch, format 5\n"
1004
def get_converter(self, format=None):
1005
"""See BzrDirFormat.get_converter()."""
1006
# there is one and only one upgrade path here.
1007
return ConvertBzrDir5To6()
1009
def initialize(self, url, _cloning=False):
1010
"""Format 5 dirs always have working tree, branch and repository.
1012
Except when they are being cloned.
1014
from bzrlib.branch import BzrBranchFormat4
1015
from bzrlib.repository import RepositoryFormat5
1016
from bzrlib.workingtree import WorkingTreeFormat2
1017
result = super(BzrDirFormat5, self).initialize(url)
1018
RepositoryFormat5().initialize(result, _internal=True)
1020
BzrBranchFormat4().initialize(result)
1021
WorkingTreeFormat2().initialize(result)
1024
def _open(self, transport):
1025
"""See BzrDirFormat._open."""
1026
return BzrDir5(transport, self)
1028
def __return_repository_format(self):
1029
"""Circular import protection."""
1030
from bzrlib.repository import RepositoryFormat5
1031
return RepositoryFormat5(self)
1032
repository_format = property(__return_repository_format)
1035
class BzrDirFormat6(BzrDirFormat):
1036
"""Bzr control format 6.
1038
This format is a combined format for working tree, branch and repository.
1040
- Format 2 working trees [always]
1041
- Format 4 branches [always]
1042
- Format 6 repositories [always]
1045
def get_format_string(self):
1046
"""See BzrDirFormat.get_format_string()."""
1047
return "Bazaar-NG branch, format 6\n"
1049
def get_converter(self, format=None):
1050
"""See BzrDirFormat.get_converter()."""
1051
# there is one and only one upgrade path here.
1052
return ConvertBzrDir6ToMeta()
1054
def initialize(self, url, _cloning=False):
1055
"""Format 6 dirs always have working tree, branch and repository.
1057
Except when they are being cloned.
1059
from bzrlib.branch import BzrBranchFormat4
1060
from bzrlib.repository import RepositoryFormat6
1061
from bzrlib.workingtree import WorkingTreeFormat2
1062
result = super(BzrDirFormat6, self).initialize(url)
1063
RepositoryFormat6().initialize(result, _internal=True)
1065
BzrBranchFormat4().initialize(result)
1067
WorkingTreeFormat2().initialize(result)
1068
except errors.NotLocalUrl:
1069
# emulate pre-check behaviour for working tree and silently
1074
def _open(self, transport):
1075
"""See BzrDirFormat._open."""
1076
return BzrDir6(transport, self)
1078
def __return_repository_format(self):
1079
"""Circular import protection."""
1080
from bzrlib.repository import RepositoryFormat6
1081
return RepositoryFormat6(self)
1082
repository_format = property(__return_repository_format)
1085
class BzrDirMetaFormat1(BzrDirFormat):
1086
"""Bzr meta control format 1
1088
This is the first format with split out working tree, branch and repository
1091
- Format 3 working trees [optional]
1092
- Format 5 branches [optional]
1093
- Format 7 repositories [optional]
1096
def get_converter(self, format=None):
1097
"""See BzrDirFormat.get_converter()."""
1099
format = BzrDirFormat.get_default_format()
1100
if not isinstance(self, format.__class__):
1101
# converting away from metadir is not implemented
1102
raise NotImplementedError(self.get_converter)
1103
return ConvertMetaToMeta(format)
1105
def get_format_string(self):
1106
"""See BzrDirFormat.get_format_string()."""
1107
return "Bazaar-NG meta directory, format 1\n"
1109
def _open(self, transport):
1110
"""See BzrDirFormat._open."""
1111
return BzrDirMeta1(transport, self)
1113
def __return_repository_format(self):
1114
"""Circular import protection."""
1115
if getattr(self, '_repository_format', None):
1116
return self._repository_format
1117
from bzrlib.repository import RepositoryFormat
1118
return RepositoryFormat.get_default_format()
1120
def __set_repository_format(self, value):
1121
"""Allow changint the repository format for metadir formats."""
1122
self._repository_format = value
1123
repository_format = property(__return_repository_format, __set_repository_format)
1126
BzrDirFormat.register_format(BzrDirFormat4())
1127
BzrDirFormat.register_format(BzrDirFormat5())
1128
BzrDirFormat.register_format(BzrDirMetaFormat1())
1129
__default_format = BzrDirFormat6()
1130
BzrDirFormat.register_format(__default_format)
1131
BzrDirFormat.set_default_format(__default_format)
1134
class BzrDirTestProviderAdapter(object):
1135
"""A tool to generate a suite testing multiple bzrdir formats at once.
1137
This is done by copying the test once for each transport and injecting
1138
the transport_server, transport_readonly_server, and bzrdir_format
1139
classes into each copy. Each copy is also given a new id() to make it
1143
def __init__(self, transport_server, transport_readonly_server, formats):
1144
self._transport_server = transport_server
1145
self._transport_readonly_server = transport_readonly_server
1146
self._formats = formats
1148
def adapt(self, test):
1149
result = TestSuite()
1150
for format in self._formats:
1151
new_test = deepcopy(test)
1152
new_test.transport_server = self._transport_server
1153
new_test.transport_readonly_server = self._transport_readonly_server
1154
new_test.bzrdir_format = format
1155
def make_new_test_id():
1156
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1157
return lambda: new_id
1158
new_test.id = make_new_test_id()
1159
result.addTest(new_test)
1163
class ScratchDir(BzrDir6):
1164
"""Special test class: a bzrdir that cleans up itself..
1166
>>> d = ScratchDir()
1167
>>> base = d.transport.base
1170
>>> b.transport.__del__()
1175
def __init__(self, files=[], dirs=[], transport=None):
1176
"""Make a test branch.
1178
This creates a temporary directory and runs init-tree in it.
1180
If any files are listed, they are created in the working copy.
1182
if transport is None:
1183
transport = bzrlib.transport.local.ScratchTransport()
1184
# local import for scope restriction
1185
BzrDirFormat6().initialize(transport.base)
1186
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1187
self.create_repository()
1188
self.create_branch()
1189
self.create_workingtree()
1191
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1193
# BzrBranch creates a clone to .bzr and then forgets about the
1194
# original transport. A ScratchTransport() deletes itself and
1195
# everything underneath it when it goes away, so we need to
1196
# grab a local copy to prevent that from happening
1197
self._transport = transport
1200
self._transport.mkdir(d)
1203
self._transport.put(f, 'content of %s' % f)
1207
>>> orig = ScratchDir(files=["file1", "file2"])
1208
>>> os.listdir(orig.base)
1209
[u'.bzr', u'file1', u'file2']
1210
>>> clone = orig.clone()
1211
>>> if os.name != 'nt':
1212
... os.path.samefile(orig.base, clone.base)
1214
... orig.base == clone.base
1217
>>> os.listdir(clone.base)
1218
[u'.bzr', u'file1', u'file2']
1220
from shutil import copytree
1221
from bzrlib.osutils import mkdtemp
1224
copytree(self.base, base, symlinks=True)
1226
transport=bzrlib.transport.local.ScratchTransport(base))
1229
class Converter(object):
1230
"""Converts a disk format object from one format to another."""
1232
def convert(self, to_convert, pb):
1233
"""Perform the conversion of to_convert, giving feedback via pb.
1235
:param to_convert: The disk object to convert.
1236
:param pb: a progress bar to use for progress information.
1239
def step(self, message):
1240
"""Update the pb by a step."""
1242
self.pb.update(message, self.count, self.total)
1245
class ConvertBzrDir4To5(Converter):
1246
"""Converts format 4 bzr dirs to format 5."""
1249
super(ConvertBzrDir4To5, self).__init__()
1250
self.converted_revs = set()
1251
self.absent_revisions = set()
1255
def convert(self, to_convert, pb):
1256
"""See Converter.convert()."""
1257
self.bzrdir = to_convert
1259
self.pb.note('starting upgrade from format 4 to 5')
1260
if isinstance(self.bzrdir.transport, LocalTransport):
1261
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1262
self._convert_to_weaves()
1263
return BzrDir.open(self.bzrdir.root_transport.base)
1265
def _convert_to_weaves(self):
1266
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1269
stat = self.bzrdir.transport.stat('weaves')
1270
if not S_ISDIR(stat.st_mode):
1271
self.bzrdir.transport.delete('weaves')
1272
self.bzrdir.transport.mkdir('weaves')
1273
except errors.NoSuchFile:
1274
self.bzrdir.transport.mkdir('weaves')
1275
# deliberately not a WeaveFile as we want to build it up slowly.
1276
self.inv_weave = Weave('inventory')
1277
# holds in-memory weaves for all files
1278
self.text_weaves = {}
1279
self.bzrdir.transport.delete('branch-format')
1280
self.branch = self.bzrdir.open_branch()
1281
self._convert_working_inv()
1282
rev_history = self.branch.revision_history()
1283
# to_read is a stack holding the revisions we still need to process;
1284
# appending to it adds new highest-priority revisions
1285
self.known_revisions = set(rev_history)
1286
self.to_read = rev_history[-1:]
1288
rev_id = self.to_read.pop()
1289
if (rev_id not in self.revisions
1290
and rev_id not in self.absent_revisions):
1291
self._load_one_rev(rev_id)
1293
to_import = self._make_order()
1294
for i, rev_id in enumerate(to_import):
1295
self.pb.update('converting revision', i, len(to_import))
1296
self._convert_one_rev(rev_id)
1298
self._write_all_weaves()
1299
self._write_all_revs()
1300
self.pb.note('upgraded to weaves:')
1301
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1302
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1303
self.pb.note(' %6d texts', self.text_count)
1304
self._cleanup_spare_files_after_format4()
1305
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1307
def _cleanup_spare_files_after_format4(self):
1308
# FIXME working tree upgrade foo.
1309
for n in 'merged-patches', 'pending-merged-patches':
1311
## assert os.path.getsize(p) == 0
1312
self.bzrdir.transport.delete(n)
1313
except errors.NoSuchFile:
1315
self.bzrdir.transport.delete_tree('inventory-store')
1316
self.bzrdir.transport.delete_tree('text-store')
1318
def _convert_working_inv(self):
1319
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1320
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1321
# FIXME inventory is a working tree change.
1322
self.branch.control_files.put('inventory', new_inv_xml)
1324
def _write_all_weaves(self):
1325
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1326
weave_transport = self.bzrdir.transport.clone('weaves')
1327
weaves = WeaveStore(weave_transport, prefixed=False)
1328
transaction = WriteTransaction()
1332
for file_id, file_weave in self.text_weaves.items():
1333
self.pb.update('writing weave', i, len(self.text_weaves))
1334
weaves._put_weave(file_id, file_weave, transaction)
1336
self.pb.update('inventory', 0, 1)
1337
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1338
self.pb.update('inventory', 1, 1)
1342
def _write_all_revs(self):
1343
"""Write all revisions out in new form."""
1344
self.bzrdir.transport.delete_tree('revision-store')
1345
self.bzrdir.transport.mkdir('revision-store')
1346
revision_transport = self.bzrdir.transport.clone('revision-store')
1348
_revision_store = TextRevisionStore(TextStore(revision_transport,
1352
transaction = bzrlib.transactions.WriteTransaction()
1353
for i, rev_id in enumerate(self.converted_revs):
1354
self.pb.update('write revision', i, len(self.converted_revs))
1355
_revision_store.add_revision(self.revisions[rev_id], transaction)
1359
def _load_one_rev(self, rev_id):
1360
"""Load a revision object into memory.
1362
Any parents not either loaded or abandoned get queued to be
1364
self.pb.update('loading revision',
1365
len(self.revisions),
1366
len(self.known_revisions))
1367
if not self.branch.repository.has_revision(rev_id):
1369
self.pb.note('revision {%s} not present in branch; '
1370
'will be converted as a ghost',
1372
self.absent_revisions.add(rev_id)
1374
rev = self.branch.repository._revision_store.get_revision(rev_id,
1375
self.branch.repository.get_transaction())
1376
for parent_id in rev.parent_ids:
1377
self.known_revisions.add(parent_id)
1378
self.to_read.append(parent_id)
1379
self.revisions[rev_id] = rev
1381
def _load_old_inventory(self, rev_id):
1382
assert rev_id not in self.converted_revs
1383
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1384
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1385
rev = self.revisions[rev_id]
1386
if rev.inventory_sha1:
1387
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1388
'inventory sha mismatch for {%s}' % rev_id
1391
def _load_updated_inventory(self, rev_id):
1392
assert rev_id in self.converted_revs
1393
inv_xml = self.inv_weave.get_text(rev_id)
1394
inv = serializer_v5.read_inventory_from_string(inv_xml)
1397
def _convert_one_rev(self, rev_id):
1398
"""Convert revision and all referenced objects to new format."""
1399
rev = self.revisions[rev_id]
1400
inv = self._load_old_inventory(rev_id)
1401
present_parents = [p for p in rev.parent_ids
1402
if p not in self.absent_revisions]
1403
self._convert_revision_contents(rev, inv, present_parents)
1404
self._store_new_weave(rev, inv, present_parents)
1405
self.converted_revs.add(rev_id)
1407
def _store_new_weave(self, rev, inv, present_parents):
1408
# the XML is now updated with text versions
1412
if ie.kind == 'root_directory':
1414
assert hasattr(ie, 'revision'), \
1415
'no revision on {%s} in {%s}' % \
1416
(file_id, rev.revision_id)
1417
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1418
new_inv_sha1 = sha_string(new_inv_xml)
1419
self.inv_weave.add_lines(rev.revision_id,
1421
new_inv_xml.splitlines(True))
1422
rev.inventory_sha1 = new_inv_sha1
1424
def _convert_revision_contents(self, rev, inv, present_parents):
1425
"""Convert all the files within a revision.
1427
Also upgrade the inventory to refer to the text revision ids."""
1428
rev_id = rev.revision_id
1429
mutter('converting texts of revision {%s}',
1431
parent_invs = map(self._load_updated_inventory, present_parents)
1434
self._convert_file_version(rev, ie, parent_invs)
1436
def _convert_file_version(self, rev, ie, parent_invs):
1437
"""Convert one version of one file.
1439
The file needs to be added into the weave if it is a merge
1440
of >=2 parents or if it's changed from its parent.
1442
if ie.kind == 'root_directory':
1444
file_id = ie.file_id
1445
rev_id = rev.revision_id
1446
w = self.text_weaves.get(file_id)
1449
self.text_weaves[file_id] = w
1450
text_changed = False
1451
previous_entries = ie.find_previous_heads(parent_invs, w)
1452
for old_revision in previous_entries:
1453
# if this fails, its a ghost ?
1454
assert old_revision in self.converted_revs
1455
self.snapshot_ie(previous_entries, ie, w, rev_id)
1457
assert getattr(ie, 'revision', None) is not None
1459
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1460
# TODO: convert this logic, which is ~= snapshot to
1461
# a call to:. This needs the path figured out. rather than a work_tree
1462
# a v4 revision_tree can be given, or something that looks enough like
1463
# one to give the file content to the entry if it needs it.
1464
# and we need something that looks like a weave store for snapshot to
1466
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1467
if len(previous_revisions) == 1:
1468
previous_ie = previous_revisions.values()[0]
1469
if ie._unchanged(previous_ie):
1470
ie.revision = previous_ie.revision
1473
text = self.branch.repository.text_store.get(ie.text_id)
1474
file_lines = text.readlines()
1475
assert sha_strings(file_lines) == ie.text_sha1
1476
assert sum(map(len, file_lines)) == ie.text_size
1477
w.add_lines(rev_id, previous_revisions, file_lines)
1478
self.text_count += 1
1480
w.add_lines(rev_id, previous_revisions, [])
1481
ie.revision = rev_id
1483
def _make_order(self):
1484
"""Return a suitable order for importing revisions.
1486
The order must be such that an revision is imported after all
1487
its (present) parents.
1489
todo = set(self.revisions.keys())
1490
done = self.absent_revisions.copy()
1493
# scan through looking for a revision whose parents
1495
for rev_id in sorted(list(todo)):
1496
rev = self.revisions[rev_id]
1497
parent_ids = set(rev.parent_ids)
1498
if parent_ids.issubset(done):
1499
# can take this one now
1500
order.append(rev_id)
1506
class ConvertBzrDir5To6(Converter):
1507
"""Converts format 5 bzr dirs to format 6."""
1509
def convert(self, to_convert, pb):
1510
"""See Converter.convert()."""
1511
self.bzrdir = to_convert
1513
self.pb.note('starting upgrade from format 5 to 6')
1514
self._convert_to_prefixed()
1515
return BzrDir.open(self.bzrdir.root_transport.base)
1517
def _convert_to_prefixed(self):
1518
from bzrlib.store import hash_prefix
1519
self.bzrdir.transport.delete('branch-format')
1520
for store_name in ["weaves", "revision-store"]:
1521
self.pb.note("adding prefixes to %s" % store_name)
1522
store_transport = self.bzrdir.transport.clone(store_name)
1523
for filename in store_transport.list_dir('.'):
1524
if (filename.endswith(".weave") or
1525
filename.endswith(".gz") or
1526
filename.endswith(".sig")):
1527
file_id = os.path.splitext(filename)[0]
1530
prefix_dir = hash_prefix(file_id)
1531
# FIXME keep track of the dirs made RBC 20060121
1533
store_transport.move(filename, prefix_dir + '/' + filename)
1534
except errors.NoSuchFile: # catches missing dirs strangely enough
1535
store_transport.mkdir(prefix_dir)
1536
store_transport.move(filename, prefix_dir + '/' + filename)
1537
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1540
class ConvertBzrDir6ToMeta(Converter):
1541
"""Converts format 6 bzr dirs to metadirs."""
1543
def convert(self, to_convert, pb):
1544
"""See Converter.convert()."""
1545
self.bzrdir = to_convert
1548
self.total = 20 # the steps we know about
1549
self.garbage_inventories = []
1551
self.pb.note('starting upgrade from format 6 to metadir')
1552
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1553
# its faster to move specific files around than to open and use the apis...
1554
# first off, nuke ancestry.weave, it was never used.
1556
self.step('Removing ancestry.weave')
1557
self.bzrdir.transport.delete('ancestry.weave')
1558
except errors.NoSuchFile:
1560
# find out whats there
1561
self.step('Finding branch files')
1562
last_revision = self.bzrdir.open_workingtree().last_revision()
1563
bzrcontents = self.bzrdir.transport.list_dir('.')
1564
for name in bzrcontents:
1565
if name.startswith('basis-inventory.'):
1566
self.garbage_inventories.append(name)
1567
# create new directories for repository, working tree and branch
1568
dir_mode = self.bzrdir._control_files._dir_mode
1569
self.file_mode = self.bzrdir._control_files._file_mode
1570
repository_names = [('inventory.weave', True),
1571
('revision-store', True),
1573
self.step('Upgrading repository ')
1574
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1575
self.make_lock('repository')
1576
# we hard code the formats here because we are converting into
1577
# the meta format. The meta format upgrader can take this to a
1578
# future format within each component.
1579
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1580
for entry in repository_names:
1581
self.move_entry('repository', entry)
1583
self.step('Upgrading branch ')
1584
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1585
self.make_lock('branch')
1586
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1587
branch_files = [('revision-history', True),
1588
('branch-name', True),
1590
for entry in branch_files:
1591
self.move_entry('branch', entry)
1593
self.step('Upgrading working tree')
1594
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1595
self.make_lock('checkout')
1596
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1597
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1598
checkout_files = [('pending-merges', True),
1599
('inventory', True),
1600
('stat-cache', False)]
1601
for entry in checkout_files:
1602
self.move_entry('checkout', entry)
1603
if last_revision is not None:
1604
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1606
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1607
return BzrDir.open(self.bzrdir.root_transport.base)
1609
def make_lock(self, name):
1610
"""Make a lock for the new control dir name."""
1611
self.step('Make %s lock' % name)
1612
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1614
def move_entry(self, new_dir, entry):
1615
"""Move then entry name into new_dir."""
1617
mandatory = entry[1]
1618
self.step('Moving %s' % name)
1620
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1621
except errors.NoSuchFile:
1625
def put_format(self, dirname, format):
1626
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1629
class ConvertMetaToMeta(Converter):
1630
"""Converts the components of metadirs."""
1632
def __init__(self, target_format):
1633
"""Create a metadir to metadir converter.
1635
:param target_format: The final metadir format that is desired.
1637
self.target_format = target_format
1639
def convert(self, to_convert, pb):
1640
"""See Converter.convert()."""
1641
self.bzrdir = to_convert
1645
self.step('checking repository format')
1647
repo = self.bzrdir.open_repository()
1648
except errors.NoRepositoryPresent:
1651
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1652
from bzrlib.repository import CopyConverter
1653
self.pb.note('starting repository conversion')
1654
converter = CopyConverter(self.target_format.repository_format)
1655
converter.convert(repo, pb)