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,
44
repository as _mod_repository,
49
from bzrlib.osutils import (
54
from bzrlib.smart.client import SmartClient
55
from bzrlib.store.revision.text import TextRevisionStore
56
from bzrlib.store.text import TextStore
57
from bzrlib.store.versioned import WeaveStore
58
from bzrlib.transactions import WriteTransaction
59
from bzrlib.transport import get_transport
60
from bzrlib.weave import Weave
63
from bzrlib.trace import mutter
64
from bzrlib.transport.local import LocalTransport
68
"""A .bzr control diretory.
70
BzrDir instances let you create or open any of the things that can be
71
found within .bzr - checkouts, branches and repositories.
74
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
76
a transport connected to the directory this bzr was opened from.
80
"""Invoke break_lock on the first object in the bzrdir.
82
If there is a tree, the tree is opened and break_lock() called.
83
Otherwise, branch is tried, and finally repository.
85
# XXX: This seems more like a UI function than something that really
86
# belongs in this class.
88
thing_to_unlock = self.open_workingtree()
89
except (errors.NotLocalUrl, errors.NoWorkingTree):
91
thing_to_unlock = self.open_branch()
92
except errors.NotBranchError:
94
thing_to_unlock = self.open_repository()
95
except errors.NoRepositoryPresent:
97
thing_to_unlock.break_lock()
99
def can_convert_format(self):
100
"""Return true if this bzrdir is one whose format we can convert from."""
103
def check_conversion_target(self, target_format):
104
target_repo_format = target_format.repository_format
105
source_repo_format = self._format.repository_format
106
source_repo_format.check_conversion_target(target_repo_format)
109
def _check_supported(format, allow_unsupported):
110
"""Check whether format is a supported format.
112
If allow_unsupported is True, this is a no-op.
114
if not allow_unsupported and not format.is_supported():
115
# see open_downlevel to open legacy branches.
116
raise errors.UnsupportedFormatError(format=format)
118
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
119
"""Clone this bzrdir and its contents to url verbatim.
121
If urls last component does not exist, it will be created.
123
if revision_id is not None, then the clone operation may tune
124
itself to download less data.
125
:param force_new_repo: Do not use a shared repository for the target
126
even if one is available.
129
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
130
result = self._format.initialize(url)
132
local_repo = self.find_repository()
133
except errors.NoRepositoryPresent:
136
# may need to copy content in
138
result_repo = local_repo.clone(
140
revision_id=revision_id,
142
result_repo.set_make_working_trees(local_repo.make_working_trees())
145
result_repo = result.find_repository()
146
# fetch content this dir needs.
148
# XXX FIXME RBC 20060214 need tests for this when the basis
150
result_repo.fetch(basis_repo, revision_id=revision_id)
151
result_repo.fetch(local_repo, revision_id=revision_id)
152
except errors.NoRepositoryPresent:
153
# needed to make one anyway.
154
result_repo = local_repo.clone(
156
revision_id=revision_id,
158
result_repo.set_make_working_trees(local_repo.make_working_trees())
159
# 1 if there is a branch present
160
# make sure its content is available in the target repository
163
self.open_branch().clone(result, revision_id=revision_id)
164
except errors.NotBranchError:
167
self.open_workingtree().clone(result, basis=basis_tree)
168
except (errors.NoWorkingTree, errors.NotLocalUrl):
172
def _get_basis_components(self, basis):
173
"""Retrieve the basis components that are available at basis."""
175
return None, None, None
177
basis_tree = basis.open_workingtree()
178
basis_branch = basis_tree.branch
179
basis_repo = basis_branch.repository
180
except (errors.NoWorkingTree, errors.NotLocalUrl):
183
basis_branch = basis.open_branch()
184
basis_repo = basis_branch.repository
185
except errors.NotBranchError:
188
basis_repo = basis.open_repository()
189
except errors.NoRepositoryPresent:
191
return basis_repo, basis_branch, basis_tree
193
# TODO: This should be given a Transport, and should chdir up; otherwise
194
# this will open a new connection.
195
def _make_tail(self, url):
196
head, tail = urlutils.split(url)
197
if tail and tail != '.':
198
t = get_transport(head)
201
except errors.FileExists:
204
# TODO: Should take a Transport
206
def create(cls, base):
207
"""Create a new BzrDir at the url 'base'.
209
This will call the current default formats initialize with base
210
as the only parameter.
212
If you need a specific format, consider creating an instance
213
of that and calling initialize().
215
if cls is not BzrDir:
216
raise AssertionError("BzrDir.create always creates the default format, "
217
"not one of %r" % cls)
218
head, tail = urlutils.split(base)
219
if tail and tail != '.':
220
t = get_transport(head)
223
except errors.FileExists:
225
return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
227
def create_branch(self):
228
"""Create a branch in this BzrDir.
230
The bzrdirs format will control what branch format is created.
231
For more control see BranchFormatXX.create(a_bzrdir).
233
raise NotImplementedError(self.create_branch)
236
def create_branch_and_repo(base, force_new_repo=False):
237
"""Create a new BzrDir, Branch and Repository at the url 'base'.
239
This will use the current default BzrDirFormat, and use whatever
240
repository format that that uses via bzrdir.create_branch and
241
create_repository. If a shared repository is available that is used
244
The created Branch object is returned.
246
:param base: The URL to create the branch at.
247
:param force_new_repo: If True a new repository is always created.
249
bzrdir = BzrDir.create(base)
250
bzrdir._find_or_create_repository(force_new_repo)
251
return bzrdir.create_branch()
253
def _find_or_create_repository(self, force_new_repo):
254
"""Create a new repository if needed, returning the repository."""
256
return self.create_repository()
258
return self.find_repository()
259
except errors.NoRepositoryPresent:
260
return self.create_repository()
263
def create_branch_convenience(base, force_new_repo=False,
264
force_new_tree=None, format=None):
265
"""Create a new BzrDir, Branch and Repository at the url 'base'.
267
This is a convenience function - it will use an existing repository
268
if possible, can be told explicitly whether to create a working tree or
271
This will use the current default BzrDirFormat, and use whatever
272
repository format that that uses via bzrdir.create_branch and
273
create_repository. If a shared repository is available that is used
274
preferentially. Whatever repository is used, its tree creation policy
277
The created Branch object is returned.
278
If a working tree cannot be made due to base not being a file:// url,
279
no error is raised unless force_new_tree is True, in which case no
280
data is created on disk and NotLocalUrl is raised.
282
:param base: The URL to create the branch at.
283
:param force_new_repo: If True a new repository is always created.
284
:param force_new_tree: If True or False force creation of a tree or
285
prevent such creation respectively.
286
:param format: Override for the for the bzrdir format to create
289
# check for non local urls
290
t = get_transport(safe_unicode(base))
291
if not isinstance(t, LocalTransport):
292
raise errors.NotLocalUrl(base)
294
bzrdir = BzrDir.create(base)
296
bzrdir = format.initialize(base)
297
repo = bzrdir._find_or_create_repository(force_new_repo)
298
result = bzrdir.create_branch()
299
if force_new_tree or (repo.make_working_trees() and
300
force_new_tree is None):
302
bzrdir.create_workingtree()
303
except errors.NotLocalUrl:
308
def create_repository(base, shared=False):
309
"""Create a new BzrDir and Repository at the url 'base'.
311
This will use the current default BzrDirFormat, and use whatever
312
repository format that that uses for bzrdirformat.create_repository.
314
:param shared: Create a shared repository rather than a standalone
316
The Repository object is returned.
318
This must be overridden as an instance method in child classes, where
319
it should take no parameters and construct whatever repository format
320
that child class desires.
322
bzrdir = BzrDir.create(base)
323
return bzrdir.create_repository(shared)
326
def create_standalone_workingtree(base):
327
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
329
'base' must be a local path or a file:// url.
331
This will use the current default BzrDirFormat, and use whatever
332
repository format that that uses for bzrdirformat.create_workingtree,
333
create_branch and create_repository.
335
:return: The WorkingTree object.
337
t = get_transport(safe_unicode(base))
338
if not isinstance(t, LocalTransport):
339
raise errors.NotLocalUrl(base)
340
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
341
force_new_repo=True).bzrdir
342
return bzrdir.create_workingtree()
344
def create_workingtree(self, revision_id=None):
345
"""Create a working tree at this BzrDir.
347
revision_id: create it as of this revision id.
349
raise NotImplementedError(self.create_workingtree)
351
def destroy_workingtree(self):
352
"""Destroy the working tree at this BzrDir.
354
Formats that do not support this may raise UnsupportedOperation.
356
raise NotImplementedError(self.destroy_workingtree)
358
def destroy_workingtree_metadata(self):
359
"""Destroy the control files for the working tree at this BzrDir.
361
The contents of working tree files are not affected.
362
Formats that do not support this may raise UnsupportedOperation.
364
raise NotImplementedError(self.destroy_workingtree_metadata)
366
def find_repository(self):
367
"""Find the repository that should be used for a_bzrdir.
369
This does not require a branch as we use it to find the repo for
370
new branches as well as to hook existing branches up to their
374
return self.open_repository()
375
except errors.NoRepositoryPresent:
377
next_transport = self.root_transport.clone('..')
379
# find the next containing bzrdir
381
found_bzrdir = BzrDir.open_containing_from_transport(
383
except errors.NotBranchError:
385
raise errors.NoRepositoryPresent(self)
386
# does it have a repository ?
388
repository = found_bzrdir.open_repository()
389
except errors.NoRepositoryPresent:
390
next_transport = found_bzrdir.root_transport.clone('..')
391
if (found_bzrdir.root_transport.base == next_transport.base):
392
# top of the file system
396
if ((found_bzrdir.root_transport.base ==
397
self.root_transport.base) or repository.is_shared()):
400
raise errors.NoRepositoryPresent(self)
401
raise errors.NoRepositoryPresent(self)
403
def get_branch_reference(self):
404
"""Return the referenced URL for the branch in this bzrdir.
406
:raises NotBranchError: If there is no Branch.
407
:return: The URL the branch in this bzrdir references if it is a
408
reference branch, or None for regular branches.
412
def get_branch_transport(self, branch_format):
413
"""Get the transport for use by branch format in this BzrDir.
415
Note that bzr dirs that do not support format strings will raise
416
IncompatibleFormat if the branch format they are given has
417
a format string, and vice versa.
419
If branch_format is None, the transport is returned with no
420
checking. if it is not None, then the returned transport is
421
guaranteed to point to an existing directory ready for use.
423
raise NotImplementedError(self.get_branch_transport)
425
def get_repository_transport(self, repository_format):
426
"""Get the transport for use by repository format in this BzrDir.
428
Note that bzr dirs that do not support format strings will raise
429
IncompatibleFormat if the repository format they are given has
430
a format string, and vice versa.
432
If repository_format is None, the transport is returned with no
433
checking. if it is not None, then the returned transport is
434
guaranteed to point to an existing directory ready for use.
436
raise NotImplementedError(self.get_repository_transport)
438
def get_workingtree_transport(self, tree_format):
439
"""Get the transport for use by workingtree format in this BzrDir.
441
Note that bzr dirs that do not support format strings will raise
442
IncompatibleFormat if the workingtree format they are given has
443
a format string, and vice versa.
445
If workingtree_format is None, the transport is returned with no
446
checking. if it is not None, then the returned transport is
447
guaranteed to point to an existing directory ready for use.
449
raise NotImplementedError(self.get_workingtree_transport)
451
def __init__(self, _transport, _format):
452
"""Initialize a Bzr control dir object.
454
Only really common logic should reside here, concrete classes should be
455
made with varying behaviours.
457
:param _format: the format that is creating this BzrDir instance.
458
:param _transport: the transport this dir is based at.
460
self._format = _format
461
self.transport = _transport.clone('.bzr')
462
self.root_transport = _transport
464
def is_control_filename(self, filename):
465
"""True if filename is the name of a path which is reserved for bzrdir's.
467
:param filename: A filename within the root transport of this bzrdir.
469
This is true IF and ONLY IF the filename is part of the namespace reserved
470
for bzr control dirs. Currently this is the '.bzr' directory in the root
471
of the root_transport. it is expected that plugins will need to extend
472
this in the future - for instance to make bzr talk with svn working
475
# this might be better on the BzrDirFormat class because it refers to
476
# all the possible bzrdir disk formats.
477
# This method is tested via the workingtree is_control_filename tests-
478
# it was extracted from WorkingTree.is_control_filename. If the methods
479
# contract is extended beyond the current trivial implementation please
480
# add new tests for it to the appropriate place.
481
return filename == '.bzr' or filename.startswith('.bzr/')
483
def needs_format_conversion(self, format=None):
484
"""Return true if this bzrdir needs convert_format run on it.
486
For instance, if the repository format is out of date but the
487
branch and working tree are not, this should return True.
489
:param format: Optional parameter indicating a specific desired
490
format we plan to arrive at.
492
raise NotImplementedError(self.needs_format_conversion)
495
def open_unsupported(base):
496
"""Open a branch which is not supported."""
497
return BzrDir.open(base, _unsupported=True)
500
def open(base, _unsupported=False):
501
"""Open an existing bzrdir, rooted at 'base' (url)
503
_unsupported is a private parameter to the BzrDir class.
505
t = get_transport(base)
506
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
509
def open_from_transport(transport, _unsupported=False):
510
"""Open a bzrdir within a particular directory.
512
:param transport: Transport containing the bzrdir.
513
:param _unsupported: private.
515
format = BzrDirFormat.find_format(transport)
516
BzrDir._check_supported(format, _unsupported)
517
return format.open(transport, _found=True)
519
def open_branch(self, unsupported=False):
520
"""Open the branch object at this BzrDir if one is present.
522
If unsupported is True, then no longer supported branch formats can
525
TODO: static convenience version of this?
527
raise NotImplementedError(self.open_branch)
530
def open_containing(url):
531
"""Open an existing branch which contains url.
533
:param url: url to search from.
534
See open_containing_from_transport for more detail.
536
return BzrDir.open_containing_from_transport(get_transport(url))
539
def open_containing_from_transport(a_transport):
540
"""Open an existing branch which contains a_transport.base
542
This probes for a branch at a_transport, and searches upwards from there.
544
Basically we keep looking up until we find the control directory or
545
run into the root. If there isn't one, raises NotBranchError.
546
If there is one and it is either an unrecognised format or an unsupported
547
format, UnknownFormatError or UnsupportedFormatError are raised.
548
If there is one, it is returned, along with the unused portion of url.
550
:return: The BzrDir that contains the path, and a Unicode path
551
for the rest of the URL.
553
# this gets the normalised url back. I.e. '.' -> the full path.
554
url = a_transport.base
557
result = BzrDir.open_from_transport(a_transport)
558
return result, urlutils.unescape(a_transport.relpath(url))
559
except errors.NotBranchError, e:
561
new_t = a_transport.clone('..')
562
if new_t.base == a_transport.base:
563
# reached the root, whatever that may be
564
raise errors.NotBranchError(path=url)
567
def open_repository(self, _unsupported=False):
568
"""Open the repository object at this BzrDir if one is present.
570
This will not follow the Branch object pointer - its strictly a direct
571
open facility. Most client code should use open_branch().repository to
574
_unsupported is a private parameter, not part of the api.
575
TODO: static convenience version of this?
577
raise NotImplementedError(self.open_repository)
579
def open_workingtree(self, _unsupported=False):
580
"""Open the workingtree object at this BzrDir if one is present.
582
TODO: static convenience version of this?
584
raise NotImplementedError(self.open_workingtree)
586
def has_branch(self):
587
"""Tell if this bzrdir contains a branch.
589
Note: if you're going to open the branch, you should just go ahead
590
and try, and not ask permission first. (This method just opens the
591
branch and discards it, and that's somewhat expensive.)
596
except errors.NotBranchError:
599
def has_workingtree(self):
600
"""Tell if this bzrdir contains a working tree.
602
This will still raise an exception if the bzrdir has a workingtree that
603
is remote & inaccessible.
605
Note: if you're going to open the working tree, you should just go ahead
606
and try, and not ask permission first. (This method just opens the
607
workingtree and discards it, and that's somewhat expensive.)
610
self.open_workingtree()
612
except errors.NoWorkingTree:
615
def cloning_metadir(self, basis=None):
616
"""Produce a metadir suitable for cloning with"""
617
def related_repository(bzrdir):
619
branch = bzrdir.open_branch()
620
return branch.repository
621
except errors.NotBranchError:
623
return bzrdir.open_repository()
624
result_format = self._format.__class__()
627
source_repository = related_repository(self)
628
except errors.NoRepositoryPresent:
631
source_repository = related_repository(self)
632
result_format.repository_format = source_repository._format
633
except errors.NoRepositoryPresent:
637
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
638
"""Create a copy of this bzrdir prepared for use as a new line of
641
If urls last component does not exist, it will be created.
643
Attributes related to the identity of the source branch like
644
branch nickname will be cleaned, a working tree is created
645
whether one existed before or not; and a local branch is always
648
if revision_id is not None, then the clone operation may tune
649
itself to download less data.
652
cloning_format = self.cloning_metadir(basis)
653
result = cloning_format.initialize(url)
654
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
656
source_branch = self.open_branch()
657
source_repository = source_branch.repository
658
except errors.NotBranchError:
661
source_repository = self.open_repository()
662
except errors.NoRepositoryPresent:
663
# copy the entire basis one if there is one
664
# but there is no repository.
665
source_repository = basis_repo
670
result_repo = result.find_repository()
671
except errors.NoRepositoryPresent:
673
if source_repository is None and result_repo is not None:
675
elif source_repository is None and result_repo is None:
676
# no repo available, make a new one
677
result.create_repository()
678
elif source_repository is not None and result_repo is None:
679
# have source, and want to make a new target repo
680
# we don't clone the repo because that preserves attributes
681
# like is_shared(), and we have not yet implemented a
682
# repository sprout().
683
result_repo = result.create_repository()
684
if result_repo is not None:
685
# fetch needed content into target.
687
# XXX FIXME RBC 20060214 need tests for this when the basis
689
result_repo.fetch(basis_repo, revision_id=revision_id)
690
if source_repository is not None:
691
result_repo.fetch(source_repository, revision_id=revision_id)
692
if source_branch is not None:
693
source_branch.sprout(result, revision_id=revision_id)
695
result.create_branch()
696
# TODO: jam 20060426 we probably need a test in here in the
697
# case that the newly sprouted branch is a remote one
698
if result_repo is None or result_repo.make_working_trees():
699
wt = result.create_workingtree()
700
if wt.inventory.root is None:
702
wt.set_root_id(self.open_workingtree.get_root_id())
703
except errors.NoWorkingTree:
708
class BzrDirPreSplitOut(BzrDir):
709
"""A common class for the all-in-one formats."""
711
def __init__(self, _transport, _format):
712
"""See BzrDir.__init__."""
713
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
714
assert self._format._lock_class == lockable_files.TransportLock
715
assert self._format._lock_file_name == 'branch-lock'
716
self._control_files = lockable_files.LockableFiles(
717
self.get_branch_transport(None),
718
self._format._lock_file_name,
719
self._format._lock_class)
721
def break_lock(self):
722
"""Pre-splitout bzrdirs do not suffer from stale locks."""
723
raise NotImplementedError(self.break_lock)
725
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
726
"""See BzrDir.clone()."""
727
from bzrlib.workingtree import WorkingTreeFormat2
729
result = self._format._initialize_for_clone(url)
730
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
731
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
732
from_branch = self.open_branch()
733
from_branch.clone(result, revision_id=revision_id)
735
self.open_workingtree().clone(result, basis=basis_tree)
736
except errors.NotLocalUrl:
737
# make a new one, this format always has to have one.
739
WorkingTreeFormat2().initialize(result)
740
except errors.NotLocalUrl:
741
# but we cannot do it for remote trees.
742
to_branch = result.open_branch()
743
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
746
def create_branch(self):
747
"""See BzrDir.create_branch."""
748
return self.open_branch()
750
def create_repository(self, shared=False):
751
"""See BzrDir.create_repository."""
753
raise errors.IncompatibleFormat('shared repository', self._format)
754
return self.open_repository()
756
def create_workingtree(self, revision_id=None):
757
"""See BzrDir.create_workingtree."""
758
# this looks buggy but is not -really-
759
# clone and sprout will have set the revision_id
760
# and that will have set it for us, its only
761
# specific uses of create_workingtree in isolation
762
# that can do wonky stuff here, and that only
763
# happens for creating checkouts, which cannot be
764
# done on this format anyway. So - acceptable wart.
765
result = self.open_workingtree()
766
if revision_id is not None:
767
if revision_id == _mod_revision.NULL_REVISION:
768
result.set_parent_ids([])
770
result.set_parent_ids([revision_id])
773
def destroy_workingtree(self):
774
"""See BzrDir.destroy_workingtree."""
775
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
777
def destroy_workingtree_metadata(self):
778
"""See BzrDir.destroy_workingtree_metadata."""
779
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
782
def get_branch_transport(self, branch_format):
783
"""See BzrDir.get_branch_transport()."""
784
if branch_format is None:
785
return self.transport
787
branch_format.get_format_string()
788
except NotImplementedError:
789
return self.transport
790
raise errors.IncompatibleFormat(branch_format, self._format)
792
def get_repository_transport(self, repository_format):
793
"""See BzrDir.get_repository_transport()."""
794
if repository_format is None:
795
return self.transport
797
repository_format.get_format_string()
798
except NotImplementedError:
799
return self.transport
800
raise errors.IncompatibleFormat(repository_format, self._format)
802
def get_workingtree_transport(self, workingtree_format):
803
"""See BzrDir.get_workingtree_transport()."""
804
if workingtree_format is None:
805
return self.transport
807
workingtree_format.get_format_string()
808
except NotImplementedError:
809
return self.transport
810
raise errors.IncompatibleFormat(workingtree_format, self._format)
812
def needs_format_conversion(self, format=None):
813
"""See BzrDir.needs_format_conversion()."""
814
# if the format is not the same as the system default,
815
# an upgrade is needed.
817
format = BzrDirFormat.get_default_format()
818
return not isinstance(self._format, format.__class__)
820
def open_branch(self, unsupported=False):
821
"""See BzrDir.open_branch."""
822
from bzrlib.branch import BzrBranchFormat4
823
format = BzrBranchFormat4()
824
self._check_supported(format, unsupported)
825
return format.open(self, _found=True)
827
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
828
"""See BzrDir.sprout()."""
829
from bzrlib.workingtree import WorkingTreeFormat2
831
result = self._format._initialize_for_clone(url)
832
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
834
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
835
except errors.NoRepositoryPresent:
838
self.open_branch().sprout(result, revision_id=revision_id)
839
except errors.NotBranchError:
841
# we always want a working tree
842
WorkingTreeFormat2().initialize(result)
846
class BzrDir4(BzrDirPreSplitOut):
847
"""A .bzr version 4 control object.
849
This is a deprecated format and may be removed after sept 2006.
852
def create_repository(self, shared=False):
853
"""See BzrDir.create_repository."""
854
return self._format.repository_format.initialize(self, shared)
856
def needs_format_conversion(self, format=None):
857
"""Format 4 dirs are always in need of conversion."""
860
def open_repository(self):
861
"""See BzrDir.open_repository."""
862
from bzrlib.repository import RepositoryFormat4
863
return RepositoryFormat4().open(self, _found=True)
866
class BzrDir5(BzrDirPreSplitOut):
867
"""A .bzr version 5 control object.
869
This is a deprecated format and may be removed after sept 2006.
872
def open_repository(self):
873
"""See BzrDir.open_repository."""
874
from bzrlib.repository import RepositoryFormat5
875
return RepositoryFormat5().open(self, _found=True)
877
def open_workingtree(self, _unsupported=False):
878
"""See BzrDir.create_workingtree."""
879
from bzrlib.workingtree import WorkingTreeFormat2
880
return WorkingTreeFormat2().open(self, _found=True)
883
class BzrDir6(BzrDirPreSplitOut):
884
"""A .bzr version 6 control object.
886
This is a deprecated format and may be removed after sept 2006.
889
def open_repository(self):
890
"""See BzrDir.open_repository."""
891
from bzrlib.repository import RepositoryFormat6
892
return RepositoryFormat6().open(self, _found=True)
894
def open_workingtree(self, _unsupported=False):
895
"""See BzrDir.create_workingtree."""
896
from bzrlib.workingtree import WorkingTreeFormat2
897
return WorkingTreeFormat2().open(self, _found=True)
900
class BzrDirMeta1(BzrDir):
901
"""A .bzr meta version 1 control object.
903
This is the first control object where the
904
individual aspects are really split out: there are separate repository,
905
workingtree and branch subdirectories and any subset of the three can be
906
present within a BzrDir.
909
def can_convert_format(self):
910
"""See BzrDir.can_convert_format()."""
913
def create_branch(self):
914
"""See BzrDir.create_branch."""
915
from bzrlib.branch import BranchFormat
916
return BranchFormat.get_default_format().initialize(self)
918
def create_repository(self, shared=False):
919
"""See BzrDir.create_repository."""
920
return self._format.repository_format.initialize(self, shared)
922
def create_workingtree(self, revision_id=None):
923
"""See BzrDir.create_workingtree."""
924
from bzrlib.workingtree import WorkingTreeFormat
925
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
927
def destroy_workingtree(self):
928
"""See BzrDir.destroy_workingtree."""
929
wt = self.open_workingtree()
930
repository = wt.branch.repository
931
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
932
wt.revert([], old_tree=empty)
933
self.destroy_workingtree_metadata()
935
def destroy_workingtree_metadata(self):
936
self.transport.delete_tree('checkout')
938
def _get_mkdir_mode(self):
939
"""Figure out the mode to use when creating a bzrdir subdir."""
940
temp_control = lockable_files.LockableFiles(self.transport, '',
941
lockable_files.TransportLock)
942
return temp_control._dir_mode
944
def get_branch_reference(self):
945
"""See BzrDir.get_branch_reference()."""
946
from bzrlib.branch import BranchFormat
947
format = BranchFormat.find_format(self)
948
return format.get_reference(self)
950
def get_branch_transport(self, branch_format):
951
"""See BzrDir.get_branch_transport()."""
952
if branch_format is None:
953
return self.transport.clone('branch')
955
branch_format.get_format_string()
956
except NotImplementedError:
957
raise errors.IncompatibleFormat(branch_format, self._format)
959
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
960
except errors.FileExists:
962
return self.transport.clone('branch')
964
def get_repository_transport(self, repository_format):
965
"""See BzrDir.get_repository_transport()."""
966
if repository_format is None:
967
return self.transport.clone('repository')
969
repository_format.get_format_string()
970
except NotImplementedError:
971
raise errors.IncompatibleFormat(repository_format, self._format)
973
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
974
except errors.FileExists:
976
return self.transport.clone('repository')
978
def get_workingtree_transport(self, workingtree_format):
979
"""See BzrDir.get_workingtree_transport()."""
980
if workingtree_format is None:
981
return self.transport.clone('checkout')
983
workingtree_format.get_format_string()
984
except NotImplementedError:
985
raise errors.IncompatibleFormat(workingtree_format, self._format)
987
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
988
except errors.FileExists:
990
return self.transport.clone('checkout')
992
def needs_format_conversion(self, format=None):
993
"""See BzrDir.needs_format_conversion()."""
995
format = BzrDirFormat.get_default_format()
996
if not isinstance(self._format, format.__class__):
997
# it is not a meta dir format, conversion is needed.
999
# we might want to push this down to the repository?
1001
if not isinstance(self.open_repository()._format,
1002
format.repository_format.__class__):
1003
# the repository needs an upgrade.
1005
except errors.NoRepositoryPresent:
1007
# currently there are no other possible conversions for meta1 formats.
1010
def open_branch(self, unsupported=False):
1011
"""See BzrDir.open_branch."""
1012
from bzrlib.branch import BranchFormat
1013
format = BranchFormat.find_format(self)
1014
self._check_supported(format, unsupported)
1015
return format.open(self, _found=True)
1017
def open_repository(self, unsupported=False):
1018
"""See BzrDir.open_repository."""
1019
from bzrlib.repository import RepositoryFormat
1020
format = RepositoryFormat.find_format(self)
1021
self._check_supported(format, unsupported)
1022
return format.open(self, _found=True)
1024
def open_workingtree(self, unsupported=False):
1025
"""See BzrDir.open_workingtree."""
1026
from bzrlib.workingtree import WorkingTreeFormat
1027
format = WorkingTreeFormat.find_format(self)
1028
self._check_supported(format, unsupported)
1029
return format.open(self, _found=True)
1032
class BzrDirFormat(object):
1033
"""An encapsulation of the initialization and open routines for a format.
1035
Formats provide three things:
1036
* An initialization routine,
1040
Formats are placed in an dict by their format string for reference
1041
during bzrdir opening. These should be subclasses of BzrDirFormat
1044
Once a format is deprecated, just deprecate the initialize and open
1045
methods on the format class. Do not deprecate the object, as the
1046
object will be created every system load.
1049
_default_format = None
1050
"""The default format used for new .bzr dirs."""
1053
"""The known formats."""
1055
_control_formats = []
1056
"""The registered control formats - .bzr, ....
1058
This is a list of BzrDirFormat objects.
1061
_lock_file_name = 'branch-lock'
1063
# _lock_class must be set in subclasses to the lock type, typ.
1064
# TransportLock or LockDir
1067
def find_format(klass, transport):
1068
"""Return the format present at transport."""
1069
for format in klass._control_formats:
1071
return format.probe_transport(transport)
1072
except errors.NotBranchError:
1073
# this format does not find a control dir here.
1075
raise errors.NotBranchError(path=transport.base)
1078
def probe_transport(klass, transport):
1079
"""Return the .bzrdir style format present in a directory."""
1081
format_string = transport.get(".bzr/branch-format").read()
1082
except errors.NoSuchFile:
1083
raise errors.NotBranchError(path=transport.base)
1086
return klass._formats[format_string]
1088
raise errors.UnknownFormatError(format=format_string)
1091
def get_default_format(klass):
1092
"""Return the current default format."""
1093
return klass._default_format
1095
def get_format_string(self):
1096
"""Return the ASCII format string that identifies this format."""
1097
raise NotImplementedError(self.get_format_string)
1099
def get_format_description(self):
1100
"""Return the short description for this format."""
1101
raise NotImplementedError(self.get_format_description)
1103
def get_converter(self, format=None):
1104
"""Return the converter to use to convert bzrdirs needing converts.
1106
This returns a bzrlib.bzrdir.Converter object.
1108
This should return the best upgrader to step this format towards the
1109
current default format. In the case of plugins we can/should provide
1110
some means for them to extend the range of returnable converters.
1112
:param format: Optional format to override the default format of the
1115
raise NotImplementedError(self.get_converter)
1117
def initialize(self, url):
1118
"""Create a bzr control dir at this url and return an opened copy.
1120
Subclasses should typically override initialize_on_transport
1121
instead of this method.
1123
return self.initialize_on_transport(get_transport(url))
1125
def initialize_on_transport(self, transport):
1126
"""Initialize a new bzrdir in the base directory of a Transport."""
1127
# Since we don't have a .bzr directory, inherit the
1128
# mode from the root directory
1129
temp_control = lockable_files.LockableFiles(transport,
1130
'', lockable_files.TransportLock)
1131
temp_control._transport.mkdir('.bzr',
1132
# FIXME: RBC 20060121 don't peek under
1134
mode=temp_control._dir_mode)
1135
file_mode = temp_control._file_mode
1137
mutter('created control directory in ' + transport.base)
1138
control = transport.clone('.bzr')
1139
utf8_files = [('README',
1140
"This is a Bazaar-NG control directory.\n"
1141
"Do not change any files in this directory.\n"),
1142
('branch-format', self.get_format_string()),
1144
# NB: no need to escape relative paths that are url safe.
1145
control_files = lockable_files.LockableFiles(control,
1146
self._lock_file_name, self._lock_class)
1147
control_files.create_lock()
1148
control_files.lock_write()
1150
for file, content in utf8_files:
1151
control_files.put_utf8(file, content)
1153
control_files.unlock()
1154
return self.open(transport, _found=True)
1156
def is_supported(self):
1157
"""Is this format supported?
1159
Supported formats must be initializable and openable.
1160
Unsupported formats may not support initialization or committing or
1161
some other features depending on the reason for not being supported.
1165
def same_model(self, target_format):
1166
return (self.repository_format.rich_root_data ==
1167
target_format.rich_root_data)
1170
def known_formats(klass):
1171
"""Return all the known formats.
1173
Concrete formats should override _known_formats.
1175
# There is double indirection here to make sure that control
1176
# formats used by more than one dir format will only be probed
1177
# once. This can otherwise be quite expensive for remote connections.
1179
for format in klass._control_formats:
1180
result.update(format._known_formats())
1184
def _known_formats(klass):
1185
"""Return the known format instances for this control format."""
1186
return set(klass._formats.values())
1188
def open(self, transport, _found=False):
1189
"""Return an instance of this format for the dir transport points at.
1191
_found is a private parameter, do not use it.
1194
found_format = BzrDirFormat.find_format(transport)
1195
if not isinstance(found_format, self.__class__):
1196
raise AssertionError("%s was asked to open %s, but it seems to need "
1198
% (self, transport, found_format))
1199
return self._open(transport)
1201
def _open(self, transport):
1202
"""Template method helper for opening BzrDirectories.
1204
This performs the actual open and any additional logic or parameter
1207
raise NotImplementedError(self._open)
1210
def register_format(klass, format):
1211
klass._formats[format.get_format_string()] = format
1214
def register_control_format(klass, format):
1215
"""Register a format that does not use '.bzrdir' for its control dir.
1217
TODO: This should be pulled up into a 'ControlDirFormat' base class
1218
which BzrDirFormat can inherit from, and renamed to register_format
1219
there. It has been done without that for now for simplicity of
1222
klass._control_formats.append(format)
1225
def set_default_format(klass, format):
1226
klass._default_format = format
1229
return self.get_format_string()[:-1]
1232
def unregister_format(klass, format):
1233
assert klass._formats[format.get_format_string()] is format
1234
del klass._formats[format.get_format_string()]
1237
def unregister_control_format(klass, format):
1238
klass._control_formats.remove(format)
1241
# register BzrDirFormat as a control format
1242
BzrDirFormat.register_control_format(BzrDirFormat)
1245
class BzrDirFormat4(BzrDirFormat):
1246
"""Bzr dir format 4.
1248
This format is a combined format for working tree, branch and repository.
1250
- Format 1 working trees [always]
1251
- Format 4 branches [always]
1252
- Format 4 repositories [always]
1254
This format is deprecated: it indexes texts using a text it which is
1255
removed in format 5; write support for this format has been removed.
1258
_lock_class = lockable_files.TransportLock
1260
def get_format_string(self):
1261
"""See BzrDirFormat.get_format_string()."""
1262
return "Bazaar-NG branch, format 0.0.4\n"
1264
def get_format_description(self):
1265
"""See BzrDirFormat.get_format_description()."""
1266
return "All-in-one format 4"
1268
def get_converter(self, format=None):
1269
"""See BzrDirFormat.get_converter()."""
1270
# there is one and only one upgrade path here.
1271
return ConvertBzrDir4To5()
1273
def initialize_on_transport(self, transport):
1274
"""Format 4 branches cannot be created."""
1275
raise errors.UninitializableFormat(self)
1277
def is_supported(self):
1278
"""Format 4 is not supported.
1280
It is not supported because the model changed from 4 to 5 and the
1281
conversion logic is expensive - so doing it on the fly was not
1286
def _open(self, transport):
1287
"""See BzrDirFormat._open."""
1288
return BzrDir4(transport, self)
1290
def __return_repository_format(self):
1291
"""Circular import protection."""
1292
from bzrlib.repository import RepositoryFormat4
1293
return RepositoryFormat4()
1294
repository_format = property(__return_repository_format)
1297
class BzrDirFormat5(BzrDirFormat):
1298
"""Bzr control format 5.
1300
This format is a combined format for working tree, branch and repository.
1302
- Format 2 working trees [always]
1303
- Format 4 branches [always]
1304
- Format 5 repositories [always]
1305
Unhashed stores in the repository.
1308
_lock_class = lockable_files.TransportLock
1310
def get_format_string(self):
1311
"""See BzrDirFormat.get_format_string()."""
1312
return "Bazaar-NG branch, format 5\n"
1314
def get_format_description(self):
1315
"""See BzrDirFormat.get_format_description()."""
1316
return "All-in-one format 5"
1318
def get_converter(self, format=None):
1319
"""See BzrDirFormat.get_converter()."""
1320
# there is one and only one upgrade path here.
1321
return ConvertBzrDir5To6()
1323
def _initialize_for_clone(self, url):
1324
return self.initialize_on_transport(get_transport(url), _cloning=True)
1326
def initialize_on_transport(self, transport, _cloning=False):
1327
"""Format 5 dirs always have working tree, branch and repository.
1329
Except when they are being cloned.
1331
from bzrlib.branch import BzrBranchFormat4
1332
from bzrlib.repository import RepositoryFormat5
1333
from bzrlib.workingtree import WorkingTreeFormat2
1334
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1335
RepositoryFormat5().initialize(result, _internal=True)
1337
branch = BzrBranchFormat4().initialize(result)
1339
WorkingTreeFormat2().initialize(result)
1340
except errors.NotLocalUrl:
1341
# Even though we can't access the working tree, we need to
1342
# create its control files.
1343
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1346
def _open(self, transport):
1347
"""See BzrDirFormat._open."""
1348
return BzrDir5(transport, self)
1350
def __return_repository_format(self):
1351
"""Circular import protection."""
1352
from bzrlib.repository import RepositoryFormat5
1353
return RepositoryFormat5()
1354
repository_format = property(__return_repository_format)
1357
class BzrDirFormat6(BzrDirFormat):
1358
"""Bzr control format 6.
1360
This format is a combined format for working tree, branch and repository.
1362
- Format 2 working trees [always]
1363
- Format 4 branches [always]
1364
- Format 6 repositories [always]
1367
_lock_class = lockable_files.TransportLock
1369
def get_format_string(self):
1370
"""See BzrDirFormat.get_format_string()."""
1371
return "Bazaar-NG branch, format 6\n"
1373
def get_format_description(self):
1374
"""See BzrDirFormat.get_format_description()."""
1375
return "All-in-one format 6"
1377
def get_converter(self, format=None):
1378
"""See BzrDirFormat.get_converter()."""
1379
# there is one and only one upgrade path here.
1380
return ConvertBzrDir6ToMeta()
1382
def _initialize_for_clone(self, url):
1383
return self.initialize_on_transport(get_transport(url), _cloning=True)
1385
def initialize_on_transport(self, transport, _cloning=False):
1386
"""Format 6 dirs always have working tree, branch and repository.
1388
Except when they are being cloned.
1390
from bzrlib.branch import BzrBranchFormat4
1391
from bzrlib.repository import RepositoryFormat6
1392
from bzrlib.workingtree import WorkingTreeFormat2
1393
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1394
RepositoryFormat6().initialize(result, _internal=True)
1396
branch = BzrBranchFormat4().initialize(result)
1398
WorkingTreeFormat2().initialize(result)
1399
except errors.NotLocalUrl:
1400
# Even though we can't access the working tree, we need to
1401
# create its control files.
1402
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1405
def _open(self, transport):
1406
"""See BzrDirFormat._open."""
1407
return BzrDir6(transport, self)
1409
def __return_repository_format(self):
1410
"""Circular import protection."""
1411
from bzrlib.repository import RepositoryFormat6
1412
return RepositoryFormat6()
1413
repository_format = property(__return_repository_format)
1416
class BzrDirMetaFormat1(BzrDirFormat):
1417
"""Bzr meta control format 1
1419
This is the first format with split out working tree, branch and repository
1422
- Format 3 working trees [optional]
1423
- Format 5 branches [optional]
1424
- Format 7 repositories [optional]
1427
_lock_class = lockdir.LockDir
1429
def get_converter(self, format=None):
1430
"""See BzrDirFormat.get_converter()."""
1432
format = BzrDirFormat.get_default_format()
1433
if not isinstance(self, format.__class__):
1434
# converting away from metadir is not implemented
1435
raise NotImplementedError(self.get_converter)
1436
return ConvertMetaToMeta(format)
1438
def get_format_string(self):
1439
"""See BzrDirFormat.get_format_string()."""
1440
return "Bazaar-NG meta directory, format 1\n"
1442
def get_format_description(self):
1443
"""See BzrDirFormat.get_format_description()."""
1444
return "Meta directory format 1"
1446
def _open(self, transport):
1447
"""See BzrDirFormat._open."""
1448
return BzrDirMeta1(transport, self)
1450
def __return_repository_format(self):
1451
"""Circular import protection."""
1452
if getattr(self, '_repository_format', None):
1453
return self._repository_format
1454
from bzrlib.repository import RepositoryFormat
1455
return RepositoryFormat.get_default_format()
1457
def __set_repository_format(self, value):
1458
"""Allow changint the repository format for metadir formats."""
1459
self._repository_format = value
1461
repository_format = property(__return_repository_format, __set_repository_format)
1464
BzrDirFormat.register_format(BzrDirFormat4())
1465
BzrDirFormat.register_format(BzrDirFormat5())
1466
BzrDirFormat.register_format(BzrDirFormat6())
1467
__default_format = BzrDirMetaFormat1()
1468
BzrDirFormat.register_format(__default_format)
1469
BzrDirFormat.set_default_format(__default_format)
1472
class BzrDirTestProviderAdapter(object):
1473
"""A tool to generate a suite testing multiple bzrdir formats at once.
1475
This is done by copying the test once for each transport and injecting
1476
the transport_server, transport_readonly_server, and bzrdir_format
1477
classes into each copy. Each copy is also given a new id() to make it
1481
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1483
"""Create an object to adapt tests.
1485
:param vfs_server: A factory to create a Transport Server which has
1486
all the VFS methods working, and is writable.
1488
self._vfs_factory = vfs_factory
1489
self._transport_server = transport_server
1490
self._transport_readonly_server = transport_readonly_server
1491
self._formats = formats
1493
def adapt(self, test):
1494
result = unittest.TestSuite()
1495
for format in self._formats:
1496
new_test = deepcopy(test)
1497
new_test.vfs_transport_factory = self._vfs_factory
1498
new_test.transport_server = self._transport_server
1499
new_test.transport_readonly_server = self._transport_readonly_server
1500
new_test.bzrdir_format = format
1501
def make_new_test_id():
1502
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1503
return lambda: new_id
1504
new_test.id = make_new_test_id()
1505
result.addTest(new_test)
1509
class Converter(object):
1510
"""Converts a disk format object from one format to another."""
1512
def convert(self, to_convert, pb):
1513
"""Perform the conversion of to_convert, giving feedback via pb.
1515
:param to_convert: The disk object to convert.
1516
:param pb: a progress bar to use for progress information.
1519
def step(self, message):
1520
"""Update the pb by a step."""
1522
self.pb.update(message, self.count, self.total)
1525
class ConvertBzrDir4To5(Converter):
1526
"""Converts format 4 bzr dirs to format 5."""
1529
super(ConvertBzrDir4To5, self).__init__()
1530
self.converted_revs = set()
1531
self.absent_revisions = set()
1535
def convert(self, to_convert, pb):
1536
"""See Converter.convert()."""
1537
self.bzrdir = to_convert
1539
self.pb.note('starting upgrade from format 4 to 5')
1540
if isinstance(self.bzrdir.transport, LocalTransport):
1541
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1542
self._convert_to_weaves()
1543
return BzrDir.open(self.bzrdir.root_transport.base)
1545
def _convert_to_weaves(self):
1546
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1549
stat = self.bzrdir.transport.stat('weaves')
1550
if not S_ISDIR(stat.st_mode):
1551
self.bzrdir.transport.delete('weaves')
1552
self.bzrdir.transport.mkdir('weaves')
1553
except errors.NoSuchFile:
1554
self.bzrdir.transport.mkdir('weaves')
1555
# deliberately not a WeaveFile as we want to build it up slowly.
1556
self.inv_weave = Weave('inventory')
1557
# holds in-memory weaves for all files
1558
self.text_weaves = {}
1559
self.bzrdir.transport.delete('branch-format')
1560
self.branch = self.bzrdir.open_branch()
1561
self._convert_working_inv()
1562
rev_history = self.branch.revision_history()
1563
# to_read is a stack holding the revisions we still need to process;
1564
# appending to it adds new highest-priority revisions
1565
self.known_revisions = set(rev_history)
1566
self.to_read = rev_history[-1:]
1568
rev_id = self.to_read.pop()
1569
if (rev_id not in self.revisions
1570
and rev_id not in self.absent_revisions):
1571
self._load_one_rev(rev_id)
1573
to_import = self._make_order()
1574
for i, rev_id in enumerate(to_import):
1575
self.pb.update('converting revision', i, len(to_import))
1576
self._convert_one_rev(rev_id)
1578
self._write_all_weaves()
1579
self._write_all_revs()
1580
self.pb.note('upgraded to weaves:')
1581
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1582
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1583
self.pb.note(' %6d texts', self.text_count)
1584
self._cleanup_spare_files_after_format4()
1585
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1587
def _cleanup_spare_files_after_format4(self):
1588
# FIXME working tree upgrade foo.
1589
for n in 'merged-patches', 'pending-merged-patches':
1591
## assert os.path.getsize(p) == 0
1592
self.bzrdir.transport.delete(n)
1593
except errors.NoSuchFile:
1595
self.bzrdir.transport.delete_tree('inventory-store')
1596
self.bzrdir.transport.delete_tree('text-store')
1598
def _convert_working_inv(self):
1599
inv = xml4.serializer_v4.read_inventory(
1600
self.branch.control_files.get('inventory'))
1601
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1602
# FIXME inventory is a working tree change.
1603
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1605
def _write_all_weaves(self):
1606
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1607
weave_transport = self.bzrdir.transport.clone('weaves')
1608
weaves = WeaveStore(weave_transport, prefixed=False)
1609
transaction = WriteTransaction()
1613
for file_id, file_weave in self.text_weaves.items():
1614
self.pb.update('writing weave', i, len(self.text_weaves))
1615
weaves._put_weave(file_id, file_weave, transaction)
1617
self.pb.update('inventory', 0, 1)
1618
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1619
self.pb.update('inventory', 1, 1)
1623
def _write_all_revs(self):
1624
"""Write all revisions out in new form."""
1625
self.bzrdir.transport.delete_tree('revision-store')
1626
self.bzrdir.transport.mkdir('revision-store')
1627
revision_transport = self.bzrdir.transport.clone('revision-store')
1629
_revision_store = TextRevisionStore(TextStore(revision_transport,
1633
transaction = WriteTransaction()
1634
for i, rev_id in enumerate(self.converted_revs):
1635
self.pb.update('write revision', i, len(self.converted_revs))
1636
_revision_store.add_revision(self.revisions[rev_id], transaction)
1640
def _load_one_rev(self, rev_id):
1641
"""Load a revision object into memory.
1643
Any parents not either loaded or abandoned get queued to be
1645
self.pb.update('loading revision',
1646
len(self.revisions),
1647
len(self.known_revisions))
1648
if not self.branch.repository.has_revision(rev_id):
1650
self.pb.note('revision {%s} not present in branch; '
1651
'will be converted as a ghost',
1653
self.absent_revisions.add(rev_id)
1655
rev = self.branch.repository._revision_store.get_revision(rev_id,
1656
self.branch.repository.get_transaction())
1657
for parent_id in rev.parent_ids:
1658
self.known_revisions.add(parent_id)
1659
self.to_read.append(parent_id)
1660
self.revisions[rev_id] = rev
1662
def _load_old_inventory(self, rev_id):
1663
assert rev_id not in self.converted_revs
1664
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1665
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1666
inv.revision_id = rev_id
1667
rev = self.revisions[rev_id]
1668
if rev.inventory_sha1:
1669
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1670
'inventory sha mismatch for {%s}' % rev_id
1673
def _load_updated_inventory(self, rev_id):
1674
assert rev_id in self.converted_revs
1675
inv_xml = self.inv_weave.get_text(rev_id)
1676
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1679
def _convert_one_rev(self, rev_id):
1680
"""Convert revision and all referenced objects to new format."""
1681
rev = self.revisions[rev_id]
1682
inv = self._load_old_inventory(rev_id)
1683
present_parents = [p for p in rev.parent_ids
1684
if p not in self.absent_revisions]
1685
self._convert_revision_contents(rev, inv, present_parents)
1686
self._store_new_weave(rev, inv, present_parents)
1687
self.converted_revs.add(rev_id)
1689
def _store_new_weave(self, rev, inv, present_parents):
1690
# the XML is now updated with text versions
1692
entries = inv.iter_entries()
1694
for path, ie in entries:
1695
assert getattr(ie, 'revision', None) is not None, \
1696
'no revision on {%s} in {%s}' % \
1697
(file_id, rev.revision_id)
1698
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1699
new_inv_sha1 = sha_string(new_inv_xml)
1700
self.inv_weave.add_lines(rev.revision_id,
1702
new_inv_xml.splitlines(True))
1703
rev.inventory_sha1 = new_inv_sha1
1705
def _convert_revision_contents(self, rev, inv, present_parents):
1706
"""Convert all the files within a revision.
1708
Also upgrade the inventory to refer to the text revision ids."""
1709
rev_id = rev.revision_id
1710
mutter('converting texts of revision {%s}',
1712
parent_invs = map(self._load_updated_inventory, present_parents)
1713
entries = inv.iter_entries()
1715
for path, ie in entries:
1716
self._convert_file_version(rev, ie, parent_invs)
1718
def _convert_file_version(self, rev, ie, parent_invs):
1719
"""Convert one version of one file.
1721
The file needs to be added into the weave if it is a merge
1722
of >=2 parents or if it's changed from its parent.
1724
file_id = ie.file_id
1725
rev_id = rev.revision_id
1726
w = self.text_weaves.get(file_id)
1729
self.text_weaves[file_id] = w
1730
text_changed = False
1731
previous_entries = ie.find_previous_heads(parent_invs,
1735
for old_revision in previous_entries:
1736
# if this fails, its a ghost ?
1737
assert old_revision in self.converted_revs, \
1738
"Revision {%s} not in converted_revs" % old_revision
1739
self.snapshot_ie(previous_entries, ie, w, rev_id)
1741
assert getattr(ie, 'revision', None) is not None
1743
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1744
# TODO: convert this logic, which is ~= snapshot to
1745
# a call to:. This needs the path figured out. rather than a work_tree
1746
# a v4 revision_tree can be given, or something that looks enough like
1747
# one to give the file content to the entry if it needs it.
1748
# and we need something that looks like a weave store for snapshot to
1750
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1751
if len(previous_revisions) == 1:
1752
previous_ie = previous_revisions.values()[0]
1753
if ie._unchanged(previous_ie):
1754
ie.revision = previous_ie.revision
1757
text = self.branch.repository.text_store.get(ie.text_id)
1758
file_lines = text.readlines()
1759
assert sha_strings(file_lines) == ie.text_sha1
1760
assert sum(map(len, file_lines)) == ie.text_size
1761
w.add_lines(rev_id, previous_revisions, file_lines)
1762
self.text_count += 1
1764
w.add_lines(rev_id, previous_revisions, [])
1765
ie.revision = rev_id
1767
def _make_order(self):
1768
"""Return a suitable order for importing revisions.
1770
The order must be such that an revision is imported after all
1771
its (present) parents.
1773
todo = set(self.revisions.keys())
1774
done = self.absent_revisions.copy()
1777
# scan through looking for a revision whose parents
1779
for rev_id in sorted(list(todo)):
1780
rev = self.revisions[rev_id]
1781
parent_ids = set(rev.parent_ids)
1782
if parent_ids.issubset(done):
1783
# can take this one now
1784
order.append(rev_id)
1790
class ConvertBzrDir5To6(Converter):
1791
"""Converts format 5 bzr dirs to format 6."""
1793
def convert(self, to_convert, pb):
1794
"""See Converter.convert()."""
1795
self.bzrdir = to_convert
1797
self.pb.note('starting upgrade from format 5 to 6')
1798
self._convert_to_prefixed()
1799
return BzrDir.open(self.bzrdir.root_transport.base)
1801
def _convert_to_prefixed(self):
1802
from bzrlib.store import TransportStore
1803
self.bzrdir.transport.delete('branch-format')
1804
for store_name in ["weaves", "revision-store"]:
1805
self.pb.note("adding prefixes to %s" % store_name)
1806
store_transport = self.bzrdir.transport.clone(store_name)
1807
store = TransportStore(store_transport, prefixed=True)
1808
for urlfilename in store_transport.list_dir('.'):
1809
filename = urlutils.unescape(urlfilename)
1810
if (filename.endswith(".weave") or
1811
filename.endswith(".gz") or
1812
filename.endswith(".sig")):
1813
file_id = os.path.splitext(filename)[0]
1816
prefix_dir = store.hash_prefix(file_id)
1817
# FIXME keep track of the dirs made RBC 20060121
1819
store_transport.move(filename, prefix_dir + '/' + filename)
1820
except errors.NoSuchFile: # catches missing dirs strangely enough
1821
store_transport.mkdir(prefix_dir)
1822
store_transport.move(filename, prefix_dir + '/' + filename)
1823
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1826
class ConvertBzrDir6ToMeta(Converter):
1827
"""Converts format 6 bzr dirs to metadirs."""
1829
def convert(self, to_convert, pb):
1830
"""See Converter.convert()."""
1831
from bzrlib.branch import BzrBranchFormat5
1832
self.bzrdir = to_convert
1835
self.total = 20 # the steps we know about
1836
self.garbage_inventories = []
1838
self.pb.note('starting upgrade from format 6 to metadir')
1839
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1840
# its faster to move specific files around than to open and use the apis...
1841
# first off, nuke ancestry.weave, it was never used.
1843
self.step('Removing ancestry.weave')
1844
self.bzrdir.transport.delete('ancestry.weave')
1845
except errors.NoSuchFile:
1847
# find out whats there
1848
self.step('Finding branch files')
1849
last_revision = self.bzrdir.open_branch().last_revision()
1850
bzrcontents = self.bzrdir.transport.list_dir('.')
1851
for name in bzrcontents:
1852
if name.startswith('basis-inventory.'):
1853
self.garbage_inventories.append(name)
1854
# create new directories for repository, working tree and branch
1855
self.dir_mode = self.bzrdir._control_files._dir_mode
1856
self.file_mode = self.bzrdir._control_files._file_mode
1857
repository_names = [('inventory.weave', True),
1858
('revision-store', True),
1860
self.step('Upgrading repository ')
1861
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1862
self.make_lock('repository')
1863
# we hard code the formats here because we are converting into
1864
# the meta format. The meta format upgrader can take this to a
1865
# future format within each component.
1866
self.put_format('repository', _mod_repository.RepositoryFormat7())
1867
for entry in repository_names:
1868
self.move_entry('repository', entry)
1870
self.step('Upgrading branch ')
1871
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1872
self.make_lock('branch')
1873
self.put_format('branch', BzrBranchFormat5())
1874
branch_files = [('revision-history', True),
1875
('branch-name', True),
1877
for entry in branch_files:
1878
self.move_entry('branch', entry)
1880
checkout_files = [('pending-merges', True),
1881
('inventory', True),
1882
('stat-cache', False)]
1883
# If a mandatory checkout file is not present, the branch does not have
1884
# a functional checkout. Do not create a checkout in the converted
1886
for name, mandatory in checkout_files:
1887
if mandatory and name not in bzrcontents:
1888
has_checkout = False
1892
if not has_checkout:
1893
self.pb.note('No working tree.')
1894
# If some checkout files are there, we may as well get rid of them.
1895
for name, mandatory in checkout_files:
1896
if name in bzrcontents:
1897
self.bzrdir.transport.delete(name)
1899
from bzrlib.workingtree import WorkingTreeFormat3
1900
self.step('Upgrading working tree')
1901
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1902
self.make_lock('checkout')
1904
'checkout', WorkingTreeFormat3())
1905
self.bzrdir.transport.delete_multi(
1906
self.garbage_inventories, self.pb)
1907
for entry in checkout_files:
1908
self.move_entry('checkout', entry)
1909
if last_revision is not None:
1910
self.bzrdir._control_files.put_utf8(
1911
'checkout/last-revision', last_revision)
1912
self.bzrdir._control_files.put_utf8(
1913
'branch-format', BzrDirMetaFormat1().get_format_string())
1914
return BzrDir.open(self.bzrdir.root_transport.base)
1916
def make_lock(self, name):
1917
"""Make a lock for the new control dir name."""
1918
self.step('Make %s lock' % name)
1919
ld = lockdir.LockDir(self.bzrdir.transport,
1921
file_modebits=self.file_mode,
1922
dir_modebits=self.dir_mode)
1925
def move_entry(self, new_dir, entry):
1926
"""Move then entry name into new_dir."""
1928
mandatory = entry[1]
1929
self.step('Moving %s' % name)
1931
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1932
except errors.NoSuchFile:
1936
def put_format(self, dirname, format):
1937
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1940
class ConvertMetaToMeta(Converter):
1941
"""Converts the components of metadirs."""
1943
def __init__(self, target_format):
1944
"""Create a metadir to metadir converter.
1946
:param target_format: The final metadir format that is desired.
1948
self.target_format = target_format
1950
def convert(self, to_convert, pb):
1951
"""See Converter.convert()."""
1952
self.bzrdir = to_convert
1956
self.step('checking repository format')
1958
repo = self.bzrdir.open_repository()
1959
except errors.NoRepositoryPresent:
1962
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1963
from bzrlib.repository import CopyConverter
1964
self.pb.note('starting repository conversion')
1965
converter = CopyConverter(self.target_format.repository_format)
1966
converter.convert(repo, pb)
1970
# This is not in remote.py because it's small, and needs to be registered.
1971
# Putting it in remote.py creates a circular import problem.
1972
# we can make it a lazy object if the control formats is turned into something
1974
class RemoteBzrDirFormat(BzrDirMetaFormat1):
1975
"""Format representing bzrdirs accessed via a smart server"""
1977
def get_format_description(self):
1978
return 'bzr remote bzrdir'
1981
def probe_transport(klass, transport):
1982
"""Return a RemoteBzrDirFormat object if it looks possible."""
1984
transport.get_smart_client()
1985
except (NotImplementedError, AttributeError,
1986
errors.TransportNotPossible):
1987
# no smart server, so not a branch for this format type.
1988
raise errors.NotBranchError(path=transport.base)
1992
def initialize_on_transport(self, transport):
1993
# hand off the request to the smart server
1994
medium = transport.get_smart_medium()
1995
client = SmartClient(medium)
1996
path = client.remote_path_from_transport(transport)
1997
response = SmartClient(medium).call('BzrDirFormat.initialize', path)
1998
assert response[0] in ('ok', ), 'unexpected response code %s' % response[0]
1999
return remote.RemoteBzrDir(transport)
2001
def _open(self, transport):
2002
return remote.RemoteBzrDir(transport)
2004
def __eq__(self, other):
2005
if not isinstance(other, RemoteBzrDirFormat):
2007
return self.get_format_description() == other.get_format_description()
2010
# We can't use register_control_format because it adds it at a lower priority
2011
# than the existing branches, whereas this should take priority.
2012
BzrDirFormat._control_formats.insert(0, RemoteBzrDirFormat)