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
# TODO: remove unittest dependency; put that stuff inside the test suite
25
# TODO: Can we move specific formats into separate modules to make this file
28
from cStringIO import StringIO
31
from bzrlib.lazy_import import lazy_import
32
lazy_import(globals(), """
33
from copy import deepcopy
34
from stat import S_ISDIR
43
revision as _mod_revision,
48
from bzrlib.osutils import (
53
from bzrlib.store.revision.text import TextRevisionStore
54
from bzrlib.store.text import TextStore
55
from bzrlib.store.versioned import WeaveStore
56
from bzrlib.transactions import WriteTransaction
57
from bzrlib.transport import get_transport
58
from bzrlib.weave import Weave
61
from bzrlib.trace import mutter
62
from bzrlib.transport.local import LocalTransport
66
"""A .bzr control diretory.
68
BzrDir instances let you create or open any of the things that can be
69
found within .bzr - checkouts, branches and repositories.
72
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
74
a transport connected to the directory this bzr was opened from.
78
"""Invoke break_lock on the first object in the bzrdir.
80
If there is a tree, the tree is opened and break_lock() called.
81
Otherwise, branch is tried, and finally repository.
83
# XXX: This seems more like a UI function than something that really
84
# belongs in this class.
86
thing_to_unlock = self.open_workingtree()
87
except (errors.NotLocalUrl, errors.NoWorkingTree):
89
thing_to_unlock = self.open_branch()
90
except errors.NotBranchError:
92
thing_to_unlock = self.open_repository()
93
except errors.NoRepositoryPresent:
95
thing_to_unlock.break_lock()
97
def can_convert_format(self):
98
"""Return true if this bzrdir is one whose format we can convert from."""
101
def check_conversion_target(self, target_format):
102
target_repo_format = target_format.repository_format
103
source_repo_format = self._format.repository_format
104
source_repo_format.check_conversion_target(target_repo_format)
107
def _check_supported(format, allow_unsupported):
108
"""Check whether format is a supported format.
110
If allow_unsupported is True, this is a no-op.
112
if not allow_unsupported and not format.is_supported():
113
# see open_downlevel to open legacy branches.
114
raise errors.UnsupportedFormatError(format=format)
116
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
117
"""Clone this bzrdir and its contents to url verbatim.
119
If urls last component does not exist, it will be created.
121
if revision_id is not None, then the clone operation may tune
122
itself to download less data.
123
:param force_new_repo: Do not use a shared repository for the target
124
even if one is available.
127
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
128
result = self._format.initialize(url)
130
local_repo = self.find_repository()
131
except errors.NoRepositoryPresent:
134
# may need to copy content in
136
result_repo = local_repo.clone(
138
revision_id=revision_id,
140
result_repo.set_make_working_trees(local_repo.make_working_trees())
143
result_repo = result.find_repository()
144
# fetch content this dir needs.
146
# XXX FIXME RBC 20060214 need tests for this when the basis
148
result_repo.fetch(basis_repo, revision_id=revision_id)
149
result_repo.fetch(local_repo, revision_id=revision_id)
150
except errors.NoRepositoryPresent:
151
# needed to make one anyway.
152
result_repo = local_repo.clone(
154
revision_id=revision_id,
156
result_repo.set_make_working_trees(local_repo.make_working_trees())
157
# 1 if there is a branch present
158
# make sure its content is available in the target repository
161
self.open_branch().clone(result, revision_id=revision_id)
162
except errors.NotBranchError:
165
self.open_workingtree().clone(result, basis=basis_tree)
166
except (errors.NoWorkingTree, errors.NotLocalUrl):
170
def _get_basis_components(self, basis):
171
"""Retrieve the basis components that are available at basis."""
173
return None, None, None
175
basis_tree = basis.open_workingtree()
176
basis_branch = basis_tree.branch
177
basis_repo = basis_branch.repository
178
except (errors.NoWorkingTree, errors.NotLocalUrl):
181
basis_branch = basis.open_branch()
182
basis_repo = basis_branch.repository
183
except errors.NotBranchError:
186
basis_repo = basis.open_repository()
187
except errors.NoRepositoryPresent:
189
return basis_repo, basis_branch, basis_tree
191
# TODO: This should be given a Transport, and should chdir up; otherwise
192
# this will open a new connection.
193
def _make_tail(self, url):
194
head, tail = urlutils.split(url)
195
if tail and tail != '.':
196
t = get_transport(head)
199
except errors.FileExists:
202
# TODO: Should take a Transport
204
def create(cls, base):
205
"""Create a new BzrDir at the url 'base'.
207
This will call the current default formats initialize with base
208
as the only parameter.
210
If you need a specific format, consider creating an instance
211
of that and calling initialize().
213
if cls is not BzrDir:
214
raise AssertionError("BzrDir.create always creates the default format, "
215
"not one of %r" % cls)
216
head, tail = urlutils.split(base)
217
if tail and tail != '.':
218
t = get_transport(head)
221
except errors.FileExists:
223
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
225
def create_branch(self):
226
"""Create a branch in this BzrDir.
228
The bzrdirs format will control what branch format is created.
229
For more control see BranchFormatXX.create(a_bzrdir).
231
raise NotImplementedError(self.create_branch)
234
def create_branch_and_repo(base, force_new_repo=False):
235
"""Create a new BzrDir, Branch and Repository at the url 'base'.
237
This will use the current default BzrDirFormat, and use whatever
238
repository format that that uses via bzrdir.create_branch and
239
create_repository. If a shared repository is available that is used
242
The created Branch object is returned.
244
:param base: The URL to create the branch at.
245
:param force_new_repo: If True a new repository is always created.
247
bzrdir = BzrDir.create(base)
248
bzrdir._find_or_create_repository(force_new_repo)
249
return bzrdir.create_branch()
251
def _find_or_create_repository(self, force_new_repo):
252
"""Create a new repository if needed, returning the repository."""
254
return self.create_repository()
256
return self.find_repository()
257
except errors.NoRepositoryPresent:
258
return self.create_repository()
261
def create_branch_convenience(base, force_new_repo=False,
262
force_new_tree=None, format=None):
263
"""Create a new BzrDir, Branch and Repository at the url 'base'.
265
This is a convenience function - it will use an existing repository
266
if possible, can be told explicitly whether to create a working tree or
269
This will use the current default BzrDirFormat, and use whatever
270
repository format that that uses via bzrdir.create_branch and
271
create_repository. If a shared repository is available that is used
272
preferentially. Whatever repository is used, its tree creation policy
275
The created Branch object is returned.
276
If a working tree cannot be made due to base not being a file:// url,
277
no error is raised unless force_new_tree is True, in which case no
278
data is created on disk and NotLocalUrl is raised.
280
:param base: The URL to create the branch at.
281
:param force_new_repo: If True a new repository is always created.
282
:param force_new_tree: If True or False force creation of a tree or
283
prevent such creation respectively.
284
:param format: Override for the for the bzrdir format to create
287
# check for non local urls
288
t = get_transport(safe_unicode(base))
289
if not isinstance(t, LocalTransport):
290
raise errors.NotLocalUrl(base)
292
bzrdir = BzrDir.create(base)
294
bzrdir = format.initialize(base)
295
repo = bzrdir._find_or_create_repository(force_new_repo)
296
result = bzrdir.create_branch()
297
if force_new_tree or (repo.make_working_trees() and
298
force_new_tree is None):
300
bzrdir.create_workingtree()
301
except errors.NotLocalUrl:
306
def create_repository(base, shared=False):
307
"""Create a new BzrDir and Repository at the url 'base'.
309
This will use the current default BzrDirFormat, and use whatever
310
repository format that that uses for bzrdirformat.create_repository.
312
:param shared: Create a shared repository rather than a standalone
314
The Repository object is returned.
316
This must be overridden as an instance method in child classes, where
317
it should take no parameters and construct whatever repository format
318
that child class desires.
320
bzrdir = BzrDir.create(base)
321
return bzrdir.create_repository(shared)
324
def create_standalone_workingtree(base):
325
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
327
'base' must be a local path or a file:// url.
329
This will use the current default BzrDirFormat, and use whatever
330
repository format that that uses for bzrdirformat.create_workingtree,
331
create_branch and create_repository.
333
:return: The WorkingTree object.
335
t = get_transport(safe_unicode(base))
336
if not isinstance(t, LocalTransport):
337
raise errors.NotLocalUrl(base)
338
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
339
force_new_repo=True).bzrdir
340
return bzrdir.create_workingtree()
342
def create_workingtree(self, revision_id=None):
343
"""Create a working tree at this BzrDir.
345
revision_id: create it as of this revision id.
347
raise NotImplementedError(self.create_workingtree)
349
def destroy_workingtree(self):
350
"""Destroy the working tree at this BzrDir.
352
Formats that do not support this may raise UnsupportedOperation.
354
raise NotImplementedError(self.destroy_workingtree)
356
def destroy_workingtree_metadata(self):
357
"""Destroy the control files for the working tree at this BzrDir.
359
The contents of working tree files are not affected.
360
Formats that do not support this may raise UnsupportedOperation.
362
raise NotImplementedError(self.destroy_workingtree_metadata)
364
def find_repository(self):
365
"""Find the repository that should be used for a_bzrdir.
367
This does not require a branch as we use it to find the repo for
368
new branches as well as to hook existing branches up to their
372
return self.open_repository()
373
except errors.NoRepositoryPresent:
375
next_transport = self.root_transport.clone('..')
377
# find the next containing bzrdir
379
found_bzrdir = BzrDir.open_containing_from_transport(
381
except errors.NotBranchError:
383
raise errors.NoRepositoryPresent(self)
384
# does it have a repository ?
386
repository = found_bzrdir.open_repository()
387
except errors.NoRepositoryPresent:
388
next_transport = found_bzrdir.root_transport.clone('..')
389
if (found_bzrdir.root_transport.base == next_transport.base):
390
# top of the file system
394
if ((found_bzrdir.root_transport.base ==
395
self.root_transport.base) or repository.is_shared()):
398
raise errors.NoRepositoryPresent(self)
399
raise errors.NoRepositoryPresent(self)
401
def get_branch_reference(self):
402
"""Return the referenced URL for the branch in this bzrdir.
404
:raises NotBranchError: If there is no Branch.
405
:return: The URL the branch in this bzrdir references if it is a
406
reference branch, or None for regular branches.
410
def get_branch_transport(self, branch_format):
411
"""Get the transport for use by branch format in this BzrDir.
413
Note that bzr dirs that do not support format strings will raise
414
IncompatibleFormat if the branch format they are given has
415
a format string, and vice versa.
417
If branch_format is None, the transport is returned with no
418
checking. if it is not None, then the returned transport is
419
guaranteed to point to an existing directory ready for use.
421
raise NotImplementedError(self.get_branch_transport)
423
def get_repository_transport(self, repository_format):
424
"""Get the transport for use by repository format in this BzrDir.
426
Note that bzr dirs that do not support format strings will raise
427
IncompatibleFormat if the repository format they are given has
428
a format string, and vice versa.
430
If repository_format is None, the transport is returned with no
431
checking. if it is not None, then the returned transport is
432
guaranteed to point to an existing directory ready for use.
434
raise NotImplementedError(self.get_repository_transport)
436
def get_workingtree_transport(self, tree_format):
437
"""Get the transport for use by workingtree format in this BzrDir.
439
Note that bzr dirs that do not support format strings will raise
440
IncompatibleFormat if the workingtree format they are given has
441
a format string, and vice versa.
443
If workingtree_format is None, the transport is returned with no
444
checking. if it is not None, then the returned transport is
445
guaranteed to point to an existing directory ready for use.
447
raise NotImplementedError(self.get_workingtree_transport)
449
def __init__(self, _transport, _format):
450
"""Initialize a Bzr control dir object.
452
Only really common logic should reside here, concrete classes should be
453
made with varying behaviours.
455
:param _format: the format that is creating this BzrDir instance.
456
:param _transport: the transport this dir is based at.
458
self._format = _format
459
self.transport = _transport.clone('.bzr')
460
self.root_transport = _transport
462
def is_control_filename(self, filename):
463
"""True if filename is the name of a path which is reserved for bzrdir's.
465
:param filename: A filename within the root transport of this bzrdir.
467
This is true IF and ONLY IF the filename is part of the namespace reserved
468
for bzr control dirs. Currently this is the '.bzr' directory in the root
469
of the root_transport. it is expected that plugins will need to extend
470
this in the future - for instance to make bzr talk with svn working
473
# this might be better on the BzrDirFormat class because it refers to
474
# all the possible bzrdir disk formats.
475
# This method is tested via the workingtree is_control_filename tests-
476
# it was extracted from WorkingTree.is_control_filename. If the methods
477
# contract is extended beyond the current trivial implementation please
478
# add new tests for it to the appropriate place.
479
return filename == '.bzr' or filename.startswith('.bzr/')
481
def needs_format_conversion(self, format=None):
482
"""Return true if this bzrdir needs convert_format run on it.
484
For instance, if the repository format is out of date but the
485
branch and working tree are not, this should return True.
487
:param format: Optional parameter indicating a specific desired
488
format we plan to arrive at.
490
raise NotImplementedError(self.needs_format_conversion)
493
def open_unsupported(base):
494
"""Open a branch which is not supported."""
495
return BzrDir.open(base, _unsupported=True)
498
def open(base, _unsupported=False):
499
"""Open an existing bzrdir, rooted at 'base' (url)
501
_unsupported is a private parameter to the BzrDir class.
503
t = get_transport(base)
504
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
507
def open_from_transport(transport, _unsupported=False):
508
"""Open a bzrdir within a particular directory.
510
:param transport: Transport containing the bzrdir.
511
:param _unsupported: private.
513
format = BzrDirFormat.find_format(transport)
514
BzrDir._check_supported(format, _unsupported)
515
return format.open(transport, _found=True)
517
def open_branch(self, unsupported=False):
518
"""Open the branch object at this BzrDir if one is present.
520
If unsupported is True, then no longer supported branch formats can
523
TODO: static convenience version of this?
525
raise NotImplementedError(self.open_branch)
528
def open_containing(url):
529
"""Open an existing branch which contains url.
531
:param url: url to search from.
532
See open_containing_from_transport for more detail.
534
return BzrDir.open_containing_from_transport(get_transport(url))
537
def open_containing_from_transport(a_transport):
538
"""Open an existing branch which contains a_transport.base
540
This probes for a branch at a_transport, and searches upwards from there.
542
Basically we keep looking up until we find the control directory or
543
run into the root. If there isn't one, raises NotBranchError.
544
If there is one and it is either an unrecognised format or an unsupported
545
format, UnknownFormatError or UnsupportedFormatError are raised.
546
If there is one, it is returned, along with the unused portion of url.
548
:return: The BzrDir that contains the path, and a Unicode path
549
for the rest of the URL.
551
# this gets the normalised url back. I.e. '.' -> the full path.
552
url = a_transport.base
555
result = BzrDir.open_from_transport(a_transport)
556
return result, urlutils.unescape(a_transport.relpath(url))
557
except errors.NotBranchError, e:
559
new_t = a_transport.clone('..')
560
if new_t.base == a_transport.base:
561
# reached the root, whatever that may be
562
raise errors.NotBranchError(path=url)
565
def open_repository(self, _unsupported=False):
566
"""Open the repository object at this BzrDir if one is present.
568
This will not follow the Branch object pointer - its strictly a direct
569
open facility. Most client code should use open_branch().repository to
572
_unsupported is a private parameter, not part of the api.
573
TODO: static convenience version of this?
575
raise NotImplementedError(self.open_repository)
577
def open_workingtree(self, _unsupported=False):
578
"""Open the workingtree object at this BzrDir if one is present.
580
TODO: static convenience version of this?
582
raise NotImplementedError(self.open_workingtree)
584
def has_branch(self):
585
"""Tell if this bzrdir contains a branch.
587
Note: if you're going to open the branch, you should just go ahead
588
and try, and not ask permission first. (This method just opens the
589
branch and discards it, and that's somewhat expensive.)
594
except errors.NotBranchError:
597
def has_workingtree(self):
598
"""Tell if this bzrdir contains a working tree.
600
This will still raise an exception if the bzrdir has a workingtree that
601
is remote & inaccessible.
603
Note: if you're going to open the working tree, you should just go ahead
604
and try, and not ask permission first. (This method just opens the
605
workingtree and discards it, and that's somewhat expensive.)
608
self.open_workingtree()
610
except errors.NoWorkingTree:
613
def cloning_metadir(self, basis=None):
614
"""Produce a metadir suitable for cloning with"""
615
def related_repository(bzrdir):
617
branch = bzrdir.open_branch()
618
return branch.repository
619
except errors.NotBranchError:
621
return bzrdir.open_repository()
622
result_format = self._format.__class__()
625
source_repository = related_repository(self)
626
except errors.NoRepositoryPresent:
629
source_repository = related_repository(self)
630
result_format.repository_format = source_repository._format
631
except errors.NoRepositoryPresent:
635
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
636
"""Create a copy of this bzrdir prepared for use as a new line of
639
If urls last component does not exist, it will be created.
641
Attributes related to the identity of the source branch like
642
branch nickname will be cleaned, a working tree is created
643
whether one existed before or not; and a local branch is always
646
if revision_id is not None, then the clone operation may tune
647
itself to download less data.
650
cloning_format = self.cloning_metadir(basis)
651
result = cloning_format.initialize(url)
652
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
654
source_branch = self.open_branch()
655
source_repository = source_branch.repository
656
except errors.NotBranchError:
659
source_repository = self.open_repository()
660
except errors.NoRepositoryPresent:
661
# copy the entire basis one if there is one
662
# but there is no repository.
663
source_repository = basis_repo
668
result_repo = result.find_repository()
669
except errors.NoRepositoryPresent:
671
if source_repository is None and result_repo is not None:
673
elif source_repository is None and result_repo is None:
674
# no repo available, make a new one
675
result.create_repository()
676
elif source_repository is not None and result_repo is None:
677
# have source, and want to make a new target repo
678
# we don't clone the repo because that preserves attributes
679
# like is_shared(), and we have not yet implemented a
680
# repository sprout().
681
result_repo = result.create_repository()
682
if result_repo is not None:
683
# fetch needed content into target.
685
# XXX FIXME RBC 20060214 need tests for this when the basis
687
result_repo.fetch(basis_repo, revision_id=revision_id)
688
if source_repository is not None:
689
result_repo.fetch(source_repository, revision_id=revision_id)
690
if source_branch is not None:
691
source_branch.sprout(result, revision_id=revision_id)
693
result.create_branch()
694
# TODO: jam 20060426 we probably need a test in here in the
695
# case that the newly sprouted branch is a remote one
696
if result_repo is None or result_repo.make_working_trees():
697
wt = result.create_workingtree()
698
if wt.inventory.root is None:
700
wt.set_root_id(self.open_workingtree.get_root_id())
701
except errors.NoWorkingTree:
706
class BzrDirPreSplitOut(BzrDir):
707
"""A common class for the all-in-one formats."""
709
def __init__(self, _transport, _format):
710
"""See BzrDir.__init__."""
711
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
712
assert self._format._lock_class == lockable_files.TransportLock
713
assert self._format._lock_file_name == 'branch-lock'
714
self._control_files = lockable_files.LockableFiles(
715
self.get_branch_transport(None),
716
self._format._lock_file_name,
717
self._format._lock_class)
719
def break_lock(self):
720
"""Pre-splitout bzrdirs do not suffer from stale locks."""
721
raise NotImplementedError(self.break_lock)
723
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
724
"""See BzrDir.clone()."""
725
from bzrlib.workingtree import WorkingTreeFormat2
727
result = self._format._initialize_for_clone(url)
728
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
729
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
730
from_branch = self.open_branch()
731
from_branch.clone(result, revision_id=revision_id)
733
self.open_workingtree().clone(result, basis=basis_tree)
734
except errors.NotLocalUrl:
735
# make a new one, this format always has to have one.
737
WorkingTreeFormat2().initialize(result)
738
except errors.NotLocalUrl:
739
# but we cannot do it for remote trees.
740
to_branch = result.open_branch()
741
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
744
def create_branch(self):
745
"""See BzrDir.create_branch."""
746
return self.open_branch()
748
def create_repository(self, shared=False):
749
"""See BzrDir.create_repository."""
751
raise errors.IncompatibleFormat('shared repository', self._format)
752
return self.open_repository()
754
def create_workingtree(self, revision_id=None):
755
"""See BzrDir.create_workingtree."""
756
# this looks buggy but is not -really-
757
# clone and sprout will have set the revision_id
758
# and that will have set it for us, its only
759
# specific uses of create_workingtree in isolation
760
# that can do wonky stuff here, and that only
761
# happens for creating checkouts, which cannot be
762
# done on this format anyway. So - acceptable wart.
763
result = self.open_workingtree()
764
if revision_id is not None:
765
if revision_id == _mod_revision.NULL_REVISION:
766
result.set_parent_ids([])
768
result.set_parent_ids([revision_id])
771
def destroy_workingtree(self):
772
"""See BzrDir.destroy_workingtree."""
773
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
775
def destroy_workingtree_metadata(self):
776
"""See BzrDir.destroy_workingtree_metadata."""
777
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
780
def get_branch_transport(self, branch_format):
781
"""See BzrDir.get_branch_transport()."""
782
if branch_format is None:
783
return self.transport
785
branch_format.get_format_string()
786
except NotImplementedError:
787
return self.transport
788
raise errors.IncompatibleFormat(branch_format, self._format)
790
def get_repository_transport(self, repository_format):
791
"""See BzrDir.get_repository_transport()."""
792
if repository_format is None:
793
return self.transport
795
repository_format.get_format_string()
796
except NotImplementedError:
797
return self.transport
798
raise errors.IncompatibleFormat(repository_format, self._format)
800
def get_workingtree_transport(self, workingtree_format):
801
"""See BzrDir.get_workingtree_transport()."""
802
if workingtree_format is None:
803
return self.transport
805
workingtree_format.get_format_string()
806
except NotImplementedError:
807
return self.transport
808
raise errors.IncompatibleFormat(workingtree_format, self._format)
810
def needs_format_conversion(self, format=None):
811
"""See BzrDir.needs_format_conversion()."""
812
# if the format is not the same as the system default,
813
# an upgrade is needed.
815
format = BzrDirFormat.get_default_format()
816
return not isinstance(self._format, format.__class__)
818
def open_branch(self, unsupported=False):
819
"""See BzrDir.open_branch."""
820
from bzrlib.branch import BzrBranchFormat4
821
format = BzrBranchFormat4()
822
self._check_supported(format, unsupported)
823
return format.open(self, _found=True)
825
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
826
"""See BzrDir.sprout()."""
827
from bzrlib.workingtree import WorkingTreeFormat2
829
result = self._format._initialize_for_clone(url)
830
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
832
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
833
except errors.NoRepositoryPresent:
836
self.open_branch().sprout(result, revision_id=revision_id)
837
except errors.NotBranchError:
839
# we always want a working tree
840
WorkingTreeFormat2().initialize(result)
844
class BzrDir4(BzrDirPreSplitOut):
845
"""A .bzr version 4 control object.
847
This is a deprecated format and may be removed after sept 2006.
850
def create_repository(self, shared=False):
851
"""See BzrDir.create_repository."""
852
return self._format.repository_format.initialize(self, shared)
854
def needs_format_conversion(self, format=None):
855
"""Format 4 dirs are always in need of conversion."""
858
def open_repository(self):
859
"""See BzrDir.open_repository."""
860
from bzrlib.repository import RepositoryFormat4
861
return RepositoryFormat4().open(self, _found=True)
864
class BzrDir5(BzrDirPreSplitOut):
865
"""A .bzr version 5 control object.
867
This is a deprecated format and may be removed after sept 2006.
870
def open_repository(self):
871
"""See BzrDir.open_repository."""
872
from bzrlib.repository import RepositoryFormat5
873
return RepositoryFormat5().open(self, _found=True)
875
def open_workingtree(self, _unsupported=False):
876
"""See BzrDir.create_workingtree."""
877
from bzrlib.workingtree import WorkingTreeFormat2
878
return WorkingTreeFormat2().open(self, _found=True)
881
class BzrDir6(BzrDirPreSplitOut):
882
"""A .bzr version 6 control object.
884
This is a deprecated format and may be removed after sept 2006.
887
def open_repository(self):
888
"""See BzrDir.open_repository."""
889
from bzrlib.repository import RepositoryFormat6
890
return RepositoryFormat6().open(self, _found=True)
892
def open_workingtree(self, _unsupported=False):
893
"""See BzrDir.create_workingtree."""
894
from bzrlib.workingtree import WorkingTreeFormat2
895
return WorkingTreeFormat2().open(self, _found=True)
898
class BzrDirMeta1(BzrDir):
899
"""A .bzr meta version 1 control object.
901
This is the first control object where the
902
individual aspects are really split out: there are separate repository,
903
workingtree and branch subdirectories and any subset of the three can be
904
present within a BzrDir.
907
def can_convert_format(self):
908
"""See BzrDir.can_convert_format()."""
911
def create_branch(self):
912
"""See BzrDir.create_branch."""
913
from bzrlib.branch import BranchFormat
914
return BranchFormat.get_default_format().initialize(self)
916
def create_repository(self, shared=False):
917
"""See BzrDir.create_repository."""
918
return self._format.repository_format.initialize(self, shared)
920
def create_workingtree(self, revision_id=None):
921
"""See BzrDir.create_workingtree."""
922
from bzrlib.workingtree import WorkingTreeFormat
923
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
925
def destroy_workingtree(self):
926
"""See BzrDir.destroy_workingtree."""
927
wt = self.open_workingtree()
928
repository = wt.branch.repository
929
empty = repository.revision_tree(bzrlib.revision.NULL_REVISION)
930
wt.revert([], old_tree=empty)
931
self.destroy_workingtree_metadata()
933
def destroy_workingtree_metadata(self):
934
self.transport.delete_tree('checkout')
936
def _get_mkdir_mode(self):
937
"""Figure out the mode to use when creating a bzrdir subdir."""
938
temp_control = lockable_files.LockableFiles(self.transport, '',
939
lockable_files.TransportLock)
940
return temp_control._dir_mode
942
def get_branch_reference(self):
943
"""See BzrDir.get_branch_reference()."""
944
from bzrlib.branch import BranchFormat
945
format = BranchFormat.find_format(self)
946
return format.get_reference(self)
948
def get_branch_transport(self, branch_format):
949
"""See BzrDir.get_branch_transport()."""
950
if branch_format is None:
951
return self.transport.clone('branch')
953
branch_format.get_format_string()
954
except NotImplementedError:
955
raise errors.IncompatibleFormat(branch_format, self._format)
957
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
958
except errors.FileExists:
960
return self.transport.clone('branch')
962
def get_repository_transport(self, repository_format):
963
"""See BzrDir.get_repository_transport()."""
964
if repository_format is None:
965
return self.transport.clone('repository')
967
repository_format.get_format_string()
968
except NotImplementedError:
969
raise errors.IncompatibleFormat(repository_format, self._format)
971
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
972
except errors.FileExists:
974
return self.transport.clone('repository')
976
def get_workingtree_transport(self, workingtree_format):
977
"""See BzrDir.get_workingtree_transport()."""
978
if workingtree_format is None:
979
return self.transport.clone('checkout')
981
workingtree_format.get_format_string()
982
except NotImplementedError:
983
raise errors.IncompatibleFormat(workingtree_format, self._format)
985
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
986
except errors.FileExists:
988
return self.transport.clone('checkout')
990
def needs_format_conversion(self, format=None):
991
"""See BzrDir.needs_format_conversion()."""
993
format = BzrDirFormat.get_default_format()
994
if not isinstance(self._format, format.__class__):
995
# it is not a meta dir format, conversion is needed.
997
# we might want to push this down to the repository?
999
if not isinstance(self.open_repository()._format,
1000
format.repository_format.__class__):
1001
# the repository needs an upgrade.
1003
except errors.NoRepositoryPresent:
1005
# currently there are no other possible conversions for meta1 formats.
1008
def open_branch(self, unsupported=False):
1009
"""See BzrDir.open_branch."""
1010
from bzrlib.branch import BranchFormat
1011
format = BranchFormat.find_format(self)
1012
self._check_supported(format, unsupported)
1013
return format.open(self, _found=True)
1015
def open_repository(self, unsupported=False):
1016
"""See BzrDir.open_repository."""
1017
from bzrlib.repository import RepositoryFormat
1018
format = RepositoryFormat.find_format(self)
1019
self._check_supported(format, unsupported)
1020
return format.open(self, _found=True)
1022
def open_workingtree(self, unsupported=False):
1023
"""See BzrDir.open_workingtree."""
1024
from bzrlib.workingtree import WorkingTreeFormat
1025
format = WorkingTreeFormat.find_format(self)
1026
self._check_supported(format, unsupported)
1027
return format.open(self, _found=True)
1030
class BzrDirFormat(object):
1031
"""An encapsulation of the initialization and open routines for a format.
1033
Formats provide three things:
1034
* An initialization routine,
1038
Formats are placed in an dict by their format string for reference
1039
during bzrdir opening. These should be subclasses of BzrDirFormat
1042
Once a format is deprecated, just deprecate the initialize and open
1043
methods on the format class. Do not deprecate the object, as the
1044
object will be created every system load.
1047
_default_format = None
1048
"""The default format used for new .bzr dirs."""
1051
"""The known formats."""
1053
_control_formats = []
1054
"""The registered control formats - .bzr, ....
1056
This is a list of BzrDirFormat objects.
1059
_lock_file_name = 'branch-lock'
1061
# _lock_class must be set in subclasses to the lock type, typ.
1062
# TransportLock or LockDir
1065
def find_format(klass, transport):
1066
"""Return the format present at transport."""
1067
for format in klass._control_formats:
1069
return format.probe_transport(transport)
1070
except errors.NotBranchError:
1071
# this format does not find a control dir here.
1073
raise errors.NotBranchError(path=transport.base)
1076
def probe_transport(klass, transport):
1077
"""Return the .bzrdir style format present in a directory."""
1079
format_string = transport.get(".bzr/branch-format").read()
1080
except errors.NoSuchFile:
1081
raise errors.NotBranchError(path=transport.base)
1084
return klass._formats[format_string]
1086
raise errors.UnknownFormatError(format=format_string)
1089
def get_default_format(klass):
1090
"""Return the current default format."""
1091
return klass._default_format
1093
def get_format_string(self):
1094
"""Return the ASCII format string that identifies this format."""
1095
raise NotImplementedError(self.get_format_string)
1097
def get_format_description(self):
1098
"""Return the short description for this format."""
1099
raise NotImplementedError(self.get_format_description)
1101
def get_converter(self, format=None):
1102
"""Return the converter to use to convert bzrdirs needing converts.
1104
This returns a bzrlib.bzrdir.Converter object.
1106
This should return the best upgrader to step this format towards the
1107
current default format. In the case of plugins we can/should provide
1108
some means for them to extend the range of returnable converters.
1110
:param format: Optional format to override the default format of the
1113
raise NotImplementedError(self.get_converter)
1115
def initialize(self, url):
1116
"""Create a bzr control dir at this url and return an opened copy.
1118
Subclasses should typically override initialize_on_transport
1119
instead of this method.
1121
return self.initialize_on_transport(get_transport(url))
1123
def initialize_on_transport(self, transport):
1124
"""Initialize a new bzrdir in the base directory of a Transport."""
1125
# Since we don't have a .bzr directory, inherit the
1126
# mode from the root directory
1127
temp_control = lockable_files.LockableFiles(transport,
1128
'', lockable_files.TransportLock)
1129
temp_control._transport.mkdir('.bzr',
1130
# FIXME: RBC 20060121 don't peek under
1132
mode=temp_control._dir_mode)
1133
file_mode = temp_control._file_mode
1135
mutter('created control directory in ' + transport.base)
1136
control = transport.clone('.bzr')
1137
utf8_files = [('README',
1138
"This is a Bazaar-NG control directory.\n"
1139
"Do not change any files in this directory.\n"),
1140
('branch-format', self.get_format_string()),
1142
# NB: no need to escape relative paths that are url safe.
1143
control_files = lockable_files.LockableFiles(control,
1144
self._lock_file_name, self._lock_class)
1145
control_files.create_lock()
1146
control_files.lock_write()
1148
for file, content in utf8_files:
1149
control_files.put_utf8(file, content)
1151
control_files.unlock()
1152
return self.open(transport, _found=True)
1154
def is_supported(self):
1155
"""Is this format supported?
1157
Supported formats must be initializable and openable.
1158
Unsupported formats may not support initialization or committing or
1159
some other features depending on the reason for not being supported.
1163
def same_model(self, target_format):
1164
return (self.repository_format.rich_root_data ==
1165
target_format.rich_root_data)
1168
def known_formats(klass):
1169
"""Return all the known formats.
1171
Concrete formats should override _known_formats.
1173
# There is double indirection here to make sure that control
1174
# formats used by more than one dir format will only be probed
1175
# once. This can otherwise be quite expensive for remote connections.
1177
for format in klass._control_formats:
1178
result.update(format._known_formats())
1182
def _known_formats(klass):
1183
"""Return the known format instances for this control format."""
1184
return set(klass._formats.values())
1186
def open(self, transport, _found=False):
1187
"""Return an instance of this format for the dir transport points at.
1189
_found is a private parameter, do not use it.
1192
assert isinstance(BzrDirFormat.find_format(transport),
1194
return self._open(transport)
1196
def _open(self, transport):
1197
"""Template method helper for opening BzrDirectories.
1199
This performs the actual open and any additional logic or parameter
1202
raise NotImplementedError(self._open)
1205
def register_format(klass, format):
1206
klass._formats[format.get_format_string()] = format
1209
def register_control_format(klass, format):
1210
"""Register a format that does not use '.bzrdir' for its control dir.
1212
TODO: This should be pulled up into a 'ControlDirFormat' base class
1213
which BzrDirFormat can inherit from, and renamed to register_format
1214
there. It has been done without that for now for simplicity of
1217
klass._control_formats.append(format)
1220
def set_default_format(klass, format):
1221
klass._default_format = format
1224
return self.get_format_string()[:-1]
1227
def unregister_format(klass, format):
1228
assert klass._formats[format.get_format_string()] is format
1229
del klass._formats[format.get_format_string()]
1232
def unregister_control_format(klass, format):
1233
klass._control_formats.remove(format)
1236
# register BzrDirFormat as a control format
1237
BzrDirFormat.register_control_format(BzrDirFormat)
1240
class BzrDirFormat4(BzrDirFormat):
1241
"""Bzr dir format 4.
1243
This format is a combined format for working tree, branch and repository.
1245
- Format 1 working trees [always]
1246
- Format 4 branches [always]
1247
- Format 4 repositories [always]
1249
This format is deprecated: it indexes texts using a text it which is
1250
removed in format 5; write support for this format has been removed.
1253
_lock_class = lockable_files.TransportLock
1255
def get_format_string(self):
1256
"""See BzrDirFormat.get_format_string()."""
1257
return "Bazaar-NG branch, format 0.0.4\n"
1259
def get_format_description(self):
1260
"""See BzrDirFormat.get_format_description()."""
1261
return "All-in-one format 4"
1263
def get_converter(self, format=None):
1264
"""See BzrDirFormat.get_converter()."""
1265
# there is one and only one upgrade path here.
1266
return ConvertBzrDir4To5()
1268
def initialize_on_transport(self, transport):
1269
"""Format 4 branches cannot be created."""
1270
raise errors.UninitializableFormat(self)
1272
def is_supported(self):
1273
"""Format 4 is not supported.
1275
It is not supported because the model changed from 4 to 5 and the
1276
conversion logic is expensive - so doing it on the fly was not
1281
def _open(self, transport):
1282
"""See BzrDirFormat._open."""
1283
return BzrDir4(transport, self)
1285
def __return_repository_format(self):
1286
"""Circular import protection."""
1287
from bzrlib.repository import RepositoryFormat4
1288
return RepositoryFormat4()
1289
repository_format = property(__return_repository_format)
1292
class BzrDirFormat5(BzrDirFormat):
1293
"""Bzr control format 5.
1295
This format is a combined format for working tree, branch and repository.
1297
- Format 2 working trees [always]
1298
- Format 4 branches [always]
1299
- Format 5 repositories [always]
1300
Unhashed stores in the repository.
1303
_lock_class = lockable_files.TransportLock
1305
def get_format_string(self):
1306
"""See BzrDirFormat.get_format_string()."""
1307
return "Bazaar-NG branch, format 5\n"
1309
def get_format_description(self):
1310
"""See BzrDirFormat.get_format_description()."""
1311
return "All-in-one format 5"
1313
def get_converter(self, format=None):
1314
"""See BzrDirFormat.get_converter()."""
1315
# there is one and only one upgrade path here.
1316
return ConvertBzrDir5To6()
1318
def _initialize_for_clone(self, url):
1319
return self.initialize_on_transport(get_transport(url), _cloning=True)
1321
def initialize_on_transport(self, transport, _cloning=False):
1322
"""Format 5 dirs always have working tree, branch and repository.
1324
Except when they are being cloned.
1326
from bzrlib.branch import BzrBranchFormat4
1327
from bzrlib.repository import RepositoryFormat5
1328
from bzrlib.workingtree import WorkingTreeFormat2
1329
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1330
RepositoryFormat5().initialize(result, _internal=True)
1332
branch = BzrBranchFormat4().initialize(result)
1334
WorkingTreeFormat2().initialize(result)
1335
except errors.NotLocalUrl:
1336
# Even though we can't access the working tree, we need to
1337
# create its control files.
1338
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1341
def _open(self, transport):
1342
"""See BzrDirFormat._open."""
1343
return BzrDir5(transport, self)
1345
def __return_repository_format(self):
1346
"""Circular import protection."""
1347
from bzrlib.repository import RepositoryFormat5
1348
return RepositoryFormat5()
1349
repository_format = property(__return_repository_format)
1352
class BzrDirFormat6(BzrDirFormat):
1353
"""Bzr control format 6.
1355
This format is a combined format for working tree, branch and repository.
1357
- Format 2 working trees [always]
1358
- Format 4 branches [always]
1359
- Format 6 repositories [always]
1362
_lock_class = lockable_files.TransportLock
1364
def get_format_string(self):
1365
"""See BzrDirFormat.get_format_string()."""
1366
return "Bazaar-NG branch, format 6\n"
1368
def get_format_description(self):
1369
"""See BzrDirFormat.get_format_description()."""
1370
return "All-in-one format 6"
1372
def get_converter(self, format=None):
1373
"""See BzrDirFormat.get_converter()."""
1374
# there is one and only one upgrade path here.
1375
return ConvertBzrDir6ToMeta()
1377
def _initialize_for_clone(self, url):
1378
return self.initialize_on_transport(get_transport(url), _cloning=True)
1380
def initialize_on_transport(self, transport, _cloning=False):
1381
"""Format 6 dirs always have working tree, branch and repository.
1383
Except when they are being cloned.
1385
from bzrlib.branch import BzrBranchFormat4
1386
from bzrlib.repository import RepositoryFormat6
1387
from bzrlib.workingtree import WorkingTreeFormat2
1388
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1389
RepositoryFormat6().initialize(result, _internal=True)
1391
branch = BzrBranchFormat4().initialize(result)
1393
WorkingTreeFormat2().initialize(result)
1394
except errors.NotLocalUrl:
1395
# Even though we can't access the working tree, we need to
1396
# create its control files.
1397
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1400
def _open(self, transport):
1401
"""See BzrDirFormat._open."""
1402
return BzrDir6(transport, self)
1404
def __return_repository_format(self):
1405
"""Circular import protection."""
1406
from bzrlib.repository import RepositoryFormat6
1407
return RepositoryFormat6()
1408
repository_format = property(__return_repository_format)
1411
class BzrDirMetaFormat1(BzrDirFormat):
1412
"""Bzr meta control format 1
1414
This is the first format with split out working tree, branch and repository
1417
- Format 3 working trees [optional]
1418
- Format 5 branches [optional]
1419
- Format 7 repositories [optional]
1422
_lock_class = lockdir.LockDir
1424
def get_converter(self, format=None):
1425
"""See BzrDirFormat.get_converter()."""
1427
format = BzrDirFormat.get_default_format()
1428
if not isinstance(self, format.__class__):
1429
# converting away from metadir is not implemented
1430
raise NotImplementedError(self.get_converter)
1431
return ConvertMetaToMeta(format)
1433
def get_format_string(self):
1434
"""See BzrDirFormat.get_format_string()."""
1435
return "Bazaar-NG meta directory, format 1\n"
1437
def get_format_description(self):
1438
"""See BzrDirFormat.get_format_description()."""
1439
return "Meta directory format 1"
1441
def _open(self, transport):
1442
"""See BzrDirFormat._open."""
1443
return BzrDirMeta1(transport, self)
1445
def __return_repository_format(self):
1446
"""Circular import protection."""
1447
if getattr(self, '_repository_format', None):
1448
return self._repository_format
1449
from bzrlib.repository import RepositoryFormat
1450
return RepositoryFormat.get_default_format()
1452
def __set_repository_format(self, value):
1453
"""Allow changint the repository format for metadir formats."""
1454
self._repository_format = value
1456
repository_format = property(__return_repository_format, __set_repository_format)
1459
BzrDirFormat.register_format(BzrDirFormat4())
1460
BzrDirFormat.register_format(BzrDirFormat5())
1461
BzrDirFormat.register_format(BzrDirFormat6())
1462
__default_format = BzrDirMetaFormat1()
1463
BzrDirFormat.register_format(__default_format)
1464
BzrDirFormat.set_default_format(__default_format)
1467
class BzrDirTestProviderAdapter(object):
1468
"""A tool to generate a suite testing multiple bzrdir formats at once.
1470
This is done by copying the test once for each transport and injecting
1471
the transport_server, transport_readonly_server, and bzrdir_format
1472
classes into each copy. Each copy is also given a new id() to make it
1476
def __init__(self, transport_server, transport_readonly_server, formats):
1477
self._transport_server = transport_server
1478
self._transport_readonly_server = transport_readonly_server
1479
self._formats = formats
1481
def adapt(self, test):
1482
result = unittest.TestSuite()
1483
for format in self._formats:
1484
new_test = deepcopy(test)
1485
new_test.transport_server = self._transport_server
1486
new_test.transport_readonly_server = self._transport_readonly_server
1487
new_test.bzrdir_format = format
1488
def make_new_test_id():
1489
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1490
return lambda: new_id
1491
new_test.id = make_new_test_id()
1492
result.addTest(new_test)
1496
class Converter(object):
1497
"""Converts a disk format object from one format to another."""
1499
def convert(self, to_convert, pb):
1500
"""Perform the conversion of to_convert, giving feedback via pb.
1502
:param to_convert: The disk object to convert.
1503
:param pb: a progress bar to use for progress information.
1506
def step(self, message):
1507
"""Update the pb by a step."""
1509
self.pb.update(message, self.count, self.total)
1512
class ConvertBzrDir4To5(Converter):
1513
"""Converts format 4 bzr dirs to format 5."""
1516
super(ConvertBzrDir4To5, self).__init__()
1517
self.converted_revs = set()
1518
self.absent_revisions = set()
1522
def convert(self, to_convert, pb):
1523
"""See Converter.convert()."""
1524
self.bzrdir = to_convert
1526
self.pb.note('starting upgrade from format 4 to 5')
1527
if isinstance(self.bzrdir.transport, LocalTransport):
1528
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1529
self._convert_to_weaves()
1530
return BzrDir.open(self.bzrdir.root_transport.base)
1532
def _convert_to_weaves(self):
1533
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1536
stat = self.bzrdir.transport.stat('weaves')
1537
if not S_ISDIR(stat.st_mode):
1538
self.bzrdir.transport.delete('weaves')
1539
self.bzrdir.transport.mkdir('weaves')
1540
except errors.NoSuchFile:
1541
self.bzrdir.transport.mkdir('weaves')
1542
# deliberately not a WeaveFile as we want to build it up slowly.
1543
self.inv_weave = Weave('inventory')
1544
# holds in-memory weaves for all files
1545
self.text_weaves = {}
1546
self.bzrdir.transport.delete('branch-format')
1547
self.branch = self.bzrdir.open_branch()
1548
self._convert_working_inv()
1549
rev_history = self.branch.revision_history()
1550
# to_read is a stack holding the revisions we still need to process;
1551
# appending to it adds new highest-priority revisions
1552
self.known_revisions = set(rev_history)
1553
self.to_read = rev_history[-1:]
1555
rev_id = self.to_read.pop()
1556
if (rev_id not in self.revisions
1557
and rev_id not in self.absent_revisions):
1558
self._load_one_rev(rev_id)
1560
to_import = self._make_order()
1561
for i, rev_id in enumerate(to_import):
1562
self.pb.update('converting revision', i, len(to_import))
1563
self._convert_one_rev(rev_id)
1565
self._write_all_weaves()
1566
self._write_all_revs()
1567
self.pb.note('upgraded to weaves:')
1568
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1569
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1570
self.pb.note(' %6d texts', self.text_count)
1571
self._cleanup_spare_files_after_format4()
1572
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1574
def _cleanup_spare_files_after_format4(self):
1575
# FIXME working tree upgrade foo.
1576
for n in 'merged-patches', 'pending-merged-patches':
1578
## assert os.path.getsize(p) == 0
1579
self.bzrdir.transport.delete(n)
1580
except errors.NoSuchFile:
1582
self.bzrdir.transport.delete_tree('inventory-store')
1583
self.bzrdir.transport.delete_tree('text-store')
1585
def _convert_working_inv(self):
1586
inv = xml4.serializer_v4.read_inventory(
1587
self.branch.control_files.get('inventory'))
1588
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1589
# FIXME inventory is a working tree change.
1590
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1592
def _write_all_weaves(self):
1593
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1594
weave_transport = self.bzrdir.transport.clone('weaves')
1595
weaves = WeaveStore(weave_transport, prefixed=False)
1596
transaction = WriteTransaction()
1600
for file_id, file_weave in self.text_weaves.items():
1601
self.pb.update('writing weave', i, len(self.text_weaves))
1602
weaves._put_weave(file_id, file_weave, transaction)
1604
self.pb.update('inventory', 0, 1)
1605
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1606
self.pb.update('inventory', 1, 1)
1610
def _write_all_revs(self):
1611
"""Write all revisions out in new form."""
1612
self.bzrdir.transport.delete_tree('revision-store')
1613
self.bzrdir.transport.mkdir('revision-store')
1614
revision_transport = self.bzrdir.transport.clone('revision-store')
1616
_revision_store = TextRevisionStore(TextStore(revision_transport,
1620
transaction = WriteTransaction()
1621
for i, rev_id in enumerate(self.converted_revs):
1622
self.pb.update('write revision', i, len(self.converted_revs))
1623
_revision_store.add_revision(self.revisions[rev_id], transaction)
1627
def _load_one_rev(self, rev_id):
1628
"""Load a revision object into memory.
1630
Any parents not either loaded or abandoned get queued to be
1632
self.pb.update('loading revision',
1633
len(self.revisions),
1634
len(self.known_revisions))
1635
if not self.branch.repository.has_revision(rev_id):
1637
self.pb.note('revision {%s} not present in branch; '
1638
'will be converted as a ghost',
1640
self.absent_revisions.add(rev_id)
1642
rev = self.branch.repository._revision_store.get_revision(rev_id,
1643
self.branch.repository.get_transaction())
1644
for parent_id in rev.parent_ids:
1645
self.known_revisions.add(parent_id)
1646
self.to_read.append(parent_id)
1647
self.revisions[rev_id] = rev
1649
def _load_old_inventory(self, rev_id):
1650
assert rev_id not in self.converted_revs
1651
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1652
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1653
inv.revision_id = rev_id
1654
rev = self.revisions[rev_id]
1655
if rev.inventory_sha1:
1656
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1657
'inventory sha mismatch for {%s}' % rev_id
1660
def _load_updated_inventory(self, rev_id):
1661
assert rev_id in self.converted_revs
1662
inv_xml = self.inv_weave.get_text(rev_id)
1663
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1666
def _convert_one_rev(self, rev_id):
1667
"""Convert revision and all referenced objects to new format."""
1668
rev = self.revisions[rev_id]
1669
inv = self._load_old_inventory(rev_id)
1670
present_parents = [p for p in rev.parent_ids
1671
if p not in self.absent_revisions]
1672
self._convert_revision_contents(rev, inv, present_parents)
1673
self._store_new_weave(rev, inv, present_parents)
1674
self.converted_revs.add(rev_id)
1676
def _store_new_weave(self, rev, inv, present_parents):
1677
# the XML is now updated with text versions
1679
entries = inv.iter_entries()
1681
for path, ie in entries:
1682
assert getattr(ie, 'revision', None) is not None, \
1683
'no revision on {%s} in {%s}' % \
1684
(file_id, rev.revision_id)
1685
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1686
new_inv_sha1 = sha_string(new_inv_xml)
1687
self.inv_weave.add_lines(rev.revision_id,
1689
new_inv_xml.splitlines(True))
1690
rev.inventory_sha1 = new_inv_sha1
1692
def _convert_revision_contents(self, rev, inv, present_parents):
1693
"""Convert all the files within a revision.
1695
Also upgrade the inventory to refer to the text revision ids."""
1696
rev_id = rev.revision_id
1697
mutter('converting texts of revision {%s}',
1699
parent_invs = map(self._load_updated_inventory, present_parents)
1700
entries = inv.iter_entries()
1702
for path, ie in entries:
1703
self._convert_file_version(rev, ie, parent_invs)
1705
def _convert_file_version(self, rev, ie, parent_invs):
1706
"""Convert one version of one file.
1708
The file needs to be added into the weave if it is a merge
1709
of >=2 parents or if it's changed from its parent.
1711
file_id = ie.file_id
1712
rev_id = rev.revision_id
1713
w = self.text_weaves.get(file_id)
1716
self.text_weaves[file_id] = w
1717
text_changed = False
1718
previous_entries = ie.find_previous_heads(parent_invs,
1722
for old_revision in previous_entries:
1723
# if this fails, its a ghost ?
1724
assert old_revision in self.converted_revs, \
1725
"Revision {%s} not in converted_revs" % old_revision
1726
self.snapshot_ie(previous_entries, ie, w, rev_id)
1728
assert getattr(ie, 'revision', None) is not None
1730
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1731
# TODO: convert this logic, which is ~= snapshot to
1732
# a call to:. This needs the path figured out. rather than a work_tree
1733
# a v4 revision_tree can be given, or something that looks enough like
1734
# one to give the file content to the entry if it needs it.
1735
# and we need something that looks like a weave store for snapshot to
1737
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1738
if len(previous_revisions) == 1:
1739
previous_ie = previous_revisions.values()[0]
1740
if ie._unchanged(previous_ie):
1741
ie.revision = previous_ie.revision
1744
text = self.branch.repository.text_store.get(ie.text_id)
1745
file_lines = text.readlines()
1746
assert sha_strings(file_lines) == ie.text_sha1
1747
assert sum(map(len, file_lines)) == ie.text_size
1748
w.add_lines(rev_id, previous_revisions, file_lines)
1749
self.text_count += 1
1751
w.add_lines(rev_id, previous_revisions, [])
1752
ie.revision = rev_id
1754
def _make_order(self):
1755
"""Return a suitable order for importing revisions.
1757
The order must be such that an revision is imported after all
1758
its (present) parents.
1760
todo = set(self.revisions.keys())
1761
done = self.absent_revisions.copy()
1764
# scan through looking for a revision whose parents
1766
for rev_id in sorted(list(todo)):
1767
rev = self.revisions[rev_id]
1768
parent_ids = set(rev.parent_ids)
1769
if parent_ids.issubset(done):
1770
# can take this one now
1771
order.append(rev_id)
1777
class ConvertBzrDir5To6(Converter):
1778
"""Converts format 5 bzr dirs to format 6."""
1780
def convert(self, to_convert, pb):
1781
"""See Converter.convert()."""
1782
self.bzrdir = to_convert
1784
self.pb.note('starting upgrade from format 5 to 6')
1785
self._convert_to_prefixed()
1786
return BzrDir.open(self.bzrdir.root_transport.base)
1788
def _convert_to_prefixed(self):
1789
from bzrlib.store import TransportStore
1790
self.bzrdir.transport.delete('branch-format')
1791
for store_name in ["weaves", "revision-store"]:
1792
self.pb.note("adding prefixes to %s" % store_name)
1793
store_transport = self.bzrdir.transport.clone(store_name)
1794
store = TransportStore(store_transport, prefixed=True)
1795
for urlfilename in store_transport.list_dir('.'):
1796
filename = urlutils.unescape(urlfilename)
1797
if (filename.endswith(".weave") or
1798
filename.endswith(".gz") or
1799
filename.endswith(".sig")):
1800
file_id = os.path.splitext(filename)[0]
1803
prefix_dir = store.hash_prefix(file_id)
1804
# FIXME keep track of the dirs made RBC 20060121
1806
store_transport.move(filename, prefix_dir + '/' + filename)
1807
except errors.NoSuchFile: # catches missing dirs strangely enough
1808
store_transport.mkdir(prefix_dir)
1809
store_transport.move(filename, prefix_dir + '/' + filename)
1810
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1813
class ConvertBzrDir6ToMeta(Converter):
1814
"""Converts format 6 bzr dirs to metadirs."""
1816
def convert(self, to_convert, pb):
1817
"""See Converter.convert()."""
1818
self.bzrdir = to_convert
1821
self.total = 20 # the steps we know about
1822
self.garbage_inventories = []
1824
self.pb.note('starting upgrade from format 6 to metadir')
1825
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1826
# its faster to move specific files around than to open and use the apis...
1827
# first off, nuke ancestry.weave, it was never used.
1829
self.step('Removing ancestry.weave')
1830
self.bzrdir.transport.delete('ancestry.weave')
1831
except errors.NoSuchFile:
1833
# find out whats there
1834
self.step('Finding branch files')
1835
last_revision = self.bzrdir.open_branch().last_revision()
1836
bzrcontents = self.bzrdir.transport.list_dir('.')
1837
for name in bzrcontents:
1838
if name.startswith('basis-inventory.'):
1839
self.garbage_inventories.append(name)
1840
# create new directories for repository, working tree and branch
1841
self.dir_mode = self.bzrdir._control_files._dir_mode
1842
self.file_mode = self.bzrdir._control_files._file_mode
1843
repository_names = [('inventory.weave', True),
1844
('revision-store', True),
1846
self.step('Upgrading repository ')
1847
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1848
self.make_lock('repository')
1849
# we hard code the formats here because we are converting into
1850
# the meta format. The meta format upgrader can take this to a
1851
# future format within each component.
1852
self.put_format('repository', bzrlib.repository.RepositoryFormat7())
1853
for entry in repository_names:
1854
self.move_entry('repository', entry)
1856
self.step('Upgrading branch ')
1857
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1858
self.make_lock('branch')
1859
self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
1860
branch_files = [('revision-history', True),
1861
('branch-name', True),
1863
for entry in branch_files:
1864
self.move_entry('branch', entry)
1866
checkout_files = [('pending-merges', True),
1867
('inventory', True),
1868
('stat-cache', False)]
1869
# If a mandatory checkout file is not present, the branch does not have
1870
# a functional checkout. Do not create a checkout in the converted
1872
for name, mandatory in checkout_files:
1873
if mandatory and name not in bzrcontents:
1874
has_checkout = False
1878
if not has_checkout:
1879
self.pb.note('No working tree.')
1880
# If some checkout files are there, we may as well get rid of them.
1881
for name, mandatory in checkout_files:
1882
if name in bzrcontents:
1883
self.bzrdir.transport.delete(name)
1885
self.step('Upgrading working tree')
1886
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1887
self.make_lock('checkout')
1889
'checkout', bzrlib.workingtree.WorkingTreeFormat3())
1890
self.bzrdir.transport.delete_multi(
1891
self.garbage_inventories, self.pb)
1892
for entry in checkout_files:
1893
self.move_entry('checkout', entry)
1894
if last_revision is not None:
1895
self.bzrdir._control_files.put_utf8(
1896
'checkout/last-revision', last_revision)
1897
self.bzrdir._control_files.put_utf8(
1898
'branch-format', BzrDirMetaFormat1().get_format_string())
1899
return BzrDir.open(self.bzrdir.root_transport.base)
1901
def make_lock(self, name):
1902
"""Make a lock for the new control dir name."""
1903
self.step('Make %s lock' % name)
1904
ld = lockdir.LockDir(self.bzrdir.transport,
1906
file_modebits=self.file_mode,
1907
dir_modebits=self.dir_mode)
1910
def move_entry(self, new_dir, entry):
1911
"""Move then entry name into new_dir."""
1913
mandatory = entry[1]
1914
self.step('Moving %s' % name)
1916
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1917
except errors.NoSuchFile:
1921
def put_format(self, dirname, format):
1922
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1925
class ConvertMetaToMeta(Converter):
1926
"""Converts the components of metadirs."""
1928
def __init__(self, target_format):
1929
"""Create a metadir to metadir converter.
1931
:param target_format: The final metadir format that is desired.
1933
self.target_format = target_format
1935
def convert(self, to_convert, pb):
1936
"""See Converter.convert()."""
1937
self.bzrdir = to_convert
1941
self.step('checking repository format')
1943
repo = self.bzrdir.open_repository()
1944
except errors.NoRepositoryPresent:
1947
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1948
from bzrlib.repository import CopyConverter
1949
self.pb.note('starting repository conversion')
1950
converter = CopyConverter(self.target_format.repository_format)
1951
converter.convert(repo, pb)
1955
# This is not in remote.py because it's small, and needs to be registered.
1956
# Putting it in remote.py creates a circular import problem.
1957
# we can make it a lazy object if the control formats is turned into something
1959
class RemoteBzrDirFormat(BzrDirMetaFormat1):
1960
"""Format representing bzrdirs accessed via a smart server"""
1962
def get_format_description(self):
1963
return 'bzr remote bzrdir'
1966
def probe_transport(klass, transport):
1967
"""Return a RemoteBzrDirFormat object if it looks possible."""
1969
transport.get_smart_client()
1970
except (NotImplementedError, AttributeError,
1971
errors.TransportNotPossible):
1972
# no smart server, so not a branch for this format type.
1973
raise errors.NotBranchError(path=transport.base)
1977
def _open(self, transport):
1978
return remote.RemoteBzrDir(transport)
1980
def __eq__(self, other):
1981
if not isinstance(other, RemoteBzrDirFormat):
1983
return self.get_format_description() == other.get_format_description()
1986
# We can't use register_control_format because it adds it at a lower priority
1987
# than the existing branches, whereas this should take priority.
1988
BzrDirFormat._control_formats.insert(0, RemoteBzrDirFormat)