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
47
from bzrlib.transport.local import LocalTransport
48
import bzrlib.urlutils as urlutils
49
from bzrlib.weave import Weave
50
from bzrlib.xml4 import serializer_v4
51
from bzrlib.xml5 import serializer_v5
55
"""A .bzr control diretory.
57
BzrDir instances let you create or open any of the things that can be
58
found within .bzr - checkouts, branches and repositories.
61
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
63
a transport connected to the directory this bzr was opened from.
67
"""Invoke break_lock on the first object in the bzrdir.
69
If there is a tree, the tree is opened and break_lock() called.
70
Otherwise, branch is tried, and finally repository.
73
thing_to_unlock = self.open_workingtree()
74
except (errors.NotLocalUrl, errors.NoWorkingTree):
76
thing_to_unlock = self.open_branch()
77
except errors.NotBranchError:
79
thing_to_unlock = self.open_repository()
80
except errors.NoRepositoryPresent:
82
thing_to_unlock.break_lock()
84
def can_convert_format(self):
85
"""Return true if this bzrdir is one whose format we can convert from."""
89
def _check_supported(format, allow_unsupported):
90
"""Check whether format is a supported format.
92
If allow_unsupported is True, this is a no-op.
94
if not allow_unsupported and not format.is_supported():
95
# see open_downlevel to open legacy branches.
96
raise errors.UnsupportedFormatError(
97
'sorry, format %s not supported' % format,
98
['use a different bzr version',
99
'or remove the .bzr directory'
100
' and "bzr init" again'])
102
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
103
"""Clone this bzrdir and its contents to url verbatim.
105
If urls last component does not exist, it will be created.
107
if revision_id is not None, then the clone operation may tune
108
itself to download less data.
109
:param force_new_repo: Do not use a shared repository for the target
110
even if one is available.
113
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
114
result = self._format.initialize(url)
116
local_repo = self.find_repository()
117
except errors.NoRepositoryPresent:
120
# may need to copy content in
122
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
125
result_repo = result.find_repository()
126
# fetch content this dir needs.
128
# XXX FIXME RBC 20060214 need tests for this when the basis
130
result_repo.fetch(basis_repo, revision_id=revision_id)
131
result_repo.fetch(local_repo, revision_id=revision_id)
132
except errors.NoRepositoryPresent:
133
# needed to make one anyway.
134
local_repo.clone(result, revision_id=revision_id, basis=basis_repo)
135
# 1 if there is a branch present
136
# make sure its content is available in the target repository
139
self.open_branch().clone(result, revision_id=revision_id)
140
except errors.NotBranchError:
143
self.open_workingtree().clone(result, basis=basis_tree)
144
except (errors.NoWorkingTree, errors.NotLocalUrl):
148
def _get_basis_components(self, basis):
149
"""Retrieve the basis components that are available at basis."""
151
return None, None, None
153
basis_tree = basis.open_workingtree()
154
basis_branch = basis_tree.branch
155
basis_repo = basis_branch.repository
156
except (errors.NoWorkingTree, errors.NotLocalUrl):
159
basis_branch = basis.open_branch()
160
basis_repo = basis_branch.repository
161
except errors.NotBranchError:
164
basis_repo = basis.open_repository()
165
except errors.NoRepositoryPresent:
167
return basis_repo, basis_branch, basis_tree
169
def _make_tail(self, url):
170
segments = url.split('/')
171
if segments and segments[-1] not in ('', '.'):
172
parent = '/'.join(segments[:-1])
173
print '_make_tail(%s) => parent: %s, segment: %s' % (
174
url, parent, segments[-1])
175
t = bzrlib.transport.get_transport(parent)
177
t.mkdir(segments[-1])
178
except errors.FileExists:
182
def create(cls, base):
183
"""Create a new BzrDir at the url 'base'.
185
This will call the current default formats initialize with base
186
as the only parameter.
188
If you need a specific format, consider creating an instance
189
of that and calling initialize().
191
if cls is not BzrDir:
192
raise AssertionError("BzrDir.create always creates the default format, "
193
"not one of %r" % cls)
194
segments = base.split('/')
195
if segments and segments[-1] not in ('', '.'):
196
parent = '/'.join(segments[:-1])
197
t = bzrlib.transport.get_transport(parent)
199
t.mkdir(segments[-1])
200
except errors.FileExists:
202
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
204
def create_branch(self):
205
"""Create a branch in this BzrDir.
207
The bzrdirs format will control what branch format is created.
208
For more control see BranchFormatXX.create(a_bzrdir).
210
raise NotImplementedError(self.create_branch)
213
def create_branch_and_repo(base, force_new_repo=False):
214
"""Create a new BzrDir, Branch and Repository at the url 'base'.
216
This will use the current default BzrDirFormat, and use whatever
217
repository format that that uses via bzrdir.create_branch and
218
create_repository. If a shared repository is available that is used
221
The created Branch object is returned.
223
:param base: The URL to create the branch at.
224
:param force_new_repo: If True a new repository is always created.
226
bzrdir = BzrDir.create(base)
227
bzrdir._find_or_create_repository(force_new_repo)
228
return bzrdir.create_branch()
230
def _find_or_create_repository(self, force_new_repo):
231
"""Create a new repository if needed, returning the repository."""
233
return self.create_repository()
235
return self.find_repository()
236
except errors.NoRepositoryPresent:
237
return self.create_repository()
240
def create_branch_convenience(base, force_new_repo=False,
241
force_new_tree=None, format=None):
242
"""Create a new BzrDir, Branch and Repository at the url 'base'.
244
This is a convenience function - it will use an existing repository
245
if possible, can be told explicitly whether to create a working tree or
248
This will use the current default BzrDirFormat, and use whatever
249
repository format that that uses via bzrdir.create_branch and
250
create_repository. If a shared repository is available that is used
251
preferentially. Whatever repository is used, its tree creation policy
254
The created Branch object is returned.
255
If a working tree cannot be made due to base not being a file:// url,
256
no error is raised unless force_new_tree is True, in which case no
257
data is created on disk and NotLocalUrl is raised.
259
:param base: The URL to create the branch at.
260
:param force_new_repo: If True a new repository is always created.
261
:param force_new_tree: If True or False force creation of a tree or
262
prevent such creation respectively.
263
:param format: Override for the for the bzrdir format to create
266
# check for non local urls
267
t = get_transport(safe_unicode(base))
268
if not isinstance(t, LocalTransport):
269
raise errors.NotLocalUrl(base)
271
bzrdir = BzrDir.create(base)
273
bzrdir = format.initialize(base)
274
repo = bzrdir._find_or_create_repository(force_new_repo)
275
result = bzrdir.create_branch()
276
if force_new_tree or (repo.make_working_trees() and
277
force_new_tree is None):
279
bzrdir.create_workingtree()
280
except errors.NotLocalUrl:
285
def create_repository(base, shared=False):
286
"""Create a new BzrDir and Repository at the url 'base'.
288
This will use the current default BzrDirFormat, and use whatever
289
repository format that that uses for bzrdirformat.create_repository.
291
;param shared: Create a shared repository rather than a standalone
293
The Repository object is returned.
295
This must be overridden as an instance method in child classes, where
296
it should take no parameters and construct whatever repository format
297
that child class desires.
299
bzrdir = BzrDir.create(base)
300
return bzrdir.create_repository()
303
def create_standalone_workingtree(base):
304
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
306
'base' must be a local path or a file:// url.
308
This will use the current default BzrDirFormat, and use whatever
309
repository format that that uses for bzrdirformat.create_workingtree,
310
create_branch and create_repository.
312
The WorkingTree object is returned.
314
t = get_transport(safe_unicode(base))
315
if not isinstance(t, LocalTransport):
316
raise errors.NotLocalUrl(base)
317
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
318
force_new_repo=True).bzrdir
319
return bzrdir.create_workingtree()
321
def create_workingtree(self, revision_id=None):
322
"""Create a working tree at this BzrDir.
324
revision_id: create it as of this revision id.
326
raise NotImplementedError(self.create_workingtree)
328
def find_repository(self):
329
"""Find the repository that should be used for a_bzrdir.
331
This does not require a branch as we use it to find the repo for
332
new branches as well as to hook existing branches up to their
336
return self.open_repository()
337
except errors.NoRepositoryPresent:
339
next_transport = self.root_transport.clone('..')
342
found_bzrdir = BzrDir.open_containing_from_transport(
344
except errors.NotBranchError:
345
raise errors.NoRepositoryPresent(self)
347
repository = found_bzrdir.open_repository()
348
except errors.NoRepositoryPresent:
349
next_transport = found_bzrdir.root_transport.clone('..')
351
if ((found_bzrdir.root_transport.base ==
352
self.root_transport.base) or repository.is_shared()):
355
raise errors.NoRepositoryPresent(self)
356
raise errors.NoRepositoryPresent(self)
358
def get_branch_transport(self, branch_format):
359
"""Get the transport for use by branch format in this BzrDir.
361
Note that bzr dirs that do not support format strings will raise
362
IncompatibleFormat if the branch format they are given has
363
a format string, and vice verca.
365
If branch_format is None, the transport is returned with no
366
checking. if it is not None, then the returned transport is
367
guaranteed to point to an existing directory ready for use.
369
raise NotImplementedError(self.get_branch_transport)
371
def get_repository_transport(self, repository_format):
372
"""Get the transport for use by repository format in this BzrDir.
374
Note that bzr dirs that do not support format strings will raise
375
IncompatibleFormat if the repository format they are given has
376
a format string, and vice verca.
378
If repository_format is None, the transport is returned with no
379
checking. if it is not None, then the returned transport is
380
guaranteed to point to an existing directory ready for use.
382
raise NotImplementedError(self.get_repository_transport)
384
def get_workingtree_transport(self, tree_format):
385
"""Get the transport for use by workingtree format in this BzrDir.
387
Note that bzr dirs that do not support format strings will raise
388
IncompatibleFormat if the workingtree format they are given has
389
a format string, and vice verca.
391
If workingtree_format is None, the transport is returned with no
392
checking. if it is not None, then the returned transport is
393
guaranteed to point to an existing directory ready for use.
395
raise NotImplementedError(self.get_workingtree_transport)
397
def __init__(self, _transport, _format):
398
"""Initialize a Bzr control dir object.
400
Only really common logic should reside here, concrete classes should be
401
made with varying behaviours.
403
:param _format: the format that is creating this BzrDir instance.
404
:param _transport: the transport this dir is based at.
406
self._format = _format
407
self.transport = _transport.clone('.bzr')
408
self.root_transport = _transport
410
def needs_format_conversion(self, format=None):
411
"""Return true if this bzrdir needs convert_format run on it.
413
For instance, if the repository format is out of date but the
414
branch and working tree are not, this should return True.
416
:param format: Optional parameter indicating a specific desired
417
format we plan to arrive at.
419
raise NotImplementedError(self.needs_format_conversion)
422
def open_unsupported(base):
423
"""Open a branch which is not supported."""
424
return BzrDir.open(base, _unsupported=True)
427
def open(base, _unsupported=False):
428
"""Open an existing bzrdir, rooted at 'base' (url)
430
_unsupported is a private parameter to the BzrDir class.
432
t = get_transport(base)
433
mutter("trying to open %r with transport %r", base, t)
434
format = BzrDirFormat.find_format(t)
435
BzrDir._check_supported(format, _unsupported)
436
return format.open(t, _found=True)
438
def open_branch(self, unsupported=False):
439
"""Open the branch object at this BzrDir if one is present.
441
If unsupported is True, then no longer supported branch formats can
444
TODO: static convenience version of this?
446
raise NotImplementedError(self.open_branch)
449
def open_containing(url):
450
"""Open an existing branch which contains url.
452
:param url: url to search from.
453
See open_containing_from_transport for more detail.
455
return BzrDir.open_containing_from_transport(get_transport(url))
458
def open_containing_from_transport(a_transport):
459
"""Open an existing branch which contains a_transport.base
461
This probes for a branch at a_transport, and searches upwards from there.
463
Basically we keep looking up until we find the control directory or
464
run into the root. If there isn't one, raises NotBranchError.
465
If there is one and it is either an unrecognised format or an unsupported
466
format, UnknownFormatError or UnsupportedFormatError are raised.
467
If there is one, it is returned, along with the unused portion of url.
469
:return: The BzrDir that contains the path, and a Unicode path
470
for the rest of the URL.
472
# this gets the normalised url back. I.e. '.' -> the full path.
473
url = a_transport.base
476
format = BzrDirFormat.find_format(a_transport)
477
BzrDir._check_supported(format, False)
478
return format.open(a_transport), urlutils.unescape(a_transport.relpath(url))
479
except errors.NotBranchError, e:
480
mutter('not a branch in: %r %s', a_transport.base, e)
481
new_t = a_transport.clone('..')
482
if new_t.base == a_transport.base:
483
# reached the root, whatever that may be
484
raise errors.NotBranchError(path=url)
487
def open_repository(self, _unsupported=False):
488
"""Open the repository object at this BzrDir if one is present.
490
This will not follow the Branch object pointer - its strictly a direct
491
open facility. Most client code should use open_branch().repository to
494
_unsupported is a private parameter, not part of the api.
495
TODO: static convenience version of this?
497
raise NotImplementedError(self.open_repository)
499
def open_workingtree(self, _unsupported=False):
500
"""Open the workingtree object at this BzrDir if one is present.
502
TODO: static convenience version of this?
504
raise NotImplementedError(self.open_workingtree)
506
def has_branch(self):
507
"""Tell if this bzrdir contains a branch.
509
Note: if you're going to open the branch, you should just go ahead
510
and try, and not ask permission first. (This method just opens the
511
branch and discards it, and that's somewhat expensive.)
516
except errors.NotBranchError:
519
def has_workingtree(self):
520
"""Tell if this bzrdir contains a working tree.
522
This will still raise an exception if the bzrdir has a workingtree that
523
is remote & inaccessible.
525
Note: if you're going to open the working tree, you should just go ahead
526
and try, and not ask permission first. (This method just opens the
527
workingtree and discards it, and that's somewhat expensive.)
530
self.open_workingtree()
532
except errors.NoWorkingTree:
535
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
536
"""Create a copy of this bzrdir prepared for use as a new line of
539
If urls last component does not exist, it will be created.
541
Attributes related to the identity of the source branch like
542
branch nickname will be cleaned, a working tree is created
543
whether one existed before or not; and a local branch is always
546
if revision_id is not None, then the clone operation may tune
547
itself to download less data.
550
result = self._format.initialize(url)
551
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
553
source_branch = self.open_branch()
554
source_repository = source_branch.repository
555
except errors.NotBranchError:
558
source_repository = self.open_repository()
559
except errors.NoRepositoryPresent:
560
# copy the entire basis one if there is one
561
# but there is no repository.
562
source_repository = basis_repo
567
result_repo = result.find_repository()
568
except errors.NoRepositoryPresent:
570
if source_repository is None and result_repo is not None:
572
elif source_repository is None and result_repo is None:
573
# no repo available, make a new one
574
result.create_repository()
575
elif source_repository is not None and result_repo is None:
576
# have soure, and want to make a new target repo
577
source_repository.clone(result,
578
revision_id=revision_id,
581
# fetch needed content into target.
583
# XXX FIXME RBC 20060214 need tests for this when the basis
585
result_repo.fetch(basis_repo, revision_id=revision_id)
586
result_repo.fetch(source_repository, revision_id=revision_id)
587
if source_branch is not None:
588
source_branch.sprout(result, revision_id=revision_id)
590
result.create_branch()
591
# TODO: jam 20060426 we probably need a test in here in the
592
# case that the newly sprouted branch is a remote one
593
if result_repo is None or result_repo.make_working_trees():
594
result.create_workingtree()
598
class BzrDirPreSplitOut(BzrDir):
599
"""A common class for the all-in-one formats."""
601
def __init__(self, _transport, _format):
602
"""See BzrDir.__init__."""
603
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
604
assert self._format._lock_class == TransportLock
605
assert self._format._lock_file_name == 'branch-lock'
606
self._control_files = LockableFiles(self.get_branch_transport(None),
607
self._format._lock_file_name,
608
self._format._lock_class)
610
def break_lock(self):
611
"""Pre-splitout bzrdirs do not suffer from stale locks."""
612
raise NotImplementedError(self.break_lock)
614
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
615
"""See BzrDir.clone()."""
616
from bzrlib.workingtree import WorkingTreeFormat2
618
result = self._format._initialize_for_clone(url)
619
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
620
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
621
from_branch = self.open_branch()
622
from_branch.clone(result, revision_id=revision_id)
624
self.open_workingtree().clone(result, basis=basis_tree)
625
except errors.NotLocalUrl:
626
# make a new one, this format always has to have one.
628
WorkingTreeFormat2().initialize(result)
629
except errors.NotLocalUrl:
630
# but we cannot do it for remote trees.
631
to_branch = result.open_branch()
632
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
635
def create_branch(self):
636
"""See BzrDir.create_branch."""
637
return self.open_branch()
639
def create_repository(self, shared=False):
640
"""See BzrDir.create_repository."""
642
raise errors.IncompatibleFormat('shared repository', self._format)
643
return self.open_repository()
645
def create_workingtree(self, revision_id=None):
646
"""See BzrDir.create_workingtree."""
647
# this looks buggy but is not -really-
648
# clone and sprout will have set the revision_id
649
# and that will have set it for us, its only
650
# specific uses of create_workingtree in isolation
651
# that can do wonky stuff here, and that only
652
# happens for creating checkouts, which cannot be
653
# done on this format anyway. So - acceptable wart.
654
result = self.open_workingtree()
655
if revision_id is not None:
656
result.set_last_revision(revision_id)
659
def get_branch_transport(self, branch_format):
660
"""See BzrDir.get_branch_transport()."""
661
if branch_format is None:
662
return self.transport
664
branch_format.get_format_string()
665
except NotImplementedError:
666
return self.transport
667
raise errors.IncompatibleFormat(branch_format, self._format)
669
def get_repository_transport(self, repository_format):
670
"""See BzrDir.get_repository_transport()."""
671
if repository_format is None:
672
return self.transport
674
repository_format.get_format_string()
675
except NotImplementedError:
676
return self.transport
677
raise errors.IncompatibleFormat(repository_format, self._format)
679
def get_workingtree_transport(self, workingtree_format):
680
"""See BzrDir.get_workingtree_transport()."""
681
if workingtree_format is None:
682
return self.transport
684
workingtree_format.get_format_string()
685
except NotImplementedError:
686
return self.transport
687
raise errors.IncompatibleFormat(workingtree_format, self._format)
689
def needs_format_conversion(self, format=None):
690
"""See BzrDir.needs_format_conversion()."""
691
# if the format is not the same as the system default,
692
# an upgrade is needed.
694
format = BzrDirFormat.get_default_format()
695
return not isinstance(self._format, format.__class__)
697
def open_branch(self, unsupported=False):
698
"""See BzrDir.open_branch."""
699
from bzrlib.branch import BzrBranchFormat4
700
format = BzrBranchFormat4()
701
self._check_supported(format, unsupported)
702
return format.open(self, _found=True)
704
def sprout(self, url, revision_id=None, basis=None):
705
"""See BzrDir.sprout()."""
706
from bzrlib.workingtree import WorkingTreeFormat2
708
result = self._format._initialize_for_clone(url)
709
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
711
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
712
except errors.NoRepositoryPresent:
715
self.open_branch().sprout(result, revision_id=revision_id)
716
except errors.NotBranchError:
718
# we always want a working tree
719
WorkingTreeFormat2().initialize(result)
723
class BzrDir4(BzrDirPreSplitOut):
724
"""A .bzr version 4 control object.
726
This is a deprecated format and may be removed after sept 2006.
729
def create_repository(self, shared=False):
730
"""See BzrDir.create_repository."""
731
return self._format.repository_format.initialize(self, shared)
733
def needs_format_conversion(self, format=None):
734
"""Format 4 dirs are always in need of conversion."""
737
def open_repository(self):
738
"""See BzrDir.open_repository."""
739
from bzrlib.repository import RepositoryFormat4
740
return RepositoryFormat4().open(self, _found=True)
743
class BzrDir5(BzrDirPreSplitOut):
744
"""A .bzr version 5 control object.
746
This is a deprecated format and may be removed after sept 2006.
749
def open_repository(self):
750
"""See BzrDir.open_repository."""
751
from bzrlib.repository import RepositoryFormat5
752
return RepositoryFormat5().open(self, _found=True)
754
def open_workingtree(self, _unsupported=False):
755
"""See BzrDir.create_workingtree."""
756
from bzrlib.workingtree import WorkingTreeFormat2
757
return WorkingTreeFormat2().open(self, _found=True)
760
class BzrDir6(BzrDirPreSplitOut):
761
"""A .bzr version 6 control object.
763
This is a deprecated format and may be removed after sept 2006.
766
def open_repository(self):
767
"""See BzrDir.open_repository."""
768
from bzrlib.repository import RepositoryFormat6
769
return RepositoryFormat6().open(self, _found=True)
771
def open_workingtree(self, _unsupported=False):
772
"""See BzrDir.create_workingtree."""
773
from bzrlib.workingtree import WorkingTreeFormat2
774
return WorkingTreeFormat2().open(self, _found=True)
777
class BzrDirMeta1(BzrDir):
778
"""A .bzr meta version 1 control object.
780
This is the first control object where the
781
individual aspects are really split out: there are separate repository,
782
workingtree and branch subdirectories and any subset of the three can be
783
present within a BzrDir.
786
def can_convert_format(self):
787
"""See BzrDir.can_convert_format()."""
790
def create_branch(self):
791
"""See BzrDir.create_branch."""
792
from bzrlib.branch import BranchFormat
793
return BranchFormat.get_default_format().initialize(self)
795
def create_repository(self, shared=False):
796
"""See BzrDir.create_repository."""
797
return self._format.repository_format.initialize(self, shared)
799
def create_workingtree(self, revision_id=None):
800
"""See BzrDir.create_workingtree."""
801
from bzrlib.workingtree import WorkingTreeFormat
802
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
804
def _get_mkdir_mode(self):
805
"""Figure out the mode to use when creating a bzrdir subdir."""
806
temp_control = LockableFiles(self.transport, '', TransportLock)
807
return temp_control._dir_mode
809
def get_branch_transport(self, branch_format):
810
"""See BzrDir.get_branch_transport()."""
811
if branch_format is None:
812
return self.transport.clone('branch')
814
branch_format.get_format_string()
815
except NotImplementedError:
816
raise errors.IncompatibleFormat(branch_format, self._format)
818
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
819
except errors.FileExists:
821
return self.transport.clone('branch')
823
def get_repository_transport(self, repository_format):
824
"""See BzrDir.get_repository_transport()."""
825
if repository_format is None:
826
return self.transport.clone('repository')
828
repository_format.get_format_string()
829
except NotImplementedError:
830
raise errors.IncompatibleFormat(repository_format, self._format)
832
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
833
except errors.FileExists:
835
return self.transport.clone('repository')
837
def get_workingtree_transport(self, workingtree_format):
838
"""See BzrDir.get_workingtree_transport()."""
839
if workingtree_format is None:
840
return self.transport.clone('checkout')
842
workingtree_format.get_format_string()
843
except NotImplementedError:
844
raise errors.IncompatibleFormat(workingtree_format, self._format)
846
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
847
except errors.FileExists:
849
return self.transport.clone('checkout')
851
def needs_format_conversion(self, format=None):
852
"""See BzrDir.needs_format_conversion()."""
854
format = BzrDirFormat.get_default_format()
855
if not isinstance(self._format, format.__class__):
856
# it is not a meta dir format, conversion is needed.
858
# we might want to push this down to the repository?
860
if not isinstance(self.open_repository()._format,
861
format.repository_format.__class__):
862
# the repository needs an upgrade.
864
except errors.NoRepositoryPresent:
866
# currently there are no other possible conversions for meta1 formats.
869
def open_branch(self, unsupported=False):
870
"""See BzrDir.open_branch."""
871
from bzrlib.branch import BranchFormat
872
format = BranchFormat.find_format(self)
873
self._check_supported(format, unsupported)
874
return format.open(self, _found=True)
876
def open_repository(self, unsupported=False):
877
"""See BzrDir.open_repository."""
878
from bzrlib.repository import RepositoryFormat
879
format = RepositoryFormat.find_format(self)
880
self._check_supported(format, unsupported)
881
return format.open(self, _found=True)
883
def open_workingtree(self, unsupported=False):
884
"""See BzrDir.open_workingtree."""
885
from bzrlib.workingtree import WorkingTreeFormat
886
format = WorkingTreeFormat.find_format(self)
887
self._check_supported(format, unsupported)
888
return format.open(self, _found=True)
891
class BzrDirFormat(object):
892
"""An encapsulation of the initialization and open routines for a format.
894
Formats provide three things:
895
* An initialization routine,
899
Formats are placed in an dict by their format string for reference
900
during bzrdir opening. These should be subclasses of BzrDirFormat
903
Once a format is deprecated, just deprecate the initialize and open
904
methods on the format class. Do not deprecate the object, as the
905
object will be created every system load.
908
_default_format = None
909
"""The default format used for new .bzr dirs."""
912
"""The known formats."""
914
_lock_file_name = 'branch-lock'
916
# _lock_class must be set in subclasses to the lock type, typ.
917
# TransportLock or LockDir
920
def find_format(klass, transport):
921
"""Return the format registered for URL."""
923
format_string = transport.get(".bzr/branch-format").read()
924
return klass._formats[format_string]
925
except errors.NoSuchFile:
926
raise errors.NotBranchError(path=transport.base)
928
raise errors.UnknownFormatError(format_string)
931
def get_default_format(klass):
932
"""Return the current default format."""
933
return klass._default_format
935
def get_format_string(self):
936
"""Return the ASCII format string that identifies this format."""
937
raise NotImplementedError(self.get_format_string)
939
def get_format_description(self):
940
"""Return the short description for this format."""
941
raise NotImplementedError(self.get_format_description)
943
def get_converter(self, format=None):
944
"""Return the converter to use to convert bzrdirs needing converts.
946
This returns a bzrlib.bzrdir.Converter object.
948
This should return the best upgrader to step this format towards the
949
current default format. In the case of plugins we can/shouold provide
950
some means for them to extend the range of returnable converters.
952
:param format: Optional format to override the default foramt of the
955
raise NotImplementedError(self.get_converter)
957
def initialize(self, url):
958
"""Create a bzr control dir at this url and return an opened copy.
960
Subclasses should typically override initialize_on_transport
961
instead of this method.
963
return self.initialize_on_transport(get_transport(url))
965
def initialize_on_transport(self, transport):
966
"""Initialize a new bzrdir in the base directory of a Transport."""
967
# Since we don'transport have a .bzr directory, inherit the
968
# mode from the root directory
969
temp_control = LockableFiles(transport, '', TransportLock)
970
temp_control._transport.mkdir('.bzr',
971
# FIXME: RBC 20060121 dont peek under
973
mode=temp_control._dir_mode)
974
file_mode = temp_control._file_mode
976
mutter('created control directory in ' + transport.base)
977
control = transport.clone('.bzr')
978
utf8_files = [('README',
979
"This is a Bazaar-NG control directory.\n"
980
"Do not change any files in this directory.\n"),
981
('branch-format', self.get_format_string()),
983
# NB: no need to escape relative paths that are url safe.
984
control_files = LockableFiles(control, self._lock_file_name,
986
control_files.create_lock()
987
control_files.lock_write()
989
for file, content in utf8_files:
990
control_files.put_utf8(file, content)
992
control_files.unlock()
993
return self.open(transport, _found=True)
995
def is_supported(self):
996
"""Is this format supported?
998
Supported formats must be initializable and openable.
999
Unsupported formats may not support initialization or committing or
1000
some other features depending on the reason for not being supported.
1004
def open(self, transport, _found=False):
1005
"""Return an instance of this format for the dir transport points at.
1007
_found is a private parameter, do not use it.
1010
assert isinstance(BzrDirFormat.find_format(transport),
1012
return self._open(transport)
1014
def _open(self, transport):
1015
"""Template method helper for opening BzrDirectories.
1017
This performs the actual open and any additional logic or parameter
1020
raise NotImplementedError(self._open)
1023
def register_format(klass, format):
1024
klass._formats[format.get_format_string()] = format
1027
def set_default_format(klass, format):
1028
klass._default_format = format
1031
return self.get_format_string()[:-1]
1034
def unregister_format(klass, format):
1035
assert klass._formats[format.get_format_string()] is format
1036
del klass._formats[format.get_format_string()]
1039
class BzrDirFormat4(BzrDirFormat):
1040
"""Bzr dir format 4.
1042
This format is a combined format for working tree, branch and repository.
1044
- Format 1 working trees [always]
1045
- Format 4 branches [always]
1046
- Format 4 repositories [always]
1048
This format is deprecated: it indexes texts using a text it which is
1049
removed in format 5; write support for this format has been removed.
1052
_lock_class = TransportLock
1054
def get_format_string(self):
1055
"""See BzrDirFormat.get_format_string()."""
1056
return "Bazaar-NG branch, format 0.0.4\n"
1058
def get_format_description(self):
1059
"""See BzrDirFormat.get_format_description()."""
1060
return "All-in-one format 4"
1062
def get_converter(self, format=None):
1063
"""See BzrDirFormat.get_converter()."""
1064
# there is one and only one upgrade path here.
1065
return ConvertBzrDir4To5()
1067
def initialize_on_transport(self, transport):
1068
"""Format 4 branches cannot be created."""
1069
raise errors.UninitializableFormat(self)
1071
def is_supported(self):
1072
"""Format 4 is not supported.
1074
It is not supported because the model changed from 4 to 5 and the
1075
conversion logic is expensive - so doing it on the fly was not
1080
def _open(self, transport):
1081
"""See BzrDirFormat._open."""
1082
return BzrDir4(transport, self)
1084
def __return_repository_format(self):
1085
"""Circular import protection."""
1086
from bzrlib.repository import RepositoryFormat4
1087
return RepositoryFormat4(self)
1088
repository_format = property(__return_repository_format)
1091
class BzrDirFormat5(BzrDirFormat):
1092
"""Bzr control format 5.
1094
This format is a combined format for working tree, branch and repository.
1096
- Format 2 working trees [always]
1097
- Format 4 branches [always]
1098
- Format 5 repositories [always]
1099
Unhashed stores in the repository.
1102
_lock_class = TransportLock
1104
def get_format_string(self):
1105
"""See BzrDirFormat.get_format_string()."""
1106
return "Bazaar-NG branch, format 5\n"
1108
def get_format_description(self):
1109
"""See BzrDirFormat.get_format_description()."""
1110
return "All-in-one format 5"
1112
def get_converter(self, format=None):
1113
"""See BzrDirFormat.get_converter()."""
1114
# there is one and only one upgrade path here.
1115
return ConvertBzrDir5To6()
1117
def _initialize_for_clone(self, url):
1118
return self.initialize_on_transport(get_transport(url), _cloning=True)
1120
def initialize_on_transport(self, transport, _cloning=False):
1121
"""Format 5 dirs always have working tree, branch and repository.
1123
Except when they are being cloned.
1125
from bzrlib.branch import BzrBranchFormat4
1126
from bzrlib.repository import RepositoryFormat5
1127
from bzrlib.workingtree import WorkingTreeFormat2
1128
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1129
RepositoryFormat5().initialize(result, _internal=True)
1131
BzrBranchFormat4().initialize(result)
1132
WorkingTreeFormat2().initialize(result)
1135
def _open(self, transport):
1136
"""See BzrDirFormat._open."""
1137
return BzrDir5(transport, self)
1139
def __return_repository_format(self):
1140
"""Circular import protection."""
1141
from bzrlib.repository import RepositoryFormat5
1142
return RepositoryFormat5(self)
1143
repository_format = property(__return_repository_format)
1146
class BzrDirFormat6(BzrDirFormat):
1147
"""Bzr control format 6.
1149
This format is a combined format for working tree, branch and repository.
1151
- Format 2 working trees [always]
1152
- Format 4 branches [always]
1153
- Format 6 repositories [always]
1156
_lock_class = TransportLock
1158
def get_format_string(self):
1159
"""See BzrDirFormat.get_format_string()."""
1160
return "Bazaar-NG branch, format 6\n"
1162
def get_format_description(self):
1163
"""See BzrDirFormat.get_format_description()."""
1164
return "All-in-one format 6"
1166
def get_converter(self, format=None):
1167
"""See BzrDirFormat.get_converter()."""
1168
# there is one and only one upgrade path here.
1169
return ConvertBzrDir6ToMeta()
1171
def _initialize_for_clone(self, url):
1172
return self.initialize_on_transport(get_transport(url), _cloning=True)
1174
def initialize_on_transport(self, transport, _cloning=False):
1175
"""Format 6 dirs always have working tree, branch and repository.
1177
Except when they are being cloned.
1179
from bzrlib.branch import BzrBranchFormat4
1180
from bzrlib.repository import RepositoryFormat6
1181
from bzrlib.workingtree import WorkingTreeFormat2
1182
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1183
RepositoryFormat6().initialize(result, _internal=True)
1185
BzrBranchFormat4().initialize(result)
1187
WorkingTreeFormat2().initialize(result)
1188
except errors.NotLocalUrl:
1189
# emulate pre-check behaviour for working tree and silently
1194
def _open(self, transport):
1195
"""See BzrDirFormat._open."""
1196
return BzrDir6(transport, self)
1198
def __return_repository_format(self):
1199
"""Circular import protection."""
1200
from bzrlib.repository import RepositoryFormat6
1201
return RepositoryFormat6(self)
1202
repository_format = property(__return_repository_format)
1205
class BzrDirMetaFormat1(BzrDirFormat):
1206
"""Bzr meta control format 1
1208
This is the first format with split out working tree, branch and repository
1211
- Format 3 working trees [optional]
1212
- Format 5 branches [optional]
1213
- Format 7 repositories [optional]
1216
_lock_class = LockDir
1218
def get_converter(self, format=None):
1219
"""See BzrDirFormat.get_converter()."""
1221
format = BzrDirFormat.get_default_format()
1222
if not isinstance(self, format.__class__):
1223
# converting away from metadir is not implemented
1224
raise NotImplementedError(self.get_converter)
1225
return ConvertMetaToMeta(format)
1227
def get_format_string(self):
1228
"""See BzrDirFormat.get_format_string()."""
1229
return "Bazaar-NG meta directory, format 1\n"
1231
def get_format_description(self):
1232
"""See BzrDirFormat.get_format_description()."""
1233
return "Meta directory format 1"
1235
def _open(self, transport):
1236
"""See BzrDirFormat._open."""
1237
return BzrDirMeta1(transport, self)
1239
def __return_repository_format(self):
1240
"""Circular import protection."""
1241
if getattr(self, '_repository_format', None):
1242
return self._repository_format
1243
from bzrlib.repository import RepositoryFormat
1244
return RepositoryFormat.get_default_format()
1246
def __set_repository_format(self, value):
1247
"""Allow changint the repository format for metadir formats."""
1248
self._repository_format = value
1250
repository_format = property(__return_repository_format, __set_repository_format)
1253
BzrDirFormat.register_format(BzrDirFormat4())
1254
BzrDirFormat.register_format(BzrDirFormat5())
1255
BzrDirFormat.register_format(BzrDirFormat6())
1256
__default_format = BzrDirMetaFormat1()
1257
BzrDirFormat.register_format(__default_format)
1258
BzrDirFormat.set_default_format(__default_format)
1261
class BzrDirTestProviderAdapter(object):
1262
"""A tool to generate a suite testing multiple bzrdir formats at once.
1264
This is done by copying the test once for each transport and injecting
1265
the transport_server, transport_readonly_server, and bzrdir_format
1266
classes into each copy. Each copy is also given a new id() to make it
1270
def __init__(self, transport_server, transport_readonly_server, formats):
1271
self._transport_server = transport_server
1272
self._transport_readonly_server = transport_readonly_server
1273
self._formats = formats
1275
def adapt(self, test):
1276
result = TestSuite()
1277
for format in self._formats:
1278
new_test = deepcopy(test)
1279
new_test.transport_server = self._transport_server
1280
new_test.transport_readonly_server = self._transport_readonly_server
1281
new_test.bzrdir_format = format
1282
def make_new_test_id():
1283
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1284
return lambda: new_id
1285
new_test.id = make_new_test_id()
1286
result.addTest(new_test)
1290
class ScratchDir(BzrDir6):
1291
"""Special test class: a bzrdir that cleans up itself..
1293
>>> d = ScratchDir()
1294
>>> base = d.transport.base
1297
>>> b.transport.__del__()
1302
def __init__(self, files=[], dirs=[], transport=None):
1303
"""Make a test branch.
1305
This creates a temporary directory and runs init-tree in it.
1307
If any files are listed, they are created in the working copy.
1309
if transport is None:
1310
transport = bzrlib.transport.local.ScratchTransport()
1311
# local import for scope restriction
1312
BzrDirFormat6().initialize(transport.base)
1313
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1314
self.create_repository()
1315
self.create_branch()
1316
self.create_workingtree()
1318
super(ScratchDir, self).__init__(transport, BzrDirFormat6())
1320
# BzrBranch creates a clone to .bzr and then forgets about the
1321
# original transport. A ScratchTransport() deletes itself and
1322
# everything underneath it when it goes away, so we need to
1323
# grab a local copy to prevent that from happening
1324
self._transport = transport
1327
self._transport.mkdir(d)
1330
self._transport.put(f, 'content of %s' % f)
1334
>>> orig = ScratchDir(files=["file1", "file2"])
1335
>>> os.listdir(orig.base)
1336
[u'.bzr', u'file1', u'file2']
1337
>>> clone = orig.clone()
1338
>>> if os.name != 'nt':
1339
... os.path.samefile(orig.base, clone.base)
1341
... orig.base == clone.base
1344
>>> os.listdir(clone.base)
1345
[u'.bzr', u'file1', u'file2']
1347
from shutil import copytree
1348
from bzrlib.osutils import mkdtemp
1351
copytree(self.base, base, symlinks=True)
1353
transport=bzrlib.transport.local.ScratchTransport(base))
1356
class Converter(object):
1357
"""Converts a disk format object from one format to another."""
1359
def convert(self, to_convert, pb):
1360
"""Perform the conversion of to_convert, giving feedback via pb.
1362
:param to_convert: The disk object to convert.
1363
:param pb: a progress bar to use for progress information.
1366
def step(self, message):
1367
"""Update the pb by a step."""
1369
self.pb.update(message, self.count, self.total)
1372
class ConvertBzrDir4To5(Converter):
1373
"""Converts format 4 bzr dirs to format 5."""
1376
super(ConvertBzrDir4To5, self).__init__()
1377
self.converted_revs = set()
1378
self.absent_revisions = set()
1382
def convert(self, to_convert, pb):
1383
"""See Converter.convert()."""
1384
self.bzrdir = to_convert
1386
self.pb.note('starting upgrade from format 4 to 5')
1387
if isinstance(self.bzrdir.transport, LocalTransport):
1388
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1389
self._convert_to_weaves()
1390
return BzrDir.open(self.bzrdir.root_transport.base)
1392
def _convert_to_weaves(self):
1393
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1396
stat = self.bzrdir.transport.stat('weaves')
1397
if not S_ISDIR(stat.st_mode):
1398
self.bzrdir.transport.delete('weaves')
1399
self.bzrdir.transport.mkdir('weaves')
1400
except errors.NoSuchFile:
1401
self.bzrdir.transport.mkdir('weaves')
1402
# deliberately not a WeaveFile as we want to build it up slowly.
1403
self.inv_weave = Weave('inventory')
1404
# holds in-memory weaves for all files
1405
self.text_weaves = {}
1406
self.bzrdir.transport.delete('branch-format')
1407
self.branch = self.bzrdir.open_branch()
1408
self._convert_working_inv()
1409
rev_history = self.branch.revision_history()
1410
# to_read is a stack holding the revisions we still need to process;
1411
# appending to it adds new highest-priority revisions
1412
self.known_revisions = set(rev_history)
1413
self.to_read = rev_history[-1:]
1415
rev_id = self.to_read.pop()
1416
if (rev_id not in self.revisions
1417
and rev_id not in self.absent_revisions):
1418
self._load_one_rev(rev_id)
1420
to_import = self._make_order()
1421
for i, rev_id in enumerate(to_import):
1422
self.pb.update('converting revision', i, len(to_import))
1423
self._convert_one_rev(rev_id)
1425
self._write_all_weaves()
1426
self._write_all_revs()
1427
self.pb.note('upgraded to weaves:')
1428
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1429
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1430
self.pb.note(' %6d texts', self.text_count)
1431
self._cleanup_spare_files_after_format4()
1432
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1434
def _cleanup_spare_files_after_format4(self):
1435
# FIXME working tree upgrade foo.
1436
for n in 'merged-patches', 'pending-merged-patches':
1438
## assert os.path.getsize(p) == 0
1439
self.bzrdir.transport.delete(n)
1440
except errors.NoSuchFile:
1442
self.bzrdir.transport.delete_tree('inventory-store')
1443
self.bzrdir.transport.delete_tree('text-store')
1445
def _convert_working_inv(self):
1446
inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1447
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1448
# FIXME inventory is a working tree change.
1449
self.branch.control_files.put('inventory', new_inv_xml)
1451
def _write_all_weaves(self):
1452
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1453
weave_transport = self.bzrdir.transport.clone('weaves')
1454
weaves = WeaveStore(weave_transport, prefixed=False)
1455
transaction = WriteTransaction()
1459
for file_id, file_weave in self.text_weaves.items():
1460
self.pb.update('writing weave', i, len(self.text_weaves))
1461
weaves._put_weave(file_id, file_weave, transaction)
1463
self.pb.update('inventory', 0, 1)
1464
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1465
self.pb.update('inventory', 1, 1)
1469
def _write_all_revs(self):
1470
"""Write all revisions out in new form."""
1471
self.bzrdir.transport.delete_tree('revision-store')
1472
self.bzrdir.transport.mkdir('revision-store')
1473
revision_transport = self.bzrdir.transport.clone('revision-store')
1475
_revision_store = TextRevisionStore(TextStore(revision_transport,
1479
transaction = bzrlib.transactions.WriteTransaction()
1480
for i, rev_id in enumerate(self.converted_revs):
1481
self.pb.update('write revision', i, len(self.converted_revs))
1482
_revision_store.add_revision(self.revisions[rev_id], transaction)
1486
def _load_one_rev(self, rev_id):
1487
"""Load a revision object into memory.
1489
Any parents not either loaded or abandoned get queued to be
1491
self.pb.update('loading revision',
1492
len(self.revisions),
1493
len(self.known_revisions))
1494
if not self.branch.repository.has_revision(rev_id):
1496
self.pb.note('revision {%s} not present in branch; '
1497
'will be converted as a ghost',
1499
self.absent_revisions.add(rev_id)
1501
rev = self.branch.repository._revision_store.get_revision(rev_id,
1502
self.branch.repository.get_transaction())
1503
for parent_id in rev.parent_ids:
1504
self.known_revisions.add(parent_id)
1505
self.to_read.append(parent_id)
1506
self.revisions[rev_id] = rev
1508
def _load_old_inventory(self, rev_id):
1509
assert rev_id not in self.converted_revs
1510
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1511
inv = serializer_v4.read_inventory_from_string(old_inv_xml)
1512
rev = self.revisions[rev_id]
1513
if rev.inventory_sha1:
1514
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1515
'inventory sha mismatch for {%s}' % rev_id
1518
def _load_updated_inventory(self, rev_id):
1519
assert rev_id in self.converted_revs
1520
inv_xml = self.inv_weave.get_text(rev_id)
1521
inv = serializer_v5.read_inventory_from_string(inv_xml)
1524
def _convert_one_rev(self, rev_id):
1525
"""Convert revision and all referenced objects to new format."""
1526
rev = self.revisions[rev_id]
1527
inv = self._load_old_inventory(rev_id)
1528
present_parents = [p for p in rev.parent_ids
1529
if p not in self.absent_revisions]
1530
self._convert_revision_contents(rev, inv, present_parents)
1531
self._store_new_weave(rev, inv, present_parents)
1532
self.converted_revs.add(rev_id)
1534
def _store_new_weave(self, rev, inv, present_parents):
1535
# the XML is now updated with text versions
1539
if ie.kind == 'root_directory':
1541
assert hasattr(ie, 'revision'), \
1542
'no revision on {%s} in {%s}' % \
1543
(file_id, rev.revision_id)
1544
new_inv_xml = serializer_v5.write_inventory_to_string(inv)
1545
new_inv_sha1 = sha_string(new_inv_xml)
1546
self.inv_weave.add_lines(rev.revision_id,
1548
new_inv_xml.splitlines(True))
1549
rev.inventory_sha1 = new_inv_sha1
1551
def _convert_revision_contents(self, rev, inv, present_parents):
1552
"""Convert all the files within a revision.
1554
Also upgrade the inventory to refer to the text revision ids."""
1555
rev_id = rev.revision_id
1556
mutter('converting texts of revision {%s}',
1558
parent_invs = map(self._load_updated_inventory, present_parents)
1561
self._convert_file_version(rev, ie, parent_invs)
1563
def _convert_file_version(self, rev, ie, parent_invs):
1564
"""Convert one version of one file.
1566
The file needs to be added into the weave if it is a merge
1567
of >=2 parents or if it's changed from its parent.
1569
if ie.kind == 'root_directory':
1571
file_id = ie.file_id
1572
rev_id = rev.revision_id
1573
w = self.text_weaves.get(file_id)
1576
self.text_weaves[file_id] = w
1577
text_changed = False
1578
previous_entries = ie.find_previous_heads(parent_invs,
1582
for old_revision in previous_entries:
1583
# if this fails, its a ghost ?
1584
assert old_revision in self.converted_revs
1585
self.snapshot_ie(previous_entries, ie, w, rev_id)
1587
assert getattr(ie, 'revision', None) is not None
1589
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1590
# TODO: convert this logic, which is ~= snapshot to
1591
# a call to:. This needs the path figured out. rather than a work_tree
1592
# a v4 revision_tree can be given, or something that looks enough like
1593
# one to give the file content to the entry if it needs it.
1594
# and we need something that looks like a weave store for snapshot to
1596
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1597
if len(previous_revisions) == 1:
1598
previous_ie = previous_revisions.values()[0]
1599
if ie._unchanged(previous_ie):
1600
ie.revision = previous_ie.revision
1603
text = self.branch.repository.text_store.get(ie.text_id)
1604
file_lines = text.readlines()
1605
assert sha_strings(file_lines) == ie.text_sha1
1606
assert sum(map(len, file_lines)) == ie.text_size
1607
w.add_lines(rev_id, previous_revisions, file_lines)
1608
self.text_count += 1
1610
w.add_lines(rev_id, previous_revisions, [])
1611
ie.revision = rev_id
1613
def _make_order(self):
1614
"""Return a suitable order for importing revisions.
1616
The order must be such that an revision is imported after all
1617
its (present) parents.
1619
todo = set(self.revisions.keys())
1620
done = self.absent_revisions.copy()
1623
# scan through looking for a revision whose parents
1625
for rev_id in sorted(list(todo)):
1626
rev = self.revisions[rev_id]
1627
parent_ids = set(rev.parent_ids)
1628
if parent_ids.issubset(done):
1629
# can take this one now
1630
order.append(rev_id)
1636
class ConvertBzrDir5To6(Converter):
1637
"""Converts format 5 bzr dirs to format 6."""
1639
def convert(self, to_convert, pb):
1640
"""See Converter.convert()."""
1641
self.bzrdir = to_convert
1643
self.pb.note('starting upgrade from format 5 to 6')
1644
self._convert_to_prefixed()
1645
return BzrDir.open(self.bzrdir.root_transport.base)
1647
def _convert_to_prefixed(self):
1648
from bzrlib.store import TransportStore
1649
self.bzrdir.transport.delete('branch-format')
1650
for store_name in ["weaves", "revision-store"]:
1651
self.pb.note("adding prefixes to %s" % store_name)
1652
store_transport = self.bzrdir.transport.clone(store_name)
1653
store = TransportStore(store_transport, prefixed=True)
1654
for urlfilename in store_transport.list_dir('.'):
1655
filename = urlutils.unescape(urlfilename)
1656
if (filename.endswith(".weave") or
1657
filename.endswith(".gz") or
1658
filename.endswith(".sig")):
1659
file_id = os.path.splitext(filename)[0]
1662
prefix_dir = store.hash_prefix(file_id)
1663
# FIXME keep track of the dirs made RBC 20060121
1665
store_transport.move(filename, prefix_dir + '/' + filename)
1666
except errors.NoSuchFile: # catches missing dirs strangely enough
1667
store_transport.mkdir(prefix_dir)
1668
store_transport.move(filename, prefix_dir + '/' + filename)
1669
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1672
class ConvertBzrDir6ToMeta(Converter):
1673
"""Converts format 6 bzr dirs to metadirs."""
1675
def convert(self, to_convert, pb):
1676
"""See Converter.convert()."""
1677
self.bzrdir = to_convert
1680
self.total = 20 # the steps we know about
1681
self.garbage_inventories = []
1683
self.pb.note('starting upgrade from format 6 to metadir')
1684
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1685
# its faster to move specific files around than to open and use the apis...
1686
# first off, nuke ancestry.weave, it was never used.
1688
self.step('Removing ancestry.weave')
1689
self.bzrdir.transport.delete('ancestry.weave')
1690
except errors.NoSuchFile:
1692
# find out whats there
1693
self.step('Finding branch files')
1694
last_revision = self.bzrdir.open_branch().last_revision()
1695
bzrcontents = self.bzrdir.transport.list_dir('.')
1696
for name in bzrcontents:
1697
if name.startswith('basis-inventory.'):
1698
self.garbage_inventories.append(name)
1699
# create new directories for repository, working tree and branch
1700
self.dir_mode = self.bzrdir._control_files._dir_mode
1701
self.file_mode = self.bzrdir._control_files._file_mode
1702
repository_names = [('inventory.weave', True),
1703
('revision-store', True),
1705
self.step('Upgrading repository ')
1706
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1707
self.make_lock('repository')
1708
# we hard code the formats here because we are converting into
1709
# the meta format. The meta format upgrader can take this to a
1710
# future format within each component.
1711
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1712
for entry in repository_names:
1713
self.move_entry('repository', entry)
1715
self.step('Upgrading branch ')
1716
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1717
self.make_lock('branch')
1718
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1719
branch_files = [('revision-history', True),
1720
('branch-name', True),
1722
for entry in branch_files:
1723
self.move_entry('branch', entry)
1725
self.step('Upgrading working tree')
1726
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1727
self.make_lock('checkout')
1728
self.put_format('checkout', bzrlib.workingtree.WorkingTreeFormat3())
1729
self.bzrdir.transport.delete_multi(self.garbage_inventories, self.pb)
1730
checkout_files = [('pending-merges', True),
1731
('inventory', True),
1732
('stat-cache', False)]
1733
for entry in checkout_files:
1734
self.move_entry('checkout', entry)
1735
if last_revision is not None:
1736
self.bzrdir._control_files.put_utf8('checkout/last-revision',
1738
self.bzrdir._control_files.put_utf8('branch-format', BzrDirMetaFormat1().get_format_string())
1739
return BzrDir.open(self.bzrdir.root_transport.base)
1741
def make_lock(self, name):
1742
"""Make a lock for the new control dir name."""
1743
self.step('Make %s lock' % name)
1744
ld = LockDir(self.bzrdir.transport,
1746
file_modebits=self.file_mode,
1747
dir_modebits=self.dir_mode)
1750
def move_entry(self, new_dir, entry):
1751
"""Move then entry name into new_dir."""
1753
mandatory = entry[1]
1754
self.step('Moving %s' % name)
1756
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1757
except errors.NoSuchFile:
1761
def put_format(self, dirname, format):
1762
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1765
class ConvertMetaToMeta(Converter):
1766
"""Converts the components of metadirs."""
1768
def __init__(self, target_format):
1769
"""Create a metadir to metadir converter.
1771
:param target_format: The final metadir format that is desired.
1773
self.target_format = target_format
1775
def convert(self, to_convert, pb):
1776
"""See Converter.convert()."""
1777
self.bzrdir = to_convert
1781
self.step('checking repository format')
1783
repo = self.bzrdir.open_repository()
1784
except errors.NoRepositoryPresent:
1787
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1788
from bzrlib.repository import CopyConverter
1789
self.pb.note('starting repository conversion')
1790
converter = CopyConverter(self.target_format.repository_format)
1791
converter.convert(repo, pb)