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.revision.text import TextRevisionStore
41
from bzrlib.store.text import TextStore
42
from bzrlib.store.versioned import WeaveStore
43
from bzrlib.symbol_versioning import *
44
from bzrlib.trace import mutter
45
from bzrlib.transactions import WriteTransaction
46
from bzrlib.transport import get_transport, urlunescape
47
from bzrlib.transport.local import LocalTransport
48
from bzrlib.weave import 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."""
70
def _check_supported(format, allow_unsupported):
71
"""Check whether format is a supported format.
73
If allow_unsupported is True, this is a no-op.
75
if not allow_unsupported and not format.is_supported():
76
# see open_downlevel to open legacy branches.
77
raise errors.UnsupportedFormatError(
78
'sorry, format %s not supported' % format,
79
['use a different bzr version',
80
'or remove the .bzr directory'
81
' and "bzr init" again'])
83
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
84
"""Clone this bzrdir and its contents to url verbatim.
86
If urls last component does not exist, it will be created.
88
if revision_id is not None, then the clone operation may tune
89
itself to download less data.
90
:param force_new_repo: Do not use a shared repository for the target
91
even if one is available.
94
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
95
result = self._format.initialize(url)
97
local_repo = self.find_repository()
98
except errors.NoRepositoryPresent:
101
# may need to copy content in
103
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
106
result_repo = result.find_repository()
107
# fetch content this dir needs.
109
# XXX FIXME RBC 20060214 need tests for this when the basis
111
result_repo.fetch(basis_repo, revision_id=revision_id)
112
result_repo.fetch(local_repo, revision_id=revision_id)
113
except errors.NoRepositoryPresent:
114
# needed to make one anyway.
115
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
116
# 1 if there is a branch present
117
# make sure its content is available in the target repository
120
self.open_branch().clone(result, revision_id=revision_id)
121
except errors.NotBranchError:
124
self.open_workingtree().clone(result, basis=basis_tree)
125
except (errors.NoWorkingTree, errors.NotLocalUrl):
129
def _get_basis_components(self, basis):
130
"""Retrieve the basis components that are available at basis."""
132
return None, None, None
134
basis_tree = basis.open_workingtree()
135
basis_branch = basis_tree.branch
136
basis_repo = basis_branch.repository
137
except (errors.NoWorkingTree, errors.NotLocalUrl):
140
basis_branch = basis.open_branch()
141
basis_repo = basis_branch.repository
142
except errors.NotBranchError:
145
basis_repo = basis.open_repository()
146
except errors.NoRepositoryPresent:
148
return basis_repo, basis_branch, basis_tree
150
def _make_tail(self, url):
151
segments = url.split('/')
152
if segments and segments[-1] not in ('', '.'):
153
parent = '/'.join(segments[:-1])
154
t = bzrlib.transport.get_transport(parent)
156
t.mkdir(segments[-1])
157
except errors.FileExists:
161
def create(cls, base):
162
"""Create a new BzrDir at the url 'base'.
164
This will call the current default formats initialize with base
165
as the only parameter.
167
If you need a specific format, consider creating an instance
168
of that and calling initialize().
170
if cls is not BzrDir:
171
raise AssertionError("BzrDir.create always creates the default format, "
172
"not one of %r" % cls)
173
segments = base.split('/')
174
if segments and segments[-1] not in ('', '.'):
175
parent = '/'.join(segments[:-1])
176
t = bzrlib.transport.get_transport(parent)
178
t.mkdir(segments[-1])
179
except errors.FileExists:
181
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
183
def create_branch(self):
184
"""Create a branch in this BzrDir.
186
The bzrdirs format will control what branch format is created.
187
For more control see BranchFormatXX.create(a_bzrdir).
189
raise NotImplementedError(self.create_branch)
192
def create_branch_and_repo(base, force_new_repo=False):
193
"""Create a new BzrDir, Branch and Repository at the url 'base'.
195
This will use the current default BzrDirFormat, and use whatever
196
repository format that that uses via bzrdir.create_branch and
197
create_repository. If a shared repository is available that is used
200
The created Branch object is returned.
202
:param base: The URL to create the branch at.
203
:param force_new_repo: If True a new repository is always created.
205
bzrdir = BzrDir.create(base)
206
bzrdir._find_or_create_repository(force_new_repo)
207
return bzrdir.create_branch()
209
def _find_or_create_repository(self, force_new_repo):
210
"""Create a new repository if needed, returning the repository."""
212
return self.create_repository()
214
return self.find_repository()
215
except errors.NoRepositoryPresent:
216
return self.create_repository()
219
def create_branch_convenience(base, force_new_repo=False,
220
force_new_tree=None, format=None):
221
"""Create a new BzrDir, Branch and Repository at the url 'base'.
223
This is a convenience function - it will use an existing repository
224
if possible, can be told explicitly whether to create a working tree or
227
This will use the current default BzrDirFormat, and use whatever
228
repository format that that uses via bzrdir.create_branch and
229
create_repository. If a shared repository is available that is used
230
preferentially. Whatever repository is used, its tree creation policy
233
The created Branch object is returned.
234
If a working tree cannot be made due to base not being a file:// url,
235
no error is raised unless force_new_tree is True, in which case no
236
data is created on disk and NotLocalUrl is raised.
238
:param base: The URL to create the branch at.
239
:param force_new_repo: If True a new repository is always created.
240
:param force_new_tree: If True or False force creation of a tree or
241
prevent such creation respectively.
242
:param format: Override for the for the bzrdir format to create
245
# check for non local urls
246
t = get_transport(safe_unicode(base))
247
if not isinstance(t, LocalTransport):
248
raise errors.NotLocalUrl(base)
250
bzrdir = BzrDir.create(base)
252
bzrdir = format.initialize(base)
253
repo = bzrdir._find_or_create_repository(force_new_repo)
254
result = bzrdir.create_branch()
255
if force_new_tree or (repo.make_working_trees() and
256
force_new_tree is None):
258
bzrdir.create_workingtree()
259
except errors.NotLocalUrl:
264
def create_repository(base, shared=False):
265
"""Create a new BzrDir and Repository at the url 'base'.
267
This will use the current default BzrDirFormat, and use whatever
268
repository format that that uses for bzrdirformat.create_repository.
270
;param shared: Create a shared repository rather than a standalone
272
The Repository object is returned.
274
This must be overridden as an instance method in child classes, where
275
it should take no parameters and construct whatever repository format
276
that child class desires.
278
bzrdir = BzrDir.create(base)
279
return bzrdir.create_repository()
282
def create_standalone_workingtree(base):
283
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
285
'base' must be a local path or a file:// url.
287
This will use the current default BzrDirFormat, and use whatever
288
repository format that that uses for bzrdirformat.create_workingtree,
289
create_branch and create_repository.
291
The WorkingTree object is returned.
293
t = get_transport(safe_unicode(base))
294
if not isinstance(t, LocalTransport):
295
raise errors.NotLocalUrl(base)
296
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
297
force_new_repo=True).bzrdir
298
return bzrdir.create_workingtree()
300
def create_workingtree(self, revision_id=None):
301
"""Create a working tree at this BzrDir.
303
revision_id: create it as of this revision id.
305
raise NotImplementedError(self.create_workingtree)
307
def find_repository(self):
308
"""Find the repository that should be used for a_bzrdir.
310
This does not require a branch as we use it to find the repo for
311
new branches as well as to hook existing branches up to their
315
return self.open_repository()
316
except errors.NoRepositoryPresent:
318
next_transport = self.root_transport.clone('..')
321
found_bzrdir = BzrDir.open_containing_from_transport(
323
except errors.NotBranchError:
324
raise errors.NoRepositoryPresent(self)
326
repository = found_bzrdir.open_repository()
327
except errors.NoRepositoryPresent:
328
next_transport = found_bzrdir.root_transport.clone('..')
330
if ((found_bzrdir.root_transport.base ==
331
self.root_transport.base) or repository.is_shared()):
334
raise errors.NoRepositoryPresent(self)
335
raise errors.NoRepositoryPresent(self)
337
def get_branch_transport(self, branch_format):
338
"""Get the transport for use by branch format in this BzrDir.
340
Note that bzr dirs that do not support format strings will raise
341
IncompatibleFormat if the branch format they are given has
342
a format string, and vice verca.
344
If branch_format is None, the transport is returned with no
345
checking. if it is not None, then the returned transport is
346
guaranteed to point to an existing directory ready for use.
348
raise NotImplementedError(self.get_branch_transport)
350
def get_repository_transport(self, repository_format):
351
"""Get the transport for use by repository format in this BzrDir.
353
Note that bzr dirs that do not support format strings will raise
354
IncompatibleFormat if the repository format they are given has
355
a format string, and vice verca.
357
If repository_format is None, the transport is returned with no
358
checking. if it is not None, then the returned transport is
359
guaranteed to point to an existing directory ready for use.
361
raise NotImplementedError(self.get_repository_transport)
363
def get_workingtree_transport(self, tree_format):
364
"""Get the transport for use by workingtree format in this BzrDir.
366
Note that bzr dirs that do not support format strings will raise
367
IncompatibleFormat if the workingtree format they are given has
368
a format string, and vice verca.
370
If workingtree_format is None, the transport is returned with no
371
checking. if it is not None, then the returned transport is
372
guaranteed to point to an existing directory ready for use.
374
raise NotImplementedError(self.get_workingtree_transport)
376
def __init__(self, _transport, _format):
377
"""Initialize a Bzr control dir object.
379
Only really common logic should reside here, concrete classes should be
380
made with varying behaviours.
382
:param _format: the format that is creating this BzrDir instance.
383
:param _transport: the transport this dir is based at.
385
self._format = _format
386
self.transport = _transport.clone('.bzr')
387
self.root_transport = _transport
389
def needs_format_conversion(self, format=None):
390
"""Return true if this bzrdir needs convert_format run on it.
392
For instance, if the repository format is out of date but the
393
branch and working tree are not, this should return True.
395
:param format: Optional parameter indicating a specific desired
396
format we plan to arrive at.
398
raise NotImplementedError(self.needs_format_conversion)
401
def open_unsupported(base):
402
"""Open a branch which is not supported."""
403
return BzrDir.open(base, _unsupported=True)
406
def open(base, _unsupported=False):
407
"""Open an existing bzrdir, rooted at 'base' (url)
409
_unsupported is a private parameter to the BzrDir class.
411
t = get_transport(base)
412
mutter("trying to open %r with transport %r", base, t)
413
format = BzrDirFormat.find_format(t)
414
BzrDir._check_supported(format, _unsupported)
415
return format.open(t, _found=True)
417
def open_branch(self, unsupported=False):
418
"""Open the branch object at this BzrDir if one is present.
420
If unsupported is True, then no longer supported branch formats can
423
TODO: static convenience version of this?
425
raise NotImplementedError(self.open_branch)
428
def open_containing(url):
429
"""Open an existing branch which contains url.
431
:param url: url to search from.
432
See open_containing_from_transport for more detail.
434
return BzrDir.open_containing_from_transport(get_transport(url))
437
def open_containing_from_transport(a_transport):
438
"""Open an existing branch which contains a_transport.base
440
This probes for a branch at a_transport, and searches upwards from there.
442
Basically we keep looking up until we find the control directory or
443
run into the root. If there isn't one, raises NotBranchError.
444
If there is one and it is either an unrecognised format or an unsupported
445
format, UnknownFormatError or UnsupportedFormatError are raised.
446
If there is one, it is returned, along with the unused portion of url.
448
:return: The BzrDir that contains the path, and a Unicode path
449
for the rest of the URL.
451
# this gets the normalised url back. I.e. '.' -> the full path.
452
url = a_transport.base
455
format = BzrDirFormat.find_format(a_transport)
456
BzrDir._check_supported(format, False)
457
return format.open(a_transport), urlunescape(a_transport.relpath(url))
458
except errors.NotBranchError, e:
459
mutter('not a branch in: %r %s', a_transport.base, e)
460
new_t = a_transport.clone('..')
461
if new_t.base == a_transport.base:
462
# reached the root, whatever that may be
463
raise errors.NotBranchError(path=url)
466
def open_repository(self, _unsupported=False):
467
"""Open the repository object at this BzrDir if one is present.
469
This will not follow the Branch object pointer - its strictly a direct
470
open facility. Most client code should use open_branch().repository to
473
_unsupported is a private parameter, not part of the api.
474
TODO: static convenience version of this?
476
raise NotImplementedError(self.open_repository)
478
def open_workingtree(self, _unsupported=False):
479
"""Open the workingtree object at this BzrDir if one is present.
481
TODO: static convenience version of this?
483
raise NotImplementedError(self.open_workingtree)
485
def has_branch(self):
486
"""Tell if this bzrdir contains a branch.
488
Note: if you're going to open the branch, you should just go ahead
489
and try, and not ask permission first. (This method just opens the
490
branch and discards it, and that's somewhat expensive.)
495
except errors.NotBranchError:
498
def has_workingtree(self):
499
"""Tell if this bzrdir contains a working tree.
501
This will still raise an exception if the bzrdir has a workingtree that
502
is remote & inaccessible.
504
Note: if you're going to open the working tree, you should just go ahead
505
and try, and not ask permission first. (This method just opens the
506
workingtree and discards it, and that's somewhat expensive.)
509
self.open_workingtree()
511
except errors.NoWorkingTree:
514
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
515
"""Create a copy of this bzrdir prepared for use as a new line of
518
If urls last component does not exist, it will be created.
520
Attributes related to the identity of the source branch like
521
branch nickname will be cleaned, a working tree is created
522
whether one existed before or not; and a local branch is always
525
if revision_id is not None, then the clone operation may tune
526
itself to download less data.
529
result = self._format.initialize(url)
530
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
532
source_branch = self.open_branch()
533
source_repository = source_branch.repository
534
except errors.NotBranchError:
537
source_repository = self.open_repository()
538
except errors.NoRepositoryPresent:
539
# copy the entire basis one if there is one
540
# but there is no repository.
541
source_repository = basis_repo
546
result_repo = result.find_repository()
547
except errors.NoRepositoryPresent:
549
if source_repository is None and result_repo is not None:
551
elif source_repository is None and result_repo is None:
552
# no repo available, make a new one
553
result.create_repository()
554
elif source_repository is not None and result_repo is None:
555
# have soure, and want to make a new target repo
556
source_repository.clone(result,
557
revision_id=revision_id,
560
# fetch needed content into target.
562
# XXX FIXME RBC 20060214 need tests for this when the basis
564
result_repo.fetch(basis_repo, revision_id=revision_id)
565
result_repo.fetch(source_repository, revision_id=revision_id)
566
if source_branch is not None:
567
source_branch.sprout(result, revision_id=revision_id)
569
result.create_branch()
570
# TODO: jam 20060426 we probably need a test in here in the
571
# case that the newly sprouted branch is a remote one
572
if result_repo is None or result_repo.make_working_trees():
573
result.create_workingtree()
577
class BzrDirPreSplitOut(BzrDir):
578
"""A common class for the all-in-one formats."""
580
def __init__(self, _transport, _format):
581
"""See BzrDir.__init__."""
582
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
583
assert self._format._lock_class == TransportLock
584
assert self._format._lock_file_name == 'branch-lock'
585
self._control_files = LockableFiles(self.get_branch_transport(None),
586
self._format._lock_file_name,
587
self._format._lock_class)
589
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
590
"""See BzrDir.clone()."""
591
from bzrlib.workingtree import WorkingTreeFormat2
593
result = self._format._initialize_for_clone(url)
594
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
595
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
596
self.open_branch().clone(result, revision_id=revision_id)
598
self.open_workingtree().clone(result, basis=basis_tree)
599
except errors.NotLocalUrl:
600
# make a new one, this format always has to have one.
602
WorkingTreeFormat2().initialize(result)
603
except errors.NotLocalUrl:
604
# but we canot do it for remote trees.
608
def create_branch(self):
609
"""See BzrDir.create_branch."""
610
return self.open_branch()
612
def create_repository(self, shared=False):
613
"""See BzrDir.create_repository."""
615
raise errors.IncompatibleFormat('shared repository', self._format)
616
return self.open_repository()
618
def create_workingtree(self, revision_id=None):
619
"""See BzrDir.create_workingtree."""
620
# this looks buggy but is not -really-
621
# clone and sprout will have set the revision_id
622
# and that will have set it for us, its only
623
# specific uses of create_workingtree in isolation
624
# that can do wonky stuff here, and that only
625
# happens for creating checkouts, which cannot be
626
# done on this format anyway. So - acceptable wart.
627
result = self.open_workingtree()
628
if revision_id is not None:
629
result.set_last_revision(revision_id)
632
def get_branch_transport(self, branch_format):
633
"""See BzrDir.get_branch_transport()."""
634
if branch_format is None:
635
return self.transport
637
branch_format.get_format_string()
638
except NotImplementedError:
639
return self.transport
640
raise errors.IncompatibleFormat(branch_format, self._format)
642
def get_repository_transport(self, repository_format):
643
"""See BzrDir.get_repository_transport()."""
644
if repository_format is None:
645
return self.transport
647
repository_format.get_format_string()
648
except NotImplementedError:
649
return self.transport
650
raise errors.IncompatibleFormat(repository_format, self._format)
652
def get_workingtree_transport(self, workingtree_format):
653
"""See BzrDir.get_workingtree_transport()."""
654
if workingtree_format is None:
655
return self.transport
657
workingtree_format.get_format_string()
658
except NotImplementedError:
659
return self.transport
660
raise errors.IncompatibleFormat(workingtree_format, self._format)
662
def needs_format_conversion(self, format=None):
663
"""See BzrDir.needs_format_conversion()."""
664
# if the format is not the same as the system default,
665
# an upgrade is needed.
667
format = BzrDirFormat.get_default_format()
668
return not isinstance(self._format, format.__class__)
670
def open_branch(self, unsupported=False):
671
"""See BzrDir.open_branch."""
672
from bzrlib.branch import BzrBranchFormat4
673
format = BzrBranchFormat4()
674
self._check_supported(format, unsupported)
675
return format.open(self, _found=True)
677
def sprout(self, url, revision_id=None, basis=None):
678
"""See BzrDir.sprout()."""
679
from bzrlib.workingtree import WorkingTreeFormat2
681
result = self._format._initialize_for_clone(url)
682
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
684
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
685
except errors.NoRepositoryPresent:
688
self.open_branch().sprout(result, revision_id=revision_id)
689
except errors.NotBranchError:
691
# we always want a working tree
692
WorkingTreeFormat2().initialize(result)
696
class BzrDir4(BzrDirPreSplitOut):
697
"""A .bzr version 4 control object.
699
This is a deprecated format and may be removed after sept 2006.
702
def create_repository(self, shared=False):
703
"""See BzrDir.create_repository."""
704
return self._format.repository_format.initialize(self, shared)
706
def needs_format_conversion(self, format=None):
707
"""Format 4 dirs are always in need of conversion."""
710
def open_repository(self):
711
"""See BzrDir.open_repository."""
712
from bzrlib.repository import RepositoryFormat4
713
return RepositoryFormat4().open(self, _found=True)
716
class BzrDir5(BzrDirPreSplitOut):
717
"""A .bzr version 5 control object.
719
This is a deprecated format and may be removed after sept 2006.
722
def open_repository(self):
723
"""See BzrDir.open_repository."""
724
from bzrlib.repository import RepositoryFormat5
725
return RepositoryFormat5().open(self, _found=True)
727
def open_workingtree(self, _unsupported=False):
728
"""See BzrDir.create_workingtree."""
729
from bzrlib.workingtree import WorkingTreeFormat2
730
return WorkingTreeFormat2().open(self, _found=True)
733
class BzrDir6(BzrDirPreSplitOut):
734
"""A .bzr version 6 control object.
736
This is a deprecated format and may be removed after sept 2006.
739
def open_repository(self):
740
"""See BzrDir.open_repository."""
741
from bzrlib.repository import RepositoryFormat6
742
return RepositoryFormat6().open(self, _found=True)
744
def open_workingtree(self, _unsupported=False):
745
"""See BzrDir.create_workingtree."""
746
from bzrlib.workingtree import WorkingTreeFormat2
747
return WorkingTreeFormat2().open(self, _found=True)
750
class BzrDirMeta1(BzrDir):
751
"""A .bzr meta version 1 control object.
753
This is the first control object where the
754
individual aspects are really split out: there are separate repository,
755
workingtree and branch subdirectories and any subset of the three can be
756
present within a BzrDir.
759
def can_convert_format(self):
760
"""See BzrDir.can_convert_format()."""
763
def create_branch(self):
764
"""See BzrDir.create_branch."""
765
from bzrlib.branch import BranchFormat
766
return BranchFormat.get_default_format().initialize(self)
768
def create_repository(self, shared=False):
769
"""See BzrDir.create_repository."""
770
return self._format.repository_format.initialize(self, shared)
772
def create_workingtree(self, revision_id=None):
773
"""See BzrDir.create_workingtree."""
774
from bzrlib.workingtree import WorkingTreeFormat
775
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
777
def _get_mkdir_mode(self):
778
"""Figure out the mode to use when creating a bzrdir subdir."""
779
temp_control = LockableFiles(self.transport, '', TransportLock)
780
return temp_control._dir_mode
782
def get_branch_transport(self, branch_format):
783
"""See BzrDir.get_branch_transport()."""
784
if branch_format is None:
785
return self.transport.clone('branch')
787
branch_format.get_format_string()
788
except NotImplementedError:
789
raise errors.IncompatibleFormat(branch_format, self._format)
791
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
792
except errors.FileExists:
794
return self.transport.clone('branch')
796
def get_repository_transport(self, repository_format):
797
"""See BzrDir.get_repository_transport()."""
798
if repository_format is None:
799
return self.transport.clone('repository')
801
repository_format.get_format_string()
802
except NotImplementedError:
803
raise errors.IncompatibleFormat(repository_format, self._format)
805
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
806
except errors.FileExists:
808
return self.transport.clone('repository')
810
def get_workingtree_transport(self, workingtree_format):
811
"""See BzrDir.get_workingtree_transport()."""
812
if workingtree_format is None:
813
return self.transport.clone('checkout')
815
workingtree_format.get_format_string()
816
except NotImplementedError:
817
raise errors.IncompatibleFormat(workingtree_format, self._format)
819
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
820
except errors.FileExists:
822
return self.transport.clone('checkout')
824
def needs_format_conversion(self, format=None):
825
"""See BzrDir.needs_format_conversion()."""
827
format = BzrDirFormat.get_default_format()
828
if not isinstance(self._format, format.__class__):
829
# it is not a meta dir format, conversion is needed.
831
# we might want to push this down to the repository?
833
if not isinstance(self.open_repository()._format,
834
format.repository_format.__class__):
835
# the repository needs an upgrade.
837
except errors.NoRepositoryPresent:
839
# currently there are no other possible conversions for meta1 formats.
842
def open_branch(self, unsupported=False):
843
"""See BzrDir.open_branch."""
844
from bzrlib.branch import BranchFormat
845
format = BranchFormat.find_format(self)
846
self._check_supported(format, unsupported)
847
return format.open(self, _found=True)
849
def open_repository(self, unsupported=False):
850
"""See BzrDir.open_repository."""
851
from bzrlib.repository import RepositoryFormat
852
format = RepositoryFormat.find_format(self)
853
self._check_supported(format, unsupported)
854
return format.open(self, _found=True)
856
def open_workingtree(self, unsupported=False):
857
"""See BzrDir.open_workingtree."""
858
from bzrlib.workingtree import WorkingTreeFormat
859
format = WorkingTreeFormat.find_format(self)
860
self._check_supported(format, unsupported)
861
return format.open(self, _found=True)
864
class BzrDirFormat(object):
865
"""An encapsulation of the initialization and open routines for a format.
867
Formats provide three things:
868
* An initialization routine,
872
Formats are placed in an dict by their format string for reference
873
during bzrdir opening. These should be subclasses of BzrDirFormat
876
Once a format is deprecated, just deprecate the initialize and open
877
methods on the format class. Do not deprecate the object, as the
878
object will be created every system load.
881
_default_format = None
882
"""The default format used for new .bzr dirs."""
885
"""The known formats."""
887
_lock_file_name = 'branch-lock'
889
# _lock_class must be set in subclasses to the lock type, typ.
890
# TransportLock or LockDir
893
def find_format(klass, transport):
894
"""Return the format registered for URL."""
896
format_string = transport.get(".bzr/branch-format").read()
897
return klass._formats[format_string]
898
except errors.NoSuchFile:
899
raise errors.NotBranchError(path=transport.base)
901
raise errors.UnknownFormatError(format_string)
904
def get_default_format(klass):
905
"""Return the current default format."""
906
return klass._default_format
908
def get_format_string(self):
909
"""Return the ASCII format string that identifies this format."""
910
raise NotImplementedError(self.get_format_string)
912
def get_format_description(self):
913
"""Return the short description for this format."""
914
raise NotImplementedError(self.get_format_description)
916
def get_converter(self, format=None):
917
"""Return the converter to use to convert bzrdirs needing converts.
919
This returns a bzrlib.bzrdir.Converter object.
921
This should return the best upgrader to step this format towards the
922
current default format. In the case of plugins we can/shouold provide
923
some means for them to extend the range of returnable converters.
925
:param format: Optional format to override the default foramt of the
928
raise NotImplementedError(self.get_converter)
930
def initialize(self, url):
931
"""Create a bzr control dir at this url and return an opened copy.
933
Subclasses should typically override initialize_on_transport
934
instead of this method.
936
return self.initialize_on_transport(get_transport(url))
938
def initialize_on_transport(self, transport):
939
"""Initialize a new bzrdir in the base directory of a Transport."""
940
# Since we don'transport have a .bzr directory, inherit the
941
# mode from the root directory
942
temp_control = LockableFiles(transport, '', TransportLock)
943
temp_control._transport.mkdir('.bzr',
944
# FIXME: RBC 20060121 dont peek under
946
mode=temp_control._dir_mode)
947
file_mode = temp_control._file_mode
949
mutter('created control directory in ' + transport.base)
950
control = transport.clone('.bzr')
951
utf8_files = [('README',
952
"This is a Bazaar-NG control directory.\n"
953
"Do not change any files in this directory.\n"),
954
('branch-format', self.get_format_string()),
956
# NB: no need to escape relative paths that are url safe.
957
control_files = LockableFiles(control, self._lock_file_name,
959
control_files.create_lock()
960
control_files.lock_write()
962
for file, content in utf8_files:
963
control_files.put_utf8(file, content)
965
control_files.unlock()
966
return self.open(transport, _found=True)
968
def is_supported(self):
969
"""Is this format supported?
971
Supported formats must be initializable and openable.
972
Unsupported formats may not support initialization or committing or
973
some other features depending on the reason for not being supported.
977
def open(self, transport, _found=False):
978
"""Return an instance of this format for the dir transport points at.
980
_found is a private parameter, do not use it.
983
assert isinstance(BzrDirFormat.find_format(transport),
985
return self._open(transport)
987
def _open(self, transport):
988
"""Template method helper for opening BzrDirectories.
990
This performs the actual open and any additional logic or parameter
993
raise NotImplementedError(self._open)
996
def register_format(klass, format):
997
klass._formats[format.get_format_string()] = format
1000
def set_default_format(klass, format):
1001
klass._default_format = format
1004
return self.get_format_string()[:-1]
1007
def unregister_format(klass, format):
1008
assert klass._formats[format.get_format_string()] is format
1009
del klass._formats[format.get_format_string()]
1012
class BzrDirFormat4(BzrDirFormat):
1013
"""Bzr dir format 4.
1015
This format is a combined format for working tree, branch and repository.
1017
- Format 1 working trees [always]
1018
- Format 4 branches [always]
1019
- Format 4 repositories [always]
1021
This format is deprecated: it indexes texts using a text it which is
1022
removed in format 5; write support for this format has been removed.
1025
_lock_class = TransportLock
1027
def get_format_string(self):
1028
"""See BzrDirFormat.get_format_string()."""
1029
return "Bazaar-NG branch, format 0.0.4\n"
1031
def get_format_description(self):
1032
"""See BzrDirFormat.get_format_description()."""
1033
return "All-in-one format 4"
1035
def get_converter(self, format=None):
1036
"""See BzrDirFormat.get_converter()."""
1037
# there is one and only one upgrade path here.
1038
return ConvertBzrDir4To5()
1040
def initialize_on_transport(self, transport):
1041
"""Format 4 branches cannot be created."""
1042
raise errors.UninitializableFormat(self)
1044
def is_supported(self):
1045
"""Format 4 is not supported.
1047
It is not supported because the model changed from 4 to 5 and the
1048
conversion logic is expensive - so doing it on the fly was not
1053
def _open(self, transport):
1054
"""See BzrDirFormat._open."""
1055
return BzrDir4(transport, self)
1057
def __return_repository_format(self):
1058
"""Circular import protection."""
1059
from bzrlib.repository import RepositoryFormat4
1060
return RepositoryFormat4(self)
1061
repository_format = property(__return_repository_format)
1064
class BzrDirFormat5(BzrDirFormat):
1065
"""Bzr control format 5.
1067
This format is a combined format for working tree, branch and repository.
1069
- Format 2 working trees [always]
1070
- Format 4 branches [always]
1071
- Format 5 repositories [always]
1072
Unhashed stores in the repository.
1075
_lock_class = TransportLock
1077
def get_format_string(self):
1078
"""See BzrDirFormat.get_format_string()."""
1079
return "Bazaar-NG branch, format 5\n"
1081
def get_format_description(self):
1082
"""See BzrDirFormat.get_format_description()."""
1083
return "All-in-one format 5"
1085
def get_converter(self, format=None):
1086
"""See BzrDirFormat.get_converter()."""
1087
# there is one and only one upgrade path here.
1088
return ConvertBzrDir5To6()
1090
def _initialize_for_clone(self, url):
1091
return self.initialize_on_transport(get_transport(url), _cloning=True)
1093
def initialize_on_transport(self, transport, _cloning=False):
1094
"""Format 5 dirs always have working tree, branch and repository.
1096
Except when they are being cloned.
1098
from bzrlib.branch import BzrBranchFormat4
1099
from bzrlib.repository import RepositoryFormat5
1100
from bzrlib.workingtree import WorkingTreeFormat2
1101
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1102
RepositoryFormat5().initialize(result, _internal=True)
1104
BzrBranchFormat4().initialize(result)
1105
WorkingTreeFormat2().initialize(result)
1108
def _open(self, transport):
1109
"""See BzrDirFormat._open."""
1110
return BzrDir5(transport, self)
1112
def __return_repository_format(self):
1113
"""Circular import protection."""
1114
from bzrlib.repository import RepositoryFormat5
1115
return RepositoryFormat5(self)
1116
repository_format = property(__return_repository_format)
1119
class BzrDirFormat6(BzrDirFormat):
1120
"""Bzr control format 6.
1122
This format is a combined format for working tree, branch and repository.
1124
- Format 2 working trees [always]
1125
- Format 4 branches [always]
1126
- Format 6 repositories [always]
1129
_lock_class = TransportLock
1131
def get_format_string(self):
1132
"""See BzrDirFormat.get_format_string()."""
1133
return "Bazaar-NG branch, format 6\n"
1135
def get_format_description(self):
1136
"""See BzrDirFormat.get_format_description()."""
1137
return "All-in-one format 6"
1139
def get_converter(self, format=None):
1140
"""See BzrDirFormat.get_converter()."""
1141
# there is one and only one upgrade path here.
1142
return ConvertBzrDir6ToMeta()
1144
def _initialize_for_clone(self, url):
1145
return self.initialize_on_transport(get_transport(url), _cloning=True)
1147
def initialize_on_transport(self, transport, _cloning=False):
1148
"""Format 6 dirs always have working tree, branch and repository.
1150
Except when they are being cloned.
1152
from bzrlib.branch import BzrBranchFormat4
1153
from bzrlib.repository import RepositoryFormat6
1154
from bzrlib.workingtree import WorkingTreeFormat2
1155
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1156
RepositoryFormat6().initialize(result, _internal=True)
1158
BzrBranchFormat4().initialize(result)
1160
WorkingTreeFormat2().initialize(result)
1161
except errors.NotLocalUrl:
1162
# emulate pre-check behaviour for working tree and silently
1167
def _open(self, transport):
1168
"""See BzrDirFormat._open."""
1169
return BzrDir6(transport, self)
1171
def __return_repository_format(self):
1172
"""Circular import protection."""
1173
from bzrlib.repository import RepositoryFormat6
1174
return RepositoryFormat6(self)
1175
repository_format = property(__return_repository_format)
1178
class BzrDirMetaFormat1(BzrDirFormat):
1179
"""Bzr meta control format 1
1181
This is the first format with split out working tree, branch and repository
1184
- Format 3 working trees [optional]
1185
- Format 5 branches [optional]
1186
- Format 7 repositories [optional]
1189
_lock_class = LockDir
1191
def get_converter(self, format=None):
1192
"""See BzrDirFormat.get_converter()."""
1194
format = BzrDirFormat.get_default_format()
1195
if not isinstance(self, format.__class__):
1196
# converting away from metadir is not implemented
1197
raise NotImplementedError(self.get_converter)
1198
return ConvertMetaToMeta(format)
1200
def get_format_string(self):
1201
"""See BzrDirFormat.get_format_string()."""
1202
return "Bazaar-NG meta directory, format 1\n"
1204
def get_format_description(self):
1205
"""See BzrDirFormat.get_format_description()."""
1206
return "Meta directory format 1"
1208
def _open(self, transport):
1209
"""See BzrDirFormat._open."""
1210
return BzrDirMeta1(transport, self)
1212
def __return_repository_format(self):
1213
"""Circular import protection."""
1214
if getattr(self, '_repository_format', None):
1215
return self._repository_format
1216
from bzrlib.repository import RepositoryFormat
1217
return RepositoryFormat.get_default_format()
1219
def __set_repository_format(self, value):
1220
"""Allow changint the repository format for metadir formats."""
1221
self._repository_format = value
1223
repository_format = property(__return_repository_format, __set_repository_format)
1226
BzrDirFormat.register_format(BzrDirFormat4())
1227
BzrDirFormat.register_format(BzrDirFormat5())
1228
BzrDirFormat.register_format(BzrDirFormat6())
1229
__default_format = BzrDirMetaFormat1()
1230
BzrDirFormat.register_format(__default_format)
1231
BzrDirFormat.set_default_format(__default_format)
1234
class BzrDirTestProviderAdapter(object):
1235
"""A tool to generate a suite testing multiple bzrdir formats at once.
1237
This is done by copying the test once for each transport and injecting
1238
the transport_server, transport_readonly_server, and bzrdir_format
1239
classes into each copy. Each copy is also given a new id() to make it
1243
def __init__(self, transport_server, transport_readonly_server, formats):
1244
self._transport_server = transport_server
1245
self._transport_readonly_server = transport_readonly_server
1246
self._formats = formats
1248
def adapt(self, test):
1249
result = TestSuite()
1250
for format in self._formats:
1251
new_test = deepcopy(test)
1252
new_test.transport_server = self._transport_server
1253
new_test.transport_readonly_server = self._transport_readonly_server
1254
new_test.bzrdir_format = format
1255
def make_new_test_id():
1256
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1257
return lambda: new_id
1258
new_test.id = make_new_test_id()
1259
result.addTest(new_test)
1263
class ScratchDir(BzrDir6):
1264
"""Special test class: a bzrdir that cleans up itself..
1266
>>> d = ScratchDir()
1267
>>> base = d.transport.base
1270
>>> b.transport.__del__()
1275
def __init__(self, files=[], dirs=[], transport=None):
1276
"""Make a test branch.
1278
This creates a temporary directory and runs init-tree in it.
1280
If any files are listed, they are created in the working copy.
1282
if transport is None:
1283
transport = bzrlib.transport.local.ScratchTransport()
1284
# local import for scope restriction
1285
BzrDirFormat6().initialize(transport.base)
1286
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1287
self.create_repository()
1288
self.create_branch()
1289
self.create_workingtree()
1291
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1293
# BzrBranch creates a clone to .bzr and then forgets about the
1294
# original transport. A ScratchTransport() deletes itself and
1295
# everything underneath it when it goes away, so we need to
1296
# grab a local copy to prevent that from happening
1297
self._transport = transport
1300
self._transport.mkdir(d)
1303
self._transport.put(f, 'content of %s' % f)
1307
>>> orig = ScratchDir(files=["file1", "file2"])
1308
>>> os.listdir(orig.base)
1309
[u'.bzr', u'file1', u'file2']
1310
>>> clone = orig.clone()
1311
>>> if os.name != 'nt':
1312
... os.path.samefile(orig.base, clone.base)
1314
... orig.base == clone.base
1317
>>> os.listdir(clone.base)
1318
[u'.bzr', u'file1', u'file2']
1320
from shutil import copytree
1321
from bzrlib.osutils import mkdtemp
1324
copytree(self.base, base, symlinks=True)
1326
transport=bzrlib.transport.local.ScratchTransport(base))
1329
class Converter(object):
1330
"""Converts a disk format object from one format to another."""
1332
def convert(self, to_convert, pb):
1333
"""Perform the conversion of to_convert, giving feedback via pb.
1335
:param to_convert: The disk object to convert.
1336
:param pb: a progress bar to use for progress information.
1339
def step(self, message):
1340
"""Update the pb by a step."""
1342
self.pb.update(message, self.count, self.total)
1345
class ConvertBzrDir4To5(Converter):
1346
"""Converts format 4 bzr dirs to format 5."""
1349
super(ConvertBzrDir4To5, self).__init__()
1350
self.converted_revs = set()
1351
self.absent_revisions = set()
1355
def convert(self, to_convert, pb):
1356
"""See Converter.convert()."""
1357
self.bzrdir = to_convert
1359
self.pb.note('starting upgrade from format 4 to 5')
1360
if isinstance(self.bzrdir.transport, LocalTransport):
1361
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1362
self._convert_to_weaves()
1363
return BzrDir.open(self.bzrdir.root_transport.base)
1365
def _convert_to_weaves(self):
1366
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1369
stat = self.bzrdir.transport.stat('weaves')
1370
if not S_ISDIR(stat.st_mode):
1371
self.bzrdir.transport.delete('weaves')
1372
self.bzrdir.transport.mkdir('weaves')
1373
except errors.NoSuchFile:
1374
self.bzrdir.transport.mkdir('weaves')
1375
# deliberately not a WeaveFile as we want to build it up slowly.
1376
self.inv_weave = Weave('inventory')
1377
# holds in-memory weaves for all files
1378
self.text_weaves = {}
1379
self.bzrdir.transport.delete('branch-format')
1380
self.branch = self.bzrdir.open_branch()
1381
self._convert_working_inv()
1382
rev_history = self.branch.revision_history()
1383
# to_read is a stack holding the revisions we still need to process;
1384
# appending to it adds new highest-priority revisions
1385
self.known_revisions = set(rev_history)
1386
self.to_read = rev_history[-1:]
1388
rev_id = self.to_read.pop()
1389
if (rev_id not in self.revisions
1390
and rev_id not in self.absent_revisions):
1391
self._load_one_rev(rev_id)
1393
to_import = self._make_order()
1394
for i, rev_id in enumerate(to_import):
1395
self.pb.update('converting revision', i, len(to_import))
1396
self._convert_one_rev(rev_id)
1398
self._write_all_weaves()
1399
self._write_all_revs()
1400
self.pb.note('upgraded to weaves:')
1401
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1402
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1403
self.pb.note(' %6d texts', self.text_count)
1404
self._cleanup_spare_files_after_format4()
1405
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1407
def _cleanup_spare_files_after_format4(self):
1408
# FIXME working tree upgrade foo.
1409
for n in 'merged-patches', 'pending-merged-patches':
1411
## assert os.path.getsize(p) == 0
1412
self.bzrdir.transport.delete(n)
1413
except errors.NoSuchFile:
1415
self.bzrdir.transport.delete_tree('inventory-store')
1416
self.bzrdir.transport.delete_tree('text-store')
1418
def _convert_working_inv(self):
1419
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1420
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1421
# FIXME inventory is a working tree change.
1422
self.branch.control_files.put('inventory', new_inv_xml)
1424
def _write_all_weaves(self):
1425
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1426
weave_transport = self.bzrdir.transport.clone('weaves')
1427
weaves = WeaveStore(weave_transport, prefixed=False)
1428
transaction = WriteTransaction()
1432
for file_id, file_weave in self.text_weaves.items():
1433
self.pb.update('writing weave', i, len(self.text_weaves))
1434
weaves._put_weave(file_id, file_weave, transaction)
1436
self.pb.update('inventory', 0, 1)
1437
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1438
self.pb.update('inventory', 1, 1)
1442
def _write_all_revs(self):
1443
"""Write all revisions out in new form."""
1444
self.bzrdir.transport.delete_tree('revision-store')
1445
self.bzrdir.transport.mkdir('revision-store')
1446
revision_transport = self.bzrdir.transport.clone('revision-store')
1448
_revision_store = TextRevisionStore(TextStore(revision_transport,
1452
transaction = bzrlib.transactions.WriteTransaction()
1453
for i, rev_id in enumerate(self.converted_revs):
1454
self.pb.update('write revision', i, len(self.converted_revs))
1455
_revision_store.add_revision(self.revisions[rev_id], transaction)
1459
def _load_one_rev(self, rev_id):
1460
"""Load a revision object into memory.
1462
Any parents not either loaded or abandoned get queued to be
1464
self.pb.update('loading revision',
1465
len(self.revisions),
1466
len(self.known_revisions))
1467
if not self.branch.repository.has_revision(rev_id):
1469
self.pb.note('revision {%s} not present in branch; '
1470
'will be converted as a ghost',
1472
self.absent_revisions.add(rev_id)
1474
rev = self.branch.repository._revision_store.get_revision(rev_id,
1475
self.branch.repository.get_transaction())
1476
for parent_id in rev.parent_ids:
1477
self.known_revisions.add(parent_id)
1478
self.to_read.append(parent_id)
1479
self.revisions[rev_id] = rev
1481
def _load_old_inventory(self, rev_id):
1482
assert rev_id not in self.converted_revs
1483
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1484
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1485
rev = self.revisions[rev_id]
1486
if rev.inventory_sha1:
1487
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1488
'inventory sha mismatch for {%s}' % rev_id
1491
def _load_updated_inventory(self, rev_id):
1492
assert rev_id in self.converted_revs
1493
inv_xml = self.inv_weave.get_text(rev_id)
1494
inv = serializer_v5.read_inventory_from_string(inv_xml)
1497
def _convert_one_rev(self, rev_id):
1498
"""Convert revision and all referenced objects to new format."""
1499
rev = self.revisions[rev_id]
1500
inv = self._load_old_inventory(rev_id)
1501
present_parents = [p for p in rev.parent_ids
1502
if p not in self.absent_revisions]
1503
self._convert_revision_contents(rev, inv, present_parents)
1504
self._store_new_weave(rev, inv, present_parents)
1505
self.converted_revs.add(rev_id)
1507
def _store_new_weave(self, rev, inv, present_parents):
1508
# the XML is now updated with text versions
1512
if ie.kind == 'root_directory':
1514
assert hasattr(ie, 'revision'), \
1515
'no revision on {%s} in {%s}' % \
1516
(file_id, rev.revision_id)
1517
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1518
new_inv_sha1 = sha_string(new_inv_xml)
1519
self.inv_weave.add_lines(rev.revision_id,
1521
new_inv_xml.splitlines(True))
1522
rev.inventory_sha1 = new_inv_sha1
1524
def _convert_revision_contents(self, rev, inv, present_parents):
1525
"""Convert all the files within a revision.
1527
Also upgrade the inventory to refer to the text revision ids."""
1528
rev_id = rev.revision_id
1529
mutter('converting texts of revision {%s}',
1531
parent_invs = map(self._load_updated_inventory, present_parents)
1534
self._convert_file_version(rev, ie, parent_invs)
1536
def _convert_file_version(self, rev, ie, parent_invs):
1537
"""Convert one version of one file.
1539
The file needs to be added into the weave if it is a merge
1540
of >=2 parents or if it's changed from its parent.
1542
if ie.kind == 'root_directory':
1544
file_id = ie.file_id
1545
rev_id = rev.revision_id
1546
w = self.text_weaves.get(file_id)
1549
self.text_weaves[file_id] = w
1550
text_changed = False
1551
previous_entries = ie.find_previous_heads(parent_invs,
1555
for old_revision in previous_entries:
1556
# if this fails, its a ghost ?
1557
assert old_revision in self.converted_revs
1558
self.snapshot_ie(previous_entries, ie, w, rev_id)
1560
assert getattr(ie, 'revision', None) is not None
1562
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1563
# TODO: convert this logic, which is ~= snapshot to
1564
# a call to:. This needs the path figured out. rather than a work_tree
1565
# a v4 revision_tree can be given, or something that looks enough like
1566
# one to give the file content to the entry if it needs it.
1567
# and we need something that looks like a weave store for snapshot to
1569
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1570
if len(previous_revisions) == 1:
1571
previous_ie = previous_revisions.values()[0]
1572
if ie._unchanged(previous_ie):
1573
ie.revision = previous_ie.revision
1576
text = self.branch.repository.text_store.get(ie.text_id)
1577
file_lines = text.readlines()
1578
assert sha_strings(file_lines) == ie.text_sha1
1579
assert sum(map(len, file_lines)) == ie.text_size
1580
w.add_lines(rev_id, previous_revisions, file_lines)
1581
self.text_count += 1
1583
w.add_lines(rev_id, previous_revisions, [])
1584
ie.revision = rev_id
1586
def _make_order(self):
1587
"""Return a suitable order for importing revisions.
1589
The order must be such that an revision is imported after all
1590
its (present) parents.
1592
todo = set(self.revisions.keys())
1593
done = self.absent_revisions.copy()
1596
# scan through looking for a revision whose parents
1598
for rev_id in sorted(list(todo)):
1599
rev = self.revisions[rev_id]
1600
parent_ids = set(rev.parent_ids)
1601
if parent_ids.issubset(done):
1602
# can take this one now
1603
order.append(rev_id)
1609
class ConvertBzrDir5To6(Converter):
1610
"""Converts format 5 bzr dirs to format 6."""
1612
def convert(self, to_convert, pb):
1613
"""See Converter.convert()."""
1614
self.bzrdir = to_convert
1616
self.pb.note('starting upgrade from format 5 to 6')
1617
self._convert_to_prefixed()
1618
return BzrDir.open(self.bzrdir.root_transport.base)
1620
def _convert_to_prefixed(self):
1621
from bzrlib.store import TransportStore
1622
self.bzrdir.transport.delete('branch-format')
1623
for store_name in ["weaves", "revision-store"]:
1624
self.pb.note("adding prefixes to %s" % store_name)
1625
store_transport = self.bzrdir.transport.clone(store_name)
1626
store = TransportStore(store_transport, prefixed=True)
1627
for urlfilename in store_transport.list_dir('.'):
1628
filename = urlunescape(urlfilename)
1629
if (filename.endswith(".weave") or
1630
filename.endswith(".gz") or
1631
filename.endswith(".sig")):
1632
file_id = os.path.splitext(filename)[0]
1635
prefix_dir = store.hash_prefix(file_id)
1636
# FIXME keep track of the dirs made RBC 20060121
1638
store_transport.move(filename, prefix_dir + '/' + filename)
1639
except errors.NoSuchFile: # catches missing dirs strangely enough
1640
store_transport.mkdir(prefix_dir)
1641
store_transport.move(filename, prefix_dir + '/' + filename)
1642
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1645
class ConvertBzrDir6ToMeta(Converter):
1646
"""Converts format 6 bzr dirs to metadirs."""
1648
def convert(self, to_convert, pb):
1649
"""See Converter.convert()."""
1650
self.bzrdir = to_convert
1653
self.total = 20 # the steps we know about
1654
self.garbage_inventories = []
1656
self.pb.note('starting upgrade from format 6 to metadir')
1657
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1658
# its faster to move specific files around than to open and use the apis...
1659
# first off, nuke ancestry.weave, it was never used.
1661
self.step('Removing ancestry.weave')
1662
self.bzrdir.transport.delete('ancestry.weave')
1663
except errors.NoSuchFile:
1665
# find out whats there
1666
self.step('Finding branch files')
1667
last_revision = self.bzrdir.open_branch().last_revision()
1668
bzrcontents = self.bzrdir.transport.list_dir('.')
1669
for name in bzrcontents:
1670
if name.startswith('basis-inventory.'):
1671
self.garbage_inventories.append(name)
1672
# create new directories for repository, working tree and branch
1673
self.dir_mode = self.bzrdir._control_files._dir_mode
1674
self.file_mode = self.bzrdir._control_files._file_mode
1675
repository_names = [('inventory.weave', True),
1676
('revision-store', True),
1678
self.step('Upgrading repository ')
1679
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1680
self.make_lock('repository')
1681
# we hard code the formats here because we are converting into
1682
# the meta format. The meta format upgrader can take this to a
1683
# future format within each component.
1684
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1685
for entry in repository_names:
1686
self.move_entry('repository', entry)
1688
self.step('Upgrading branch ')
1689
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1690
self.make_lock('branch')
1691
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1692
branch_files = [('revision-history', True),
1693
('branch-name', True),
1695
for entry in branch_files:
1696
self.move_entry('branch', entry)
1698
self.step('Upgrading working tree')
1699
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1700
self.make_lock('checkout')
1701
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1702
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1703
checkout_files = [('pending-merges', True),
1704
('inventory', True),
1705
('stat-cache', False)]
1706
for entry in checkout_files:
1707
self.move_entry('checkout', entry)
1708
if last_revision is not None:
1709
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1711
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1712
return BzrDir.open(self.bzrdir.root_transport.base)
1714
def make_lock(self, name):
1715
"""Make a lock for the new control dir name."""
1716
self.step('Make %s lock' % name)
1717
ld = LockDir(self.bzrdir.transport,
1719
file_modebits=self.file_mode,
1720
dir_modebits=self.dir_mode)
1723
def move_entry(self, new_dir, entry):
1724
"""Move then entry name into new_dir."""
1726
mandatory = entry[1]
1727
self.step('Moving %s' % name)
1729
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1730
except errors.NoSuchFile:
1734
def put_format(self, dirname, format):
1735
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1738
class ConvertMetaToMeta(Converter):
1739
"""Converts the components of metadirs."""
1741
def __init__(self, target_format):
1742
"""Create a metadir to metadir converter.
1744
:param target_format: The final metadir format that is desired.
1746
self.target_format = target_format
1748
def convert(self, to_convert, pb):
1749
"""See Converter.convert()."""
1750
self.bzrdir = to_convert
1754
self.step('checking repository format')
1756
repo = self.bzrdir.open_repository()
1757
except errors.NoRepositoryPresent:
1760
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1761
from bzrlib.repository import CopyConverter
1762
self.pb.note('starting repository conversion')
1763
converter = CopyConverter(self.target_format.repository_format)
1764
converter.convert(repo, pb)