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:
155
def create(cls, base):
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
if cls is not BzrDir:
165
raise AssertionError("BzrDir.create always creates the default format, "
166
"not one of %r" % cls)
167
segments = base.split('/')
168
if segments and segments[-1] not in ('', '.'):
169
parent = '/'.join(segments[:-1])
170
t = bzrlib.transport.get_transport(parent)
172
t.mkdir(segments[-1])
173
except errors.FileExists:
175
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
177
def create_branch(self):
178
"""Create a branch in this BzrDir.
180
The bzrdirs format will control what branch format is created.
181
For more control see BranchFormatXX.create(a_bzrdir).
183
raise NotImplementedError(self.create_branch)
186
def create_branch_and_repo(base, force_new_repo=False):
187
"""Create a new BzrDir, Branch and Repository at the url 'base'.
189
This will use the current default BzrDirFormat, and use whatever
190
repository format that that uses via bzrdir.create_branch and
191
create_repository. If a shared repository is available that is used
194
The created Branch object is returned.
196
:param base: The URL to create the branch at.
197
:param force_new_repo: If True a new repository is always created.
199
bzrdir = BzrDir.create(base)
200
bzrdir._find_or_create_repository(force_new_repo)
201
return bzrdir.create_branch()
203
def _find_or_create_repository(self, force_new_repo):
204
"""Create a new repository if needed, returning the repository."""
206
return self.create_repository()
208
return self.find_repository()
209
except errors.NoRepositoryPresent:
210
return self.create_repository()
213
def create_branch_convenience(base, force_new_repo=False, force_new_tree=None):
214
"""Create a new BzrDir, Branch and Repository at the url 'base'.
216
This is a convenience function - it will use an existing repository
217
if possible, can be told explicitly whether to create a working tree or
220
This will use the current default BzrDirFormat, and use whatever
221
repository format that that uses via bzrdir.create_branch and
222
create_repository. If a shared repository is available that is used
223
preferentially. Whatever repository is used, its tree creation policy
226
The created Branch object is returned.
227
If a working tree cannot be made due to base not being a file:// url,
228
no error is raised unless force_new_tree is True, in which case no
229
data is created on disk and NotLocalUrl is raised.
231
:param base: The URL to create the branch at.
232
:param force_new_repo: If True a new repository is always created.
233
:param force_new_tree: If True or False force creation of a tree or
234
prevent such creation respectively.
237
# check for non local urls
238
t = get_transport(safe_unicode(base))
239
if not isinstance(t, LocalTransport):
240
raise errors.NotLocalUrl(base)
241
bzrdir = BzrDir.create(base)
242
repo = bzrdir._find_or_create_repository(force_new_repo)
243
result = bzrdir.create_branch()
244
if force_new_tree or (repo.make_working_trees() and
245
force_new_tree is None):
247
bzrdir.create_workingtree()
248
except errors.NotLocalUrl:
253
def create_repository(base, shared=False):
254
"""Create a new BzrDir and Repository at the url 'base'.
256
This will use the current default BzrDirFormat, and use whatever
257
repository format that that uses for bzrdirformat.create_repository.
259
;param shared: Create a shared repository rather than a standalone
261
The Repository object is returned.
263
This must be overridden as an instance method in child classes, where
264
it should take no parameters and construct whatever repository format
265
that child class desires.
267
bzrdir = BzrDir.create(base)
268
return bzrdir.create_repository()
271
def create_standalone_workingtree(base):
272
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
274
'base' must be a local path or a file:// url.
276
This will use the current default BzrDirFormat, and use whatever
277
repository format that that uses for bzrdirformat.create_workingtree,
278
create_branch and create_repository.
280
The WorkingTree object is returned.
282
t = get_transport(safe_unicode(base))
283
if not isinstance(t, LocalTransport):
284
raise errors.NotLocalUrl(base)
285
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
286
force_new_repo=True).bzrdir
287
return bzrdir.create_workingtree()
289
def create_workingtree(self, revision_id=None):
290
"""Create a working tree at this BzrDir.
292
revision_id: create it as of this revision id.
294
raise NotImplementedError(self.create_workingtree)
296
def find_repository(self):
297
"""Find the repository that should be used for a_bzrdir.
299
This does not require a branch as we use it to find the repo for
300
new branches as well as to hook existing branches up to their
304
return self.open_repository()
305
except errors.NoRepositoryPresent:
307
next_transport = self.root_transport.clone('..')
310
found_bzrdir = BzrDir.open_containing_from_transport(
312
except errors.NotBranchError:
313
raise errors.NoRepositoryPresent(self)
315
repository = found_bzrdir.open_repository()
316
except errors.NoRepositoryPresent:
317
next_transport = found_bzrdir.root_transport.clone('..')
319
if ((found_bzrdir.root_transport.base ==
320
self.root_transport.base) or repository.is_shared()):
323
raise errors.NoRepositoryPresent(self)
324
raise errors.NoRepositoryPresent(self)
326
def get_branch_transport(self, branch_format):
327
"""Get the transport for use by branch format in this BzrDir.
329
Note that bzr dirs that do not support format strings will raise
330
IncompatibleFormat if the branch format they are given has
331
a format string, and vice verca.
333
If branch_format is None, the transport is returned with no
334
checking. if it is not None, then the returned transport is
335
guaranteed to point to an existing directory ready for use.
337
raise NotImplementedError(self.get_branch_transport)
339
def get_repository_transport(self, repository_format):
340
"""Get the transport for use by repository format in this BzrDir.
342
Note that bzr dirs that do not support format strings will raise
343
IncompatibleFormat if the repository format they are given has
344
a format string, and vice verca.
346
If repository_format is None, the transport is returned with no
347
checking. if it is not None, then the returned transport is
348
guaranteed to point to an existing directory ready for use.
350
raise NotImplementedError(self.get_repository_transport)
352
def get_workingtree_transport(self, tree_format):
353
"""Get the transport for use by workingtree format in this BzrDir.
355
Note that bzr dirs that do not support format strings will raise
356
IncompatibleFormat if the workingtree format they are given has
357
a format string, and vice verca.
359
If workingtree_format is None, the transport is returned with no
360
checking. if it is not None, then the returned transport is
361
guaranteed to point to an existing directory ready for use.
363
raise NotImplementedError(self.get_workingtree_transport)
365
def __init__(self, _transport, _format):
366
"""Initialize a Bzr control dir object.
368
Only really common logic should reside here, concrete classes should be
369
made with varying behaviours.
371
:param _format: the format that is creating this BzrDir instance.
372
:param _transport: the transport this dir is based at.
374
self._format = _format
375
self.transport = _transport.clone('.bzr')
376
self.root_transport = _transport
378
def needs_format_conversion(self, format=None):
379
"""Return true if this bzrdir needs convert_format run on it.
381
For instance, if the repository format is out of date but the
382
branch and working tree are not, this should return True.
384
:param format: Optional parameter indicating a specific desired
385
format we plan to arrive at.
387
raise NotImplementedError(self.needs_format_conversion)
390
def open_unsupported(base):
391
"""Open a branch which is not supported."""
392
return BzrDir.open(base, _unsupported=True)
395
def open(base, _unsupported=False):
396
"""Open an existing bzrdir, rooted at 'base' (url)
398
_unsupported is a private parameter to the BzrDir class.
400
t = get_transport(base)
401
mutter("trying to open %r with transport %r", base, t)
402
format = BzrDirFormat.find_format(t)
403
if not _unsupported and not format.is_supported():
404
# see open_downlevel to open legacy branches.
405
raise errors.UnsupportedFormatError(
406
'sorry, format %s not supported' % format,
407
['use a different bzr version',
408
'or remove the .bzr directory'
409
' and "bzr init" again'])
410
return format.open(t, _found=True)
412
def open_branch(self, unsupported=False):
413
"""Open the branch object at this BzrDir if one is present.
415
If unsupported is True, then no longer supported branch formats can
418
TODO: static convenience version of this?
420
raise NotImplementedError(self.open_branch)
423
def open_containing(url):
424
"""Open an existing branch which contains url.
426
:param url: url to search from.
427
See open_containing_from_transport for more detail.
429
return BzrDir.open_containing_from_transport(get_transport(url))
432
def open_containing_from_transport(a_transport):
433
"""Open an existing branch which contains a_transport.base
435
This probes for a branch at a_transport, and searches upwards from there.
437
Basically we keep looking up until we find the control directory or
438
run into the root. If there isn't one, raises NotBranchError.
439
If there is one and it is either an unrecognised format or an unsupported
440
format, UnknownFormatError or UnsupportedFormatError are raised.
441
If there is one, it is returned, along with the unused portion of url.
443
# this gets the normalised url back. I.e. '.' -> the full path.
444
url = a_transport.base
447
format = BzrDirFormat.find_format(a_transport)
448
return format.open(a_transport), a_transport.relpath(url)
449
except errors.NotBranchError, e:
450
mutter('not a branch in: %r %s', a_transport.base, e)
451
new_t = a_transport.clone('..')
452
if new_t.base == a_transport.base:
453
# reached the root, whatever that may be
454
raise errors.NotBranchError(path=url)
457
def open_repository(self, _unsupported=False):
458
"""Open the repository object at this BzrDir if one is present.
460
This will not follow the Branch object pointer - its strictly a direct
461
open facility. Most client code should use open_branch().repository to
464
_unsupported is a private parameter, not part of the api.
465
TODO: static convenience version of this?
467
raise NotImplementedError(self.open_repository)
469
def open_workingtree(self, _unsupported=False):
470
"""Open the workingtree object at this BzrDir if one is present.
472
TODO: static convenience version of this?
474
raise NotImplementedError(self.open_workingtree)
476
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
477
"""Create a copy of this bzrdir prepared for use as a new line of
480
If urls last component does not exist, it will be created.
482
Attributes related to the identity of the source branch like
483
branch nickname will be cleaned, a working tree is created
484
whether one existed before or not; and a local branch is always
487
if revision_id is not None, then the clone operation may tune
488
itself to download less data.
491
result = self._format.initialize(url)
492
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
494
source_branch = self.open_branch()
495
source_repository = source_branch.repository
496
except errors.NotBranchError:
499
source_repository = self.open_repository()
500
except errors.NoRepositoryPresent:
501
# copy the entire basis one if there is one
502
# but there is no repository.
503
source_repository = basis_repo
508
result_repo = result.find_repository()
509
except errors.NoRepositoryPresent:
511
if source_repository is None and result_repo is not None:
513
elif source_repository is None and result_repo is None:
514
# no repo available, make a new one
515
result.create_repository()
516
elif source_repository is not None and result_repo is None:
517
# have soure, and want to make a new target repo
518
source_repository.clone(result,
519
revision_id=revision_id,
522
# fetch needed content into target.
524
# XXX FIXME RBC 20060214 need tests for this when the basis
526
result_repo.fetch(basis_repo, revision_id=revision_id)
527
result_repo.fetch(source_repository, revision_id=revision_id)
528
if source_branch is not None:
529
source_branch.sprout(result, revision_id=revision_id)
531
result.create_branch()
532
result.create_workingtree()
536
class BzrDirPreSplitOut(BzrDir):
537
"""A common class for the all-in-one formats."""
539
def __init__(self, _transport, _format):
540
"""See BzrDir.__init__."""
541
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
542
assert self._format._lock_class == TransportLock
543
assert self._format._lock_file_name == 'branch-lock'
544
self._control_files = LockableFiles(self.get_branch_transport(None),
545
self._format._lock_file_name,
546
self._format._lock_class)
548
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
549
"""See BzrDir.clone()."""
550
from bzrlib.workingtree import WorkingTreeFormat2
552
result = self._format.initialize(url, _cloning=True)
553
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
554
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
555
self.open_branch().clone(result, revision_id=revision_id)
557
self.open_workingtree().clone(result, basis=basis_tree)
558
except errors.NotLocalUrl:
559
# make a new one, this format always has to have one.
560
WorkingTreeFormat2().initialize(result)
563
def create_branch(self):
564
"""See BzrDir.create_branch."""
565
return self.open_branch()
567
def create_repository(self, shared=False):
568
"""See BzrDir.create_repository."""
570
raise errors.IncompatibleFormat('shared repository', self._format)
571
return self.open_repository()
573
def create_workingtree(self, revision_id=None):
574
"""See BzrDir.create_workingtree."""
575
# this looks buggy but is not -really-
576
# clone and sprout will have set the revision_id
577
# and that will have set it for us, its only
578
# specific uses of create_workingtree in isolation
579
# that can do wonky stuff here, and that only
580
# happens for creating checkouts, which cannot be
581
# done on this format anyway. So - acceptable wart.
582
result = self.open_workingtree()
583
if revision_id is not None:
584
result.set_last_revision(revision_id)
587
def get_branch_transport(self, branch_format):
588
"""See BzrDir.get_branch_transport()."""
589
if branch_format is None:
590
return self.transport
592
branch_format.get_format_string()
593
except NotImplementedError:
594
return self.transport
595
raise errors.IncompatibleFormat(branch_format, self._format)
597
def get_repository_transport(self, repository_format):
598
"""See BzrDir.get_repository_transport()."""
599
if repository_format is None:
600
return self.transport
602
repository_format.get_format_string()
603
except NotImplementedError:
604
return self.transport
605
raise errors.IncompatibleFormat(repository_format, self._format)
607
def get_workingtree_transport(self, workingtree_format):
608
"""See BzrDir.get_workingtree_transport()."""
609
if workingtree_format is None:
610
return self.transport
612
workingtree_format.get_format_string()
613
except NotImplementedError:
614
return self.transport
615
raise errors.IncompatibleFormat(workingtree_format, self._format)
617
def needs_format_conversion(self, format=None):
618
"""See BzrDir.needs_format_conversion()."""
619
# if the format is not the same as the system default,
620
# an upgrade is needed.
622
format = BzrDirFormat.get_default_format()
623
return not isinstance(self._format, format.__class__)
625
def open_branch(self, unsupported=False):
626
"""See BzrDir.open_branch."""
627
from bzrlib.branch import BzrBranchFormat4
628
format = BzrBranchFormat4()
629
self._check_supported(format, unsupported)
630
return format.open(self, _found=True)
632
def sprout(self, url, revision_id=None, basis=None):
633
"""See BzrDir.sprout()."""
634
from bzrlib.workingtree import WorkingTreeFormat2
636
result = self._format.initialize(url, _cloning=True)
637
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
639
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
640
except errors.NoRepositoryPresent:
643
self.open_branch().sprout(result, revision_id=revision_id)
644
except errors.NotBranchError:
646
# we always want a working tree
647
WorkingTreeFormat2().initialize(result)
651
class BzrDir4(BzrDirPreSplitOut):
652
"""A .bzr version 4 control object.
654
This is a deprecated format and may be removed after sept 2006.
657
def create_repository(self, shared=False):
658
"""See BzrDir.create_repository."""
659
return self._format.repository_format.initialize(self, shared)
661
def needs_format_conversion(self, format=None):
662
"""Format 4 dirs are always in need of conversion."""
665
def open_repository(self):
666
"""See BzrDir.open_repository."""
667
from bzrlib.repository import RepositoryFormat4
668
return RepositoryFormat4().open(self, _found=True)
671
class BzrDir5(BzrDirPreSplitOut):
672
"""A .bzr version 5 control object.
674
This is a deprecated format and may be removed after sept 2006.
677
def open_repository(self):
678
"""See BzrDir.open_repository."""
679
from bzrlib.repository import RepositoryFormat5
680
return RepositoryFormat5().open(self, _found=True)
682
def open_workingtree(self, _unsupported=False):
683
"""See BzrDir.create_workingtree."""
684
from bzrlib.workingtree import WorkingTreeFormat2
685
return WorkingTreeFormat2().open(self, _found=True)
688
class BzrDir6(BzrDirPreSplitOut):
689
"""A .bzr version 6 control object.
691
This is a deprecated format and may be removed after sept 2006.
694
def open_repository(self):
695
"""See BzrDir.open_repository."""
696
from bzrlib.repository import RepositoryFormat6
697
return RepositoryFormat6().open(self, _found=True)
699
def open_workingtree(self, _unsupported=False):
700
"""See BzrDir.create_workingtree."""
701
from bzrlib.workingtree import WorkingTreeFormat2
702
return WorkingTreeFormat2().open(self, _found=True)
705
class BzrDirMeta1(BzrDir):
706
"""A .bzr meta version 1 control object.
708
This is the first control object where the
709
individual aspects are really split out: there are separate repository,
710
workingtree and branch subdirectories and any subset of the three can be
711
present within a BzrDir.
714
def can_convert_format(self):
715
"""See BzrDir.can_convert_format()."""
718
def create_branch(self):
719
"""See BzrDir.create_branch."""
720
from bzrlib.branch import BranchFormat
721
return BranchFormat.get_default_format().initialize(self)
723
def create_repository(self, shared=False):
724
"""See BzrDir.create_repository."""
725
return self._format.repository_format.initialize(self, shared)
727
def create_workingtree(self, revision_id=None):
728
"""See BzrDir.create_workingtree."""
729
from bzrlib.workingtree import WorkingTreeFormat
730
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
732
def get_branch_transport(self, branch_format):
733
"""See BzrDir.get_branch_transport()."""
734
if branch_format is None:
735
return self.transport.clone('branch')
737
branch_format.get_format_string()
738
except NotImplementedError:
739
raise errors.IncompatibleFormat(branch_format, self._format)
741
self.transport.mkdir('branch')
742
except errors.FileExists:
744
return self.transport.clone('branch')
746
def get_repository_transport(self, repository_format):
747
"""See BzrDir.get_repository_transport()."""
748
if repository_format is None:
749
return self.transport.clone('repository')
751
repository_format.get_format_string()
752
except NotImplementedError:
753
raise errors.IncompatibleFormat(repository_format, self._format)
755
self.transport.mkdir('repository')
756
except errors.FileExists:
758
return self.transport.clone('repository')
760
def get_workingtree_transport(self, workingtree_format):
761
"""See BzrDir.get_workingtree_transport()."""
762
if workingtree_format is None:
763
return self.transport.clone('checkout')
765
workingtree_format.get_format_string()
766
except NotImplementedError:
767
raise errors.IncompatibleFormat(workingtree_format, self._format)
769
self.transport.mkdir('checkout')
770
except errors.FileExists:
772
return self.transport.clone('checkout')
774
def needs_format_conversion(self, format=None):
775
"""See BzrDir.needs_format_conversion()."""
777
format = BzrDirFormat.get_default_format()
778
if not isinstance(self._format, format.__class__):
779
# it is not a meta dir format, conversion is needed.
781
# we might want to push this down to the repository?
783
if not isinstance(self.open_repository()._format,
784
format.repository_format.__class__):
785
# the repository needs an upgrade.
787
except errors.NoRepositoryPresent:
789
# currently there are no other possible conversions for meta1 formats.
792
def open_branch(self, unsupported=False):
793
"""See BzrDir.open_branch."""
794
from bzrlib.branch import BranchFormat
795
format = BranchFormat.find_format(self)
796
self._check_supported(format, unsupported)
797
return format.open(self, _found=True)
799
def open_repository(self, unsupported=False):
800
"""See BzrDir.open_repository."""
801
from bzrlib.repository import RepositoryFormat
802
format = RepositoryFormat.find_format(self)
803
self._check_supported(format, unsupported)
804
return format.open(self, _found=True)
806
def open_workingtree(self, unsupported=False):
807
"""See BzrDir.open_workingtree."""
808
from bzrlib.workingtree import WorkingTreeFormat
809
format = WorkingTreeFormat.find_format(self)
810
self._check_supported(format, unsupported)
811
return format.open(self, _found=True)
814
class BzrDirFormat(object):
815
"""An encapsulation of the initialization and open routines for a format.
817
Formats provide three things:
818
* An initialization routine,
822
Formats are placed in an dict by their format string for reference
823
during bzrdir opening. These should be subclasses of BzrDirFormat
826
Once a format is deprecated, just deprecate the initialize and open
827
methods on the format class. Do not deprecate the object, as the
828
object will be created every system load.
831
_default_format = None
832
"""The default format used for new .bzr dirs."""
835
"""The known formats."""
837
_lock_file_name = 'branch-lock'
839
# _lock_class must be set in subclasses to the lock type, typ.
840
# TransportLock or LockDir
843
def find_format(klass, transport):
844
"""Return the format registered for URL."""
846
format_string = transport.get(".bzr/branch-format").read()
847
return klass._formats[format_string]
848
except errors.NoSuchFile:
849
raise errors.NotBranchError(path=transport.base)
851
raise errors.UnknownFormatError(format_string)
854
def get_default_format(klass):
855
"""Return the current default format."""
856
return klass._default_format
858
def get_format_string(self):
859
"""Return the ASCII format string that identifies this format."""
860
raise NotImplementedError(self.get_format_string)
862
def get_converter(self, format=None):
863
"""Return the converter to use to convert bzrdirs needing converts.
865
This returns a bzrlib.bzrdir.Converter object.
867
This should return the best upgrader to step this format towards the
868
current default format. In the case of plugins we can/shouold provide
869
some means for them to extend the range of returnable converters.
871
:param format: Optional format to override the default foramt of the
874
raise NotImplementedError(self.get_converter)
876
def initialize(self, url):
877
"""Create a bzr control dir at this url and return an opened copy."""
878
# Since we don't have a .bzr directory, inherit the
879
# mode from the root directory
880
t = get_transport(url)
881
temp_control = LockableFiles(t, '', TransportLock)
882
temp_control._transport.mkdir('.bzr',
883
# FIXME: RBC 20060121 dont peek under
885
mode=temp_control._dir_mode)
886
file_mode = temp_control._file_mode
888
mutter('created control directory in ' + t.base)
889
control = t.clone('.bzr')
890
utf8_files = [('README',
891
"This is a Bazaar-NG control directory.\n"
892
"Do not change any files in this directory.\n"),
893
('branch-format', self.get_format_string()),
895
# NB: no need to escape relative paths that are url safe.
896
control_files = LockableFiles(control, self._lock_file_name, self._lock_class)
897
control_files.create_lock()
898
control_files.lock_write()
900
for file, content in utf8_files:
901
control_files.put_utf8(file, content)
903
control_files.unlock()
904
return self.open(t, _found=True)
906
def is_supported(self):
907
"""Is this format supported?
909
Supported formats must be initializable and openable.
910
Unsupported formats may not support initialization or committing or
911
some other features depending on the reason for not being supported.
915
def open(self, transport, _found=False):
916
"""Return an instance of this format for the dir transport points at.
918
_found is a private parameter, do not use it.
921
assert isinstance(BzrDirFormat.find_format(transport),
923
return self._open(transport)
925
def _open(self, transport):
926
"""Template method helper for opening BzrDirectories.
928
This performs the actual open and any additional logic or parameter
931
raise NotImplementedError(self._open)
934
def register_format(klass, format):
935
klass._formats[format.get_format_string()] = format
938
def set_default_format(klass, format):
939
klass._default_format = format
942
return self.get_format_string()[:-1]
945
def unregister_format(klass, format):
946
assert klass._formats[format.get_format_string()] is format
947
del klass._formats[format.get_format_string()]
950
class BzrDirFormat4(BzrDirFormat):
953
This format is a combined format for working tree, branch and repository.
955
- Format 1 working trees [always]
956
- Format 4 branches [always]
957
- Format 4 repositories [always]
959
This format is deprecated: it indexes texts using a text it which is
960
removed in format 5; write support for this format has been removed.
963
_lock_class = TransportLock
965
def get_format_string(self):
966
"""See BzrDirFormat.get_format_string()."""
967
return "Bazaar-NG branch, format 0.0.4\n"
969
def get_converter(self, format=None):
970
"""See BzrDirFormat.get_converter()."""
971
# there is one and only one upgrade path here.
972
return ConvertBzrDir4To5()
974
def initialize(self, url):
975
"""Format 4 branches cannot be created."""
976
raise errors.UninitializableFormat(self)
978
def is_supported(self):
979
"""Format 4 is not supported.
981
It is not supported because the model changed from 4 to 5 and the
982
conversion logic is expensive - so doing it on the fly was not
987
def _open(self, transport):
988
"""See BzrDirFormat._open."""
989
return BzrDir4(transport, self)
991
def __return_repository_format(self):
992
"""Circular import protection."""
993
from bzrlib.repository import RepositoryFormat4
994
return RepositoryFormat4(self)
995
repository_format = property(__return_repository_format)
998
class BzrDirFormat5(BzrDirFormat):
999
"""Bzr control format 5.
1001
This format is a combined format for working tree, branch and repository.
1003
- Format 2 working trees [always]
1004
- Format 4 branches [always]
1005
- Format 5 repositories [always]
1006
Unhashed stores in the repository.
1009
_lock_class = TransportLock
1011
def get_format_string(self):
1012
"""See BzrDirFormat.get_format_string()."""
1013
return "Bazaar-NG branch, format 5\n"
1015
def get_converter(self, format=None):
1016
"""See BzrDirFormat.get_converter()."""
1017
# there is one and only one upgrade path here.
1018
return ConvertBzrDir5To6()
1020
def initialize(self, url, _cloning=False):
1021
"""Format 5 dirs always have working tree, branch and repository.
1023
Except when they are being cloned.
1025
from bzrlib.branch import BzrBranchFormat4
1026
from bzrlib.repository import RepositoryFormat5
1027
from bzrlib.workingtree import WorkingTreeFormat2
1028
result = super(BzrDirFormat5, self).initialize(url)
1029
RepositoryFormat5().initialize(result, _internal=True)
1031
BzrBranchFormat4().initialize(result)
1032
WorkingTreeFormat2().initialize(result)
1035
def _open(self, transport):
1036
"""See BzrDirFormat._open."""
1037
return BzrDir5(transport, self)
1039
def __return_repository_format(self):
1040
"""Circular import protection."""
1041
from bzrlib.repository import RepositoryFormat5
1042
return RepositoryFormat5(self)
1043
repository_format = property(__return_repository_format)
1046
class BzrDirFormat6(BzrDirFormat):
1047
"""Bzr control format 6.
1049
This format is a combined format for working tree, branch and repository.
1051
- Format 2 working trees [always]
1052
- Format 4 branches [always]
1053
- Format 6 repositories [always]
1056
_lock_class = TransportLock
1058
def get_format_string(self):
1059
"""See BzrDirFormat.get_format_string()."""
1060
return "Bazaar-NG branch, format 6\n"
1062
def get_converter(self, format=None):
1063
"""See BzrDirFormat.get_converter()."""
1064
# there is one and only one upgrade path here.
1065
return ConvertBzrDir6ToMeta()
1067
def initialize(self, url, _cloning=False):
1068
"""Format 6 dirs always have working tree, branch and repository.
1070
Except when they are being cloned.
1072
from bzrlib.branch import BzrBranchFormat4
1073
from bzrlib.repository import RepositoryFormat6
1074
from bzrlib.workingtree import WorkingTreeFormat2
1075
result = super(BzrDirFormat6, self).initialize(url)
1076
RepositoryFormat6().initialize(result, _internal=True)
1078
BzrBranchFormat4().initialize(result)
1080
WorkingTreeFormat2().initialize(result)
1081
except errors.NotLocalUrl:
1082
# emulate pre-check behaviour for working tree and silently
1087
def _open(self, transport):
1088
"""See BzrDirFormat._open."""
1089
return BzrDir6(transport, self)
1091
def __return_repository_format(self):
1092
"""Circular import protection."""
1093
from bzrlib.repository import RepositoryFormat6
1094
return RepositoryFormat6(self)
1095
repository_format = property(__return_repository_format)
1098
class BzrDirMetaFormat1(BzrDirFormat):
1099
"""Bzr meta control format 1
1101
This is the first format with split out working tree, branch and repository
1104
- Format 3 working trees [optional]
1105
- Format 5 branches [optional]
1106
- Format 7 repositories [optional]
1109
_lock_class = LockDir
1111
def get_converter(self, format=None):
1112
"""See BzrDirFormat.get_converter()."""
1114
format = BzrDirFormat.get_default_format()
1115
if not isinstance(self, format.__class__):
1116
# converting away from metadir is not implemented
1117
raise NotImplementedError(self.get_converter)
1118
return ConvertMetaToMeta(format)
1120
def get_format_string(self):
1121
"""See BzrDirFormat.get_format_string()."""
1122
return "Bazaar-NG meta directory, format 1\n"
1124
def _open(self, transport):
1125
"""See BzrDirFormat._open."""
1126
return BzrDirMeta1(transport, self)
1128
def __return_repository_format(self):
1129
"""Circular import protection."""
1130
if getattr(self, '_repository_format', None):
1131
return self._repository_format
1132
from bzrlib.repository import RepositoryFormat
1133
return RepositoryFormat.get_default_format()
1135
def __set_repository_format(self, value):
1136
"""Allow changint the repository format for metadir formats."""
1137
self._repository_format = value
1139
repository_format = property(__return_repository_format, __set_repository_format)
1142
BzrDirFormat.register_format(BzrDirFormat4())
1143
BzrDirFormat.register_format(BzrDirFormat5())
1144
BzrDirFormat.register_format(BzrDirMetaFormat1())
1145
__default_format = BzrDirFormat6()
1146
BzrDirFormat.register_format(__default_format)
1147
BzrDirFormat.set_default_format(__default_format)
1150
class BzrDirTestProviderAdapter(object):
1151
"""A tool to generate a suite testing multiple bzrdir formats at once.
1153
This is done by copying the test once for each transport and injecting
1154
the transport_server, transport_readonly_server, and bzrdir_format
1155
classes into each copy. Each copy is also given a new id() to make it
1159
def __init__(self, transport_server, transport_readonly_server, formats):
1160
self._transport_server = transport_server
1161
self._transport_readonly_server = transport_readonly_server
1162
self._formats = formats
1164
def adapt(self, test):
1165
result = TestSuite()
1166
for format in self._formats:
1167
new_test = deepcopy(test)
1168
new_test.transport_server = self._transport_server
1169
new_test.transport_readonly_server = self._transport_readonly_server
1170
new_test.bzrdir_format = format
1171
def make_new_test_id():
1172
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1173
return lambda: new_id
1174
new_test.id = make_new_test_id()
1175
result.addTest(new_test)
1179
class ScratchDir(BzrDir6):
1180
"""Special test class: a bzrdir that cleans up itself..
1182
>>> d = ScratchDir()
1183
>>> base = d.transport.base
1186
>>> b.transport.__del__()
1191
def __init__(self, files=[], dirs=[], transport=None):
1192
"""Make a test branch.
1194
This creates a temporary directory and runs init-tree in it.
1196
If any files are listed, they are created in the working copy.
1198
if transport is None:
1199
transport = bzrlib.transport.local.ScratchTransport()
1200
# local import for scope restriction
1201
BzrDirFormat6().initialize(transport.base)
1202
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1203
self.create_repository()
1204
self.create_branch()
1205
self.create_workingtree()
1207
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1209
# BzrBranch creates a clone to .bzr and then forgets about the
1210
# original transport. A ScratchTransport() deletes itself and
1211
# everything underneath it when it goes away, so we need to
1212
# grab a local copy to prevent that from happening
1213
self._transport = transport
1216
self._transport.mkdir(d)
1219
self._transport.put(f, 'content of %s' % f)
1223
>>> orig = ScratchDir(files=["file1", "file2"])
1224
>>> os.listdir(orig.base)
1225
[u'.bzr', u'file1', u'file2']
1226
>>> clone = orig.clone()
1227
>>> if os.name != 'nt':
1228
... os.path.samefile(orig.base, clone.base)
1230
... orig.base == clone.base
1233
>>> os.listdir(clone.base)
1234
[u'.bzr', u'file1', u'file2']
1236
from shutil import copytree
1237
from bzrlib.osutils import mkdtemp
1240
copytree(self.base, base, symlinks=True)
1242
transport=bzrlib.transport.local.ScratchTransport(base))
1245
class Converter(object):
1246
"""Converts a disk format object from one format to another."""
1248
def convert(self, to_convert, pb):
1249
"""Perform the conversion of to_convert, giving feedback via pb.
1251
:param to_convert: The disk object to convert.
1252
:param pb: a progress bar to use for progress information.
1255
def step(self, message):
1256
"""Update the pb by a step."""
1258
self.pb.update(message, self.count, self.total)
1261
class ConvertBzrDir4To5(Converter):
1262
"""Converts format 4 bzr dirs to format 5."""
1265
super(ConvertBzrDir4To5, self).__init__()
1266
self.converted_revs = set()
1267
self.absent_revisions = set()
1271
def convert(self, to_convert, pb):
1272
"""See Converter.convert()."""
1273
self.bzrdir = to_convert
1275
self.pb.note('starting upgrade from format 4 to 5')
1276
if isinstance(self.bzrdir.transport, LocalTransport):
1277
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1278
self._convert_to_weaves()
1279
return BzrDir.open(self.bzrdir.root_transport.base)
1281
def _convert_to_weaves(self):
1282
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1285
stat = self.bzrdir.transport.stat('weaves')
1286
if not S_ISDIR(stat.st_mode):
1287
self.bzrdir.transport.delete('weaves')
1288
self.bzrdir.transport.mkdir('weaves')
1289
except errors.NoSuchFile:
1290
self.bzrdir.transport.mkdir('weaves')
1291
self.inv_weave = Weave('inventory')
1292
# holds in-memory weaves for all files
1293
self.text_weaves = {}
1294
self.bzrdir.transport.delete('branch-format')
1295
self.branch = self.bzrdir.open_branch()
1296
self._convert_working_inv()
1297
rev_history = self.branch.revision_history()
1298
# to_read is a stack holding the revisions we still need to process;
1299
# appending to it adds new highest-priority revisions
1300
self.known_revisions = set(rev_history)
1301
self.to_read = rev_history[-1:]
1303
rev_id = self.to_read.pop()
1304
if (rev_id not in self.revisions
1305
and rev_id not in self.absent_revisions):
1306
self._load_one_rev(rev_id)
1308
to_import = self._make_order()
1309
for i, rev_id in enumerate(to_import):
1310
self.pb.update('converting revision', i, len(to_import))
1311
self._convert_one_rev(rev_id)
1313
self._write_all_weaves()
1314
self._write_all_revs()
1315
self.pb.note('upgraded to weaves:')
1316
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1317
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1318
self.pb.note(' %6d texts', self.text_count)
1319
self._cleanup_spare_files_after_format4()
1320
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1322
def _cleanup_spare_files_after_format4(self):
1323
# FIXME working tree upgrade foo.
1324
for n in 'merged-patches', 'pending-merged-patches':
1326
## assert os.path.getsize(p) == 0
1327
self.bzrdir.transport.delete(n)
1328
except errors.NoSuchFile:
1330
self.bzrdir.transport.delete_tree('inventory-store')
1331
self.bzrdir.transport.delete_tree('text-store')
1333
def _convert_working_inv(self):
1334
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1335
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1336
# FIXME inventory is a working tree change.
1337
self.branch.control_files.put('inventory', new_inv_xml)
1339
def _write_all_weaves(self):
1340
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1341
weave_transport = self.bzrdir.transport.clone('weaves')
1342
weaves = WeaveStore(weave_transport, prefixed=False)
1343
transaction = PassThroughTransaction()
1345
controlweaves.put_weave('inventory', self.inv_weave, transaction)
1348
for file_id, file_weave in self.text_weaves.items():
1349
self.pb.update('writing weave', i, len(self.text_weaves))
1350
weaves.put_weave(file_id, file_weave, transaction)
1355
def _write_all_revs(self):
1356
"""Write all revisions out in new form."""
1357
self.bzrdir.transport.delete_tree('revision-store')
1358
self.bzrdir.transport.mkdir('revision-store')
1359
revision_transport = self.bzrdir.transport.clone('revision-store')
1361
revision_store = TextStore(revision_transport,
1365
for i, rev_id in enumerate(self.converted_revs):
1366
self.pb.update('write revision', i, len(self.converted_revs))
1367
rev_tmp = StringIO()
1368
serializer_v5.write_revision(self.revisions[rev_id], rev_tmp)
1370
revision_store.add(rev_tmp, rev_id)
1374
def _load_one_rev(self, rev_id):
1375
"""Load a revision object into memory.
1377
Any parents not either loaded or abandoned get queued to be
1379
self.pb.update('loading revision',
1380
len(self.revisions),
1381
len(self.known_revisions))
1382
if not self.branch.repository.revision_store.has_id(rev_id):
1384
self.pb.note('revision {%s} not present in branch; '
1385
'will be converted as a ghost',
1387
self.absent_revisions.add(rev_id)
1389
rev_xml = self.branch.repository.revision_store.get(rev_id).read()
1390
rev = serializer_v4.read_revision_from_string(rev_xml)
1391
for parent_id in rev.parent_ids:
1392
self.known_revisions.add(parent_id)
1393
self.to_read.append(parent_id)
1394
self.revisions[rev_id] = rev
1396
def _load_old_inventory(self, rev_id):
1397
assert rev_id not in self.converted_revs
1398
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1399
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1400
rev = self.revisions[rev_id]
1401
if rev.inventory_sha1:
1402
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1403
'inventory sha mismatch for {%s}' % rev_id
1406
def _load_updated_inventory(self, rev_id):
1407
assert rev_id in self.converted_revs
1408
inv_xml = self.inv_weave.get_text(rev_id)
1409
inv = serializer_v5.read_inventory_from_string(inv_xml)
1412
def _convert_one_rev(self, rev_id):
1413
"""Convert revision and all referenced objects to new format."""
1414
rev = self.revisions[rev_id]
1415
inv = self._load_old_inventory(rev_id)
1416
present_parents = [p for p in rev.parent_ids
1417
if p not in self.absent_revisions]
1418
self._convert_revision_contents(rev, inv, present_parents)
1419
self._store_new_weave(rev, inv, present_parents)
1420
self.converted_revs.add(rev_id)
1422
def _store_new_weave(self, rev, inv, present_parents):
1423
# the XML is now updated with text versions
1427
if ie.kind == 'root_directory':
1429
assert hasattr(ie, 'revision'), \
1430
'no revision on {%s} in {%s}' % \
1431
(file_id, rev.revision_id)
1432
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1433
new_inv_sha1 = sha_string(new_inv_xml)
1434
self.inv_weave.add(rev.revision_id,
1436
new_inv_xml.splitlines(True),
1438
rev.inventory_sha1 = new_inv_sha1
1440
def _convert_revision_contents(self, rev, inv, present_parents):
1441
"""Convert all the files within a revision.
1443
Also upgrade the inventory to refer to the text revision ids."""
1444
rev_id = rev.revision_id
1445
mutter('converting texts of revision {%s}',
1447
parent_invs = map(self._load_updated_inventory, present_parents)
1450
self._convert_file_version(rev, ie, parent_invs)
1452
def _convert_file_version(self, rev, ie, parent_invs):
1453
"""Convert one version of one file.
1455
The file needs to be added into the weave if it is a merge
1456
of >=2 parents or if it's changed from its parent.
1458
if ie.kind == 'root_directory':
1460
file_id = ie.file_id
1461
rev_id = rev.revision_id
1462
w = self.text_weaves.get(file_id)
1465
self.text_weaves[file_id] = w
1466
text_changed = False
1467
previous_entries = ie.find_previous_heads(parent_invs, w)
1468
for old_revision in previous_entries:
1469
# if this fails, its a ghost ?
1470
assert old_revision in self.converted_revs
1471
self.snapshot_ie(previous_entries, ie, w, rev_id)
1473
assert getattr(ie, 'revision', None) is not None
1475
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1476
# TODO: convert this logic, which is ~= snapshot to
1477
# a call to:. This needs the path figured out. rather than a work_tree
1478
# a v4 revision_tree can be given, or something that looks enough like
1479
# one to give the file content to the entry if it needs it.
1480
# and we need something that looks like a weave store for snapshot to
1482
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1483
if len(previous_revisions) == 1:
1484
previous_ie = previous_revisions.values()[0]
1485
if ie._unchanged(previous_ie):
1486
ie.revision = previous_ie.revision
1488
parent_indexes = map(w.lookup, previous_revisions)
1490
text = self.branch.repository.text_store.get(ie.text_id)
1491
file_lines = text.readlines()
1492
assert sha_strings(file_lines) == ie.text_sha1
1493
assert sum(map(len, file_lines)) == ie.text_size
1494
w.add(rev_id, parent_indexes, file_lines, ie.text_sha1)
1495
self.text_count += 1
1497
w.add(rev_id, parent_indexes, [], None)
1498
ie.revision = rev_id
1500
def _make_order(self):
1501
"""Return a suitable order for importing revisions.
1503
The order must be such that an revision is imported after all
1504
its (present) parents.
1506
todo = set(self.revisions.keys())
1507
done = self.absent_revisions.copy()
1510
# scan through looking for a revision whose parents
1512
for rev_id in sorted(list(todo)):
1513
rev = self.revisions[rev_id]
1514
parent_ids = set(rev.parent_ids)
1515
if parent_ids.issubset(done):
1516
# can take this one now
1517
order.append(rev_id)
1523
class ConvertBzrDir5To6(Converter):
1524
"""Converts format 5 bzr dirs to format 6."""
1526
def convert(self, to_convert, pb):
1527
"""See Converter.convert()."""
1528
self.bzrdir = to_convert
1530
self.pb.note('starting upgrade from format 5 to 6')
1531
self._convert_to_prefixed()
1532
return BzrDir.open(self.bzrdir.root_transport.base)
1534
def _convert_to_prefixed(self):
1535
from bzrlib.store import hash_prefix
1536
self.bzrdir.transport.delete('branch-format')
1537
for store_name in ["weaves", "revision-store"]:
1538
self.pb.note("adding prefixes to %s" % store_name)
1539
store_transport = self.bzrdir.transport.clone(store_name)
1540
for filename in store_transport.list_dir('.'):
1541
if (filename.endswith(".weave") or
1542
filename.endswith(".gz") or
1543
filename.endswith(".sig")):
1544
file_id = os.path.splitext(filename)[0]
1547
prefix_dir = hash_prefix(file_id)
1548
# FIXME keep track of the dirs made RBC 20060121
1550
store_transport.move(filename, prefix_dir + '/' + filename)
1551
except errors.NoSuchFile: # catches missing dirs strangely enough
1552
store_transport.mkdir(prefix_dir)
1553
store_transport.move(filename, prefix_dir + '/' + filename)
1554
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1557
class ConvertBzrDir6ToMeta(Converter):
1558
"""Converts format 6 bzr dirs to metadirs."""
1560
def convert(self, to_convert, pb):
1561
"""See Converter.convert()."""
1562
self.bzrdir = to_convert
1565
self.total = 20 # the steps we know about
1566
self.garbage_inventories = []
1568
self.pb.note('starting upgrade from format 6 to metadir')
1569
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1570
# its faster to move specific files around than to open and use the apis...
1571
# first off, nuke ancestry.weave, it was never used.
1573
self.step('Removing ancestry.weave')
1574
self.bzrdir.transport.delete('ancestry.weave')
1575
except errors.NoSuchFile:
1577
# find out whats there
1578
self.step('Finding branch files')
1579
last_revision = self.bzrdir.open_workingtree().last_revision()
1580
bzrcontents = self.bzrdir.transport.list_dir('.')
1581
for name in bzrcontents:
1582
if name.startswith('basis-inventory.'):
1583
self.garbage_inventories.append(name)
1584
# create new directories for repository, working tree and branch
1585
dir_mode = self.bzrdir._control_files._dir_mode
1586
self.file_mode = self.bzrdir._control_files._file_mode
1587
repository_names = [('inventory.weave', True),
1588
('revision-store', True),
1590
self.step('Upgrading repository ')
1591
self.bzrdir.transport.mkdir('repository', mode=dir_mode)
1592
self.make_lock('repository')
1593
# we hard code the formats here because we are converting into
1594
# the meta format. The meta format upgrader can take this to a
1595
# future format within each component.
1596
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1597
for entry in repository_names:
1598
self.move_entry('repository', entry)
1600
self.step('Upgrading branch ')
1601
self.bzrdir.transport.mkdir('branch', mode=dir_mode)
1602
self.make_lock('branch')
1603
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1604
branch_files = [('revision-history', True),
1605
('branch-name', True),
1607
for entry in branch_files:
1608
self.move_entry('branch', entry)
1610
self.step('Upgrading working tree')
1611
self.bzrdir.transport.mkdir('checkout', mode=dir_mode)
1612
self.make_lock('checkout')
1613
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1614
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1615
checkout_files = [('pending-merges', True),
1616
('inventory', True),
1617
('stat-cache', False)]
1618
for entry in checkout_files:
1619
self.move_entry('checkout', entry)
1620
if last_revision is not None:
1621
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1623
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1624
return BzrDir.open(self.bzrdir.root_transport.base)
1626
def make_lock(self, name):
1627
"""Make a lock for the new control dir name."""
1628
self.step('Make %s lock' % name)
1629
self.bzrdir.transport.put('%s/lock' % name, StringIO(), mode=self.file_mode)
1631
def move_entry(self, new_dir, entry):
1632
"""Move then entry name into new_dir."""
1634
mandatory = entry[1]
1635
self.step('Moving %s' % name)
1637
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1638
except errors.NoSuchFile:
1642
def put_format(self, dirname, format):
1643
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1646
class ConvertMetaToMeta(Converter):
1647
"""Converts the components of metadirs."""
1649
def __init__(self, target_format):
1650
"""Create a metadir to metadir converter.
1652
:param target_format: The final metadir format that is desired.
1654
self.target_format = target_format
1656
def convert(self, to_convert, pb):
1657
"""See Converter.convert()."""
1658
self.bzrdir = to_convert
1662
self.step('checking repository format')
1664
repo = self.bzrdir.open_repository()
1665
except errors.NoRepositoryPresent:
1668
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1669
from bzrlib.repository import CopyConverter
1670
self.pb.note('starting repository conversion')
1671
converter = CopyConverter(self.target_format.repository_format)
1672
converter.convert(repo, pb)