1
# Copyright (C) 2005, 2006, 2007 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
32
from bzrlib.lazy_import import lazy_import
33
lazy_import(globals(), """
34
from copy import deepcopy
35
from stat import S_ISDIR
45
revision as _mod_revision,
51
from bzrlib.osutils import (
56
from bzrlib.smart.client import SmartClient
57
from bzrlib.store.revision.text import TextRevisionStore
58
from bzrlib.store.text import TextStore
59
from bzrlib.store.versioned import WeaveStore
60
from bzrlib.transactions import WriteTransaction
61
from bzrlib.transport import get_transport
62
from bzrlib.weave import Weave
65
from bzrlib.trace import mutter
66
from bzrlib.transport.local import LocalTransport
70
"""A .bzr control diretory.
72
BzrDir instances let you create or open any of the things that can be
73
found within .bzr - checkouts, branches and repositories.
76
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
78
a transport connected to the directory this bzr was opened from.
82
"""Invoke break_lock on the first object in the bzrdir.
84
If there is a tree, the tree is opened and break_lock() called.
85
Otherwise, branch is tried, and finally repository.
87
# XXX: This seems more like a UI function than something that really
88
# belongs in this class.
90
thing_to_unlock = self.open_workingtree()
91
except (errors.NotLocalUrl, errors.NoWorkingTree):
93
thing_to_unlock = self.open_branch()
94
except errors.NotBranchError:
96
thing_to_unlock = self.open_repository()
97
except errors.NoRepositoryPresent:
99
thing_to_unlock.break_lock()
101
def can_convert_format(self):
102
"""Return true if this bzrdir is one whose format we can convert from."""
105
def check_conversion_target(self, target_format):
106
target_repo_format = target_format.repository_format
107
source_repo_format = self._format.repository_format
108
source_repo_format.check_conversion_target(target_repo_format)
111
def _check_supported(format, allow_unsupported):
112
"""Check whether format is a supported format.
114
If allow_unsupported is True, this is a no-op.
116
if not allow_unsupported and not format.is_supported():
117
# see open_downlevel to open legacy branches.
118
raise errors.UnsupportedFormatError(format=format)
120
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
121
"""Clone this bzrdir and its contents to url verbatim.
123
If urls last component does not exist, it will be created.
125
if revision_id is not None, then the clone operation may tune
126
itself to download less data.
127
:param force_new_repo: Do not use a shared repository for the target
128
even if one is available.
131
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
132
result = self._format.initialize(url)
134
local_repo = self.find_repository()
135
except errors.NoRepositoryPresent:
138
# may need to copy content in
140
result_repo = local_repo.clone(
142
revision_id=revision_id,
144
result_repo.set_make_working_trees(local_repo.make_working_trees())
147
result_repo = result.find_repository()
148
# fetch content this dir needs.
150
# XXX FIXME RBC 20060214 need tests for this when the basis
152
result_repo.fetch(basis_repo, revision_id=revision_id)
153
result_repo.fetch(local_repo, revision_id=revision_id)
154
except errors.NoRepositoryPresent:
155
# needed to make one anyway.
156
result_repo = local_repo.clone(
158
revision_id=revision_id,
160
result_repo.set_make_working_trees(local_repo.make_working_trees())
161
# 1 if there is a branch present
162
# make sure its content is available in the target repository
165
self.open_branch().clone(result, revision_id=revision_id)
166
except errors.NotBranchError:
169
self.open_workingtree().clone(result, basis=basis_tree)
170
except (errors.NoWorkingTree, errors.NotLocalUrl):
174
def _get_basis_components(self, basis):
175
"""Retrieve the basis components that are available at basis."""
177
return None, None, None
179
basis_tree = basis.open_workingtree()
180
basis_branch = basis_tree.branch
181
basis_repo = basis_branch.repository
182
except (errors.NoWorkingTree, errors.NotLocalUrl):
185
basis_branch = basis.open_branch()
186
basis_repo = basis_branch.repository
187
except errors.NotBranchError:
190
basis_repo = basis.open_repository()
191
except errors.NoRepositoryPresent:
193
return basis_repo, basis_branch, basis_tree
195
# TODO: This should be given a Transport, and should chdir up; otherwise
196
# this will open a new connection.
197
def _make_tail(self, url):
198
head, tail = urlutils.split(url)
199
if tail and tail != '.':
200
t = get_transport(head)
203
except errors.FileExists:
206
# TODO: Should take a Transport
208
def create(cls, base, format=None):
209
"""Create a new BzrDir at the url 'base'.
211
This will call the current default formats initialize with base
212
as the only parameter.
214
:param format: If supplied, the format of branch to create. If not
215
supplied, the default is used.
217
if cls is not BzrDir:
218
raise AssertionError("BzrDir.create always creates the default"
219
" format, not one of %r" % cls)
220
head, tail = urlutils.split(base)
221
if tail and tail != '.':
222
t = get_transport(head)
225
except errors.FileExists:
228
format = BzrDirFormat.get_default_format()
229
return format.initialize(safe_unicode(base))
231
def create_branch(self):
232
"""Create a branch in this BzrDir.
234
The bzrdirs format will control what branch format is created.
235
For more control see BranchFormatXX.create(a_bzrdir).
237
raise NotImplementedError(self.create_branch)
240
def create_branch_and_repo(base, force_new_repo=False, format=None):
241
"""Create a new BzrDir, Branch and Repository at the url 'base'.
243
This will use the current default BzrDirFormat, and use whatever
244
repository format that that uses via bzrdir.create_branch and
245
create_repository. If a shared repository is available that is used
248
The created Branch object is returned.
250
:param base: The URL to create the branch at.
251
:param force_new_repo: If True a new repository is always created.
253
bzrdir = BzrDir.create(base, format)
254
bzrdir._find_or_create_repository(force_new_repo)
255
return bzrdir.create_branch()
257
def _find_or_create_repository(self, force_new_repo):
258
"""Create a new repository if needed, returning the repository."""
260
return self.create_repository()
262
return self.find_repository()
263
except errors.NoRepositoryPresent:
264
return self.create_repository()
267
def create_branch_convenience(base, force_new_repo=False,
268
force_new_tree=None, format=None):
269
"""Create a new BzrDir, Branch and Repository at the url 'base'.
271
This is a convenience function - it will use an existing repository
272
if possible, can be told explicitly whether to create a working tree or
275
This will use the current default BzrDirFormat, and use whatever
276
repository format that that uses via bzrdir.create_branch and
277
create_repository. If a shared repository is available that is used
278
preferentially. Whatever repository is used, its tree creation policy
281
The created Branch object is returned.
282
If a working tree cannot be made due to base not being a file:// url,
283
no error is raised unless force_new_tree is True, in which case no
284
data is created on disk and NotLocalUrl is raised.
286
:param base: The URL to create the branch at.
287
:param force_new_repo: If True a new repository is always created.
288
:param force_new_tree: If True or False force creation of a tree or
289
prevent such creation respectively.
290
:param format: Override for the for the bzrdir format to create
293
# check for non local urls
294
t = get_transport(safe_unicode(base))
295
if not isinstance(t, LocalTransport):
296
raise errors.NotLocalUrl(base)
297
bzrdir = BzrDir.create(base, format)
298
repo = bzrdir._find_or_create_repository(force_new_repo)
299
result = bzrdir.create_branch()
300
if force_new_tree or (repo.make_working_trees() and
301
force_new_tree is None):
303
bzrdir.create_workingtree()
304
except errors.NotLocalUrl:
309
def create_repository(base, shared=False, format=None):
310
"""Create a new BzrDir and Repository at the url 'base'.
312
If no format is supplied, this will default to the current default
313
BzrDirFormat by default, and use whatever repository format that that
314
uses for bzrdirformat.create_repository.
316
:param shared: Create a shared repository rather than a standalone
318
The Repository object is returned.
320
This must be overridden as an instance method in child classes, where
321
it should take no parameters and construct whatever repository format
322
that child class desires.
324
bzrdir = BzrDir.create(base, format)
325
return bzrdir.create_repository(shared)
328
def create_standalone_workingtree(base, format=None):
329
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
331
'base' must be a local path or a file:// url.
333
This will use the current default BzrDirFormat, and use whatever
334
repository format that that uses for bzrdirformat.create_workingtree,
335
create_branch and create_repository.
337
:return: The WorkingTree object.
339
t = get_transport(safe_unicode(base))
340
if not isinstance(t, LocalTransport):
341
raise errors.NotLocalUrl(base)
342
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
344
format=format).bzrdir
345
return bzrdir.create_workingtree()
347
def create_workingtree(self, revision_id=None):
348
"""Create a working tree at this BzrDir.
350
revision_id: create it as of this revision id.
352
raise NotImplementedError(self.create_workingtree)
354
def destroy_workingtree(self):
355
"""Destroy the working tree at this BzrDir.
357
Formats that do not support this may raise UnsupportedOperation.
359
raise NotImplementedError(self.destroy_workingtree)
361
def destroy_workingtree_metadata(self):
362
"""Destroy the control files for the working tree at this BzrDir.
364
The contents of working tree files are not affected.
365
Formats that do not support this may raise UnsupportedOperation.
367
raise NotImplementedError(self.destroy_workingtree_metadata)
369
def find_repository(self):
370
"""Find the repository that should be used for a_bzrdir.
372
This does not require a branch as we use it to find the repo for
373
new branches as well as to hook existing branches up to their
377
return self.open_repository()
378
except errors.NoRepositoryPresent:
380
next_transport = self.root_transport.clone('..')
382
# find the next containing bzrdir
384
found_bzrdir = BzrDir.open_containing_from_transport(
386
except errors.NotBranchError:
388
raise errors.NoRepositoryPresent(self)
389
# does it have a repository ?
391
repository = found_bzrdir.open_repository()
392
except errors.NoRepositoryPresent:
393
next_transport = found_bzrdir.root_transport.clone('..')
394
if (found_bzrdir.root_transport.base == next_transport.base):
395
# top of the file system
399
if ((found_bzrdir.root_transport.base ==
400
self.root_transport.base) or repository.is_shared()):
403
raise errors.NoRepositoryPresent(self)
404
raise errors.NoRepositoryPresent(self)
406
def get_branch_reference(self):
407
"""Return the referenced URL for the branch in this bzrdir.
409
:raises NotBranchError: If there is no Branch.
410
:return: The URL the branch in this bzrdir references if it is a
411
reference branch, or None for regular branches.
415
def get_branch_transport(self, branch_format):
416
"""Get the transport for use by branch format in this BzrDir.
418
Note that bzr dirs that do not support format strings will raise
419
IncompatibleFormat if the branch format they are given has
420
a format string, and vice versa.
422
If branch_format is None, the transport is returned with no
423
checking. if it is not None, then the returned transport is
424
guaranteed to point to an existing directory ready for use.
426
raise NotImplementedError(self.get_branch_transport)
428
def get_repository_transport(self, repository_format):
429
"""Get the transport for use by repository format in this BzrDir.
431
Note that bzr dirs that do not support format strings will raise
432
IncompatibleFormat if the repository format they are given has
433
a format string, and vice versa.
435
If repository_format is None, the transport is returned with no
436
checking. if it is not None, then the returned transport is
437
guaranteed to point to an existing directory ready for use.
439
raise NotImplementedError(self.get_repository_transport)
441
def get_workingtree_transport(self, tree_format):
442
"""Get the transport for use by workingtree format in this BzrDir.
444
Note that bzr dirs that do not support format strings will raise
445
IncompatibleFormat if the workingtree format they are given has
446
a format string, and vice versa.
448
If workingtree_format is None, the transport is returned with no
449
checking. if it is not None, then the returned transport is
450
guaranteed to point to an existing directory ready for use.
452
raise NotImplementedError(self.get_workingtree_transport)
454
def __init__(self, _transport, _format):
455
"""Initialize a Bzr control dir object.
457
Only really common logic should reside here, concrete classes should be
458
made with varying behaviours.
460
:param _format: the format that is creating this BzrDir instance.
461
:param _transport: the transport this dir is based at.
463
self._format = _format
464
self.transport = _transport.clone('.bzr')
465
self.root_transport = _transport
467
def is_control_filename(self, filename):
468
"""True if filename is the name of a path which is reserved for bzrdir's.
470
:param filename: A filename within the root transport of this bzrdir.
472
This is true IF and ONLY IF the filename is part of the namespace reserved
473
for bzr control dirs. Currently this is the '.bzr' directory in the root
474
of the root_transport. it is expected that plugins will need to extend
475
this in the future - for instance to make bzr talk with svn working
478
# this might be better on the BzrDirFormat class because it refers to
479
# all the possible bzrdir disk formats.
480
# This method is tested via the workingtree is_control_filename tests-
481
# it was extracted from WorkingTree.is_control_filename. If the methods
482
# contract is extended beyond the current trivial implementation please
483
# add new tests for it to the appropriate place.
484
return filename == '.bzr' or filename.startswith('.bzr/')
486
def needs_format_conversion(self, format=None):
487
"""Return true if this bzrdir needs convert_format run on it.
489
For instance, if the repository format is out of date but the
490
branch and working tree are not, this should return True.
492
:param format: Optional parameter indicating a specific desired
493
format we plan to arrive at.
495
raise NotImplementedError(self.needs_format_conversion)
498
def open_unsupported(base):
499
"""Open a branch which is not supported."""
500
return BzrDir.open(base, _unsupported=True)
503
def open(base, _unsupported=False):
504
"""Open an existing bzrdir, rooted at 'base' (url)
506
_unsupported is a private parameter to the BzrDir class.
508
t = get_transport(base)
509
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
512
def open_from_transport(transport, _unsupported=False):
513
"""Open a bzrdir within a particular directory.
515
:param transport: Transport containing the bzrdir.
516
:param _unsupported: private.
518
format = BzrDirFormat.find_format(transport)
519
BzrDir._check_supported(format, _unsupported)
520
return format.open(transport, _found=True)
522
def open_branch(self, unsupported=False):
523
"""Open the branch object at this BzrDir if one is present.
525
If unsupported is True, then no longer supported branch formats can
528
TODO: static convenience version of this?
530
raise NotImplementedError(self.open_branch)
533
def open_containing(url):
534
"""Open an existing branch which contains url.
536
:param url: url to search from.
537
See open_containing_from_transport for more detail.
539
return BzrDir.open_containing_from_transport(get_transport(url))
542
def open_containing_from_transport(a_transport):
543
"""Open an existing branch which contains a_transport.base
545
This probes for a branch at a_transport, and searches upwards from there.
547
Basically we keep looking up until we find the control directory or
548
run into the root. If there isn't one, raises NotBranchError.
549
If there is one and it is either an unrecognised format or an unsupported
550
format, UnknownFormatError or UnsupportedFormatError are raised.
551
If there is one, it is returned, along with the unused portion of url.
553
:return: The BzrDir that contains the path, and a Unicode path
554
for the rest of the URL.
556
# this gets the normalised url back. I.e. '.' -> the full path.
557
url = a_transport.base
560
result = BzrDir.open_from_transport(a_transport)
561
return result, urlutils.unescape(a_transport.relpath(url))
562
except errors.NotBranchError, e:
565
new_t = a_transport.clone('..')
566
except errors.InvalidURLJoin:
567
# reached the root, whatever that may be
568
raise errors.NotBranchError(path=url)
569
if new_t.base == a_transport.base:
570
# reached the root, whatever that may be
571
raise errors.NotBranchError(path=url)
575
def open_containing_tree_or_branch(klass, location):
576
"""Return the branch and working tree contained by a location.
578
Returns (tree, branch, relpath).
579
If there is no tree at containing the location, tree will be None.
580
If there is no branch containing the location, an exception will be
582
relpath is the portion of the path that is contained by the branch.
584
bzrdir, relpath = klass.open_containing(location)
586
tree = bzrdir.open_workingtree()
587
except (errors.NoWorkingTree, errors.NotLocalUrl):
589
branch = bzrdir.open_branch()
592
return tree, branch, relpath
594
def open_repository(self, _unsupported=False):
595
"""Open the repository object at this BzrDir if one is present.
597
This will not follow the Branch object pointer - its strictly a direct
598
open facility. Most client code should use open_branch().repository to
601
_unsupported is a private parameter, not part of the api.
602
TODO: static convenience version of this?
604
raise NotImplementedError(self.open_repository)
606
def open_workingtree(self, _unsupported=False):
607
"""Open the workingtree object at this BzrDir if one is present.
609
TODO: static convenience version of this?
611
raise NotImplementedError(self.open_workingtree)
613
def has_branch(self):
614
"""Tell if this bzrdir contains a branch.
616
Note: if you're going to open the branch, you should just go ahead
617
and try, and not ask permission first. (This method just opens the
618
branch and discards it, and that's somewhat expensive.)
623
except errors.NotBranchError:
626
def has_workingtree(self):
627
"""Tell if this bzrdir contains a working tree.
629
This will still raise an exception if the bzrdir has a workingtree that
630
is remote & inaccessible.
632
Note: if you're going to open the working tree, you should just go ahead
633
and try, and not ask permission first. (This method just opens the
634
workingtree and discards it, and that's somewhat expensive.)
637
self.open_workingtree()
639
except errors.NoWorkingTree:
642
def cloning_metadir(self):
643
"""Produce a metadir suitable for cloning with"""
644
def related_repository(bzrdir):
646
branch = bzrdir.open_branch()
647
return branch.repository
648
except errors.NotBranchError:
650
return bzrdir.open_repository()
651
result_format = self._format.__class__()
653
source_repository = related_repository(self)
654
except errors.NoRepositoryPresent:
657
repo_format = source_repository._format
658
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
659
result_format.repository_format = repo_format
662
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
663
"""Create a copy of this bzrdir prepared for use as a new line of
666
If urls last component does not exist, it will be created.
668
Attributes related to the identity of the source branch like
669
branch nickname will be cleaned, a working tree is created
670
whether one existed before or not; and a local branch is always
673
if revision_id is not None, then the clone operation may tune
674
itself to download less data.
677
cloning_format = self.cloning_metadir()
678
result = cloning_format.initialize(url)
679
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
681
source_branch = self.open_branch()
682
source_repository = source_branch.repository
683
except errors.NotBranchError:
686
source_repository = self.open_repository()
687
except errors.NoRepositoryPresent:
688
# copy the entire basis one if there is one
689
# but there is no repository.
690
source_repository = basis_repo
695
result_repo = result.find_repository()
696
except errors.NoRepositoryPresent:
698
if source_repository is None and result_repo is not None:
700
elif source_repository is None and result_repo is None:
701
# no repo available, make a new one
702
result.create_repository()
703
elif source_repository is not None and result_repo is None:
704
# have source, and want to make a new target repo
705
# we don't clone the repo because that preserves attributes
706
# like is_shared(), and we have not yet implemented a
707
# repository sprout().
708
result_repo = result.create_repository()
709
if result_repo is not None:
710
# fetch needed content into target.
712
# XXX FIXME RBC 20060214 need tests for this when the basis
714
result_repo.fetch(basis_repo, revision_id=revision_id)
715
if source_repository is not None:
716
result_repo.fetch(source_repository, revision_id=revision_id)
717
if source_branch is not None:
718
source_branch.sprout(result, revision_id=revision_id)
720
result.create_branch()
721
# TODO: jam 20060426 we probably need a test in here in the
722
# case that the newly sprouted branch is a remote one
723
if result_repo is None or result_repo.make_working_trees():
724
wt = result.create_workingtree()
725
if wt.inventory.root is None:
727
wt.set_root_id(self.open_workingtree.get_root_id())
728
except errors.NoWorkingTree:
733
class BzrDirPreSplitOut(BzrDir):
734
"""A common class for the all-in-one formats."""
736
def __init__(self, _transport, _format):
737
"""See BzrDir.__init__."""
738
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
739
assert self._format._lock_class == lockable_files.TransportLock
740
assert self._format._lock_file_name == 'branch-lock'
741
self._control_files = lockable_files.LockableFiles(
742
self.get_branch_transport(None),
743
self._format._lock_file_name,
744
self._format._lock_class)
746
def break_lock(self):
747
"""Pre-splitout bzrdirs do not suffer from stale locks."""
748
raise NotImplementedError(self.break_lock)
750
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
751
"""See BzrDir.clone()."""
752
from bzrlib.workingtree import WorkingTreeFormat2
754
result = self._format._initialize_for_clone(url)
755
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
756
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
757
from_branch = self.open_branch()
758
from_branch.clone(result, revision_id=revision_id)
760
self.open_workingtree().clone(result, basis=basis_tree)
761
except errors.NotLocalUrl:
762
# make a new one, this format always has to have one.
764
WorkingTreeFormat2().initialize(result)
765
except errors.NotLocalUrl:
766
# but we cannot do it for remote trees.
767
to_branch = result.open_branch()
768
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
771
def create_branch(self):
772
"""See BzrDir.create_branch."""
773
return self.open_branch()
775
def create_repository(self, shared=False):
776
"""See BzrDir.create_repository."""
778
raise errors.IncompatibleFormat('shared repository', self._format)
779
return self.open_repository()
781
def create_workingtree(self, revision_id=None):
782
"""See BzrDir.create_workingtree."""
783
# this looks buggy but is not -really-
784
# clone and sprout will have set the revision_id
785
# and that will have set it for us, its only
786
# specific uses of create_workingtree in isolation
787
# that can do wonky stuff here, and that only
788
# happens for creating checkouts, which cannot be
789
# done on this format anyway. So - acceptable wart.
790
result = self.open_workingtree()
791
if revision_id is not None:
792
if revision_id == _mod_revision.NULL_REVISION:
793
result.set_parent_ids([])
795
result.set_parent_ids([revision_id])
798
def destroy_workingtree(self):
799
"""See BzrDir.destroy_workingtree."""
800
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
802
def destroy_workingtree_metadata(self):
803
"""See BzrDir.destroy_workingtree_metadata."""
804
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
807
def get_branch_transport(self, branch_format):
808
"""See BzrDir.get_branch_transport()."""
809
if branch_format is None:
810
return self.transport
812
branch_format.get_format_string()
813
except NotImplementedError:
814
return self.transport
815
raise errors.IncompatibleFormat(branch_format, self._format)
817
def get_repository_transport(self, repository_format):
818
"""See BzrDir.get_repository_transport()."""
819
if repository_format is None:
820
return self.transport
822
repository_format.get_format_string()
823
except NotImplementedError:
824
return self.transport
825
raise errors.IncompatibleFormat(repository_format, self._format)
827
def get_workingtree_transport(self, workingtree_format):
828
"""See BzrDir.get_workingtree_transport()."""
829
if workingtree_format is None:
830
return self.transport
832
workingtree_format.get_format_string()
833
except NotImplementedError:
834
return self.transport
835
raise errors.IncompatibleFormat(workingtree_format, self._format)
837
def needs_format_conversion(self, format=None):
838
"""See BzrDir.needs_format_conversion()."""
839
# if the format is not the same as the system default,
840
# an upgrade is needed.
842
format = BzrDirFormat.get_default_format()
843
return not isinstance(self._format, format.__class__)
845
def open_branch(self, unsupported=False):
846
"""See BzrDir.open_branch."""
847
from bzrlib.branch import BzrBranchFormat4
848
format = BzrBranchFormat4()
849
self._check_supported(format, unsupported)
850
return format.open(self, _found=True)
852
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
853
"""See BzrDir.sprout()."""
854
from bzrlib.workingtree import WorkingTreeFormat2
856
result = self._format._initialize_for_clone(url)
857
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
859
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
860
except errors.NoRepositoryPresent:
863
self.open_branch().sprout(result, revision_id=revision_id)
864
except errors.NotBranchError:
866
# we always want a working tree
867
WorkingTreeFormat2().initialize(result)
871
class BzrDir4(BzrDirPreSplitOut):
872
"""A .bzr version 4 control object.
874
This is a deprecated format and may be removed after sept 2006.
877
def create_repository(self, shared=False):
878
"""See BzrDir.create_repository."""
879
return self._format.repository_format.initialize(self, shared)
881
def needs_format_conversion(self, format=None):
882
"""Format 4 dirs are always in need of conversion."""
885
def open_repository(self):
886
"""See BzrDir.open_repository."""
887
from bzrlib.repofmt.weaverepo import RepositoryFormat4
888
return RepositoryFormat4().open(self, _found=True)
891
class BzrDir5(BzrDirPreSplitOut):
892
"""A .bzr version 5 control object.
894
This is a deprecated format and may be removed after sept 2006.
897
def open_repository(self):
898
"""See BzrDir.open_repository."""
899
from bzrlib.repofmt.weaverepo import RepositoryFormat5
900
return RepositoryFormat5().open(self, _found=True)
902
def open_workingtree(self, _unsupported=False):
903
"""See BzrDir.create_workingtree."""
904
from bzrlib.workingtree import WorkingTreeFormat2
905
return WorkingTreeFormat2().open(self, _found=True)
908
class BzrDir6(BzrDirPreSplitOut):
909
"""A .bzr version 6 control object.
911
This is a deprecated format and may be removed after sept 2006.
914
def open_repository(self):
915
"""See BzrDir.open_repository."""
916
from bzrlib.repofmt.weaverepo import RepositoryFormat6
917
return RepositoryFormat6().open(self, _found=True)
919
def open_workingtree(self, _unsupported=False):
920
"""See BzrDir.create_workingtree."""
921
from bzrlib.workingtree import WorkingTreeFormat2
922
return WorkingTreeFormat2().open(self, _found=True)
925
class BzrDirMeta1(BzrDir):
926
"""A .bzr meta version 1 control object.
928
This is the first control object where the
929
individual aspects are really split out: there are separate repository,
930
workingtree and branch subdirectories and any subset of the three can be
931
present within a BzrDir.
934
def can_convert_format(self):
935
"""See BzrDir.can_convert_format()."""
938
def create_branch(self):
939
"""See BzrDir.create_branch."""
940
return self._format.get_branch_format().initialize(self)
942
def create_repository(self, shared=False):
943
"""See BzrDir.create_repository."""
944
return self._format.repository_format.initialize(self, shared)
946
def create_workingtree(self, revision_id=None):
947
"""See BzrDir.create_workingtree."""
948
from bzrlib.workingtree import WorkingTreeFormat
949
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
951
def destroy_workingtree(self):
952
"""See BzrDir.destroy_workingtree."""
953
wt = self.open_workingtree()
954
repository = wt.branch.repository
955
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
956
wt.revert([], old_tree=empty)
957
self.destroy_workingtree_metadata()
959
def destroy_workingtree_metadata(self):
960
self.transport.delete_tree('checkout')
962
def _get_mkdir_mode(self):
963
"""Figure out the mode to use when creating a bzrdir subdir."""
964
temp_control = lockable_files.LockableFiles(self.transport, '',
965
lockable_files.TransportLock)
966
return temp_control._dir_mode
968
def get_branch_reference(self):
969
"""See BzrDir.get_branch_reference()."""
970
from bzrlib.branch import BranchFormat
971
format = BranchFormat.find_format(self)
972
return format.get_reference(self)
974
def get_branch_transport(self, branch_format):
975
"""See BzrDir.get_branch_transport()."""
976
if branch_format is None:
977
return self.transport.clone('branch')
979
branch_format.get_format_string()
980
except NotImplementedError:
981
raise errors.IncompatibleFormat(branch_format, self._format)
983
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
984
except errors.FileExists:
986
return self.transport.clone('branch')
988
def get_repository_transport(self, repository_format):
989
"""See BzrDir.get_repository_transport()."""
990
if repository_format is None:
991
return self.transport.clone('repository')
993
repository_format.get_format_string()
994
except NotImplementedError:
995
raise errors.IncompatibleFormat(repository_format, self._format)
997
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
998
except errors.FileExists:
1000
return self.transport.clone('repository')
1002
def get_workingtree_transport(self, workingtree_format):
1003
"""See BzrDir.get_workingtree_transport()."""
1004
if workingtree_format is None:
1005
return self.transport.clone('checkout')
1007
workingtree_format.get_format_string()
1008
except NotImplementedError:
1009
raise errors.IncompatibleFormat(workingtree_format, self._format)
1011
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1012
except errors.FileExists:
1014
return self.transport.clone('checkout')
1016
def needs_format_conversion(self, format=None):
1017
"""See BzrDir.needs_format_conversion()."""
1019
format = BzrDirFormat.get_default_format()
1020
if not isinstance(self._format, format.__class__):
1021
# it is not a meta dir format, conversion is needed.
1023
# we might want to push this down to the repository?
1025
if not isinstance(self.open_repository()._format,
1026
format.repository_format.__class__):
1027
# the repository needs an upgrade.
1029
except errors.NoRepositoryPresent:
1032
if not isinstance(self.open_branch()._format,
1033
format.get_branch_format().__class__):
1034
# the repository needs an upgrade.
1036
except errors.NotBranchError:
1038
# currently there are no other possible conversions for meta1 formats.
1041
def open_branch(self, unsupported=False):
1042
"""See BzrDir.open_branch."""
1043
from bzrlib.branch import BranchFormat
1044
format = BranchFormat.find_format(self)
1045
self._check_supported(format, unsupported)
1046
return format.open(self, _found=True)
1048
def open_repository(self, unsupported=False):
1049
"""See BzrDir.open_repository."""
1050
from bzrlib.repository import RepositoryFormat
1051
format = RepositoryFormat.find_format(self)
1052
self._check_supported(format, unsupported)
1053
return format.open(self, _found=True)
1055
def open_workingtree(self, unsupported=False):
1056
"""See BzrDir.open_workingtree."""
1057
from bzrlib.workingtree import WorkingTreeFormat
1058
format = WorkingTreeFormat.find_format(self)
1059
self._check_supported(format, unsupported)
1060
return format.open(self, _found=True)
1063
class BzrDirFormat(object):
1064
"""An encapsulation of the initialization and open routines for a format.
1066
Formats provide three things:
1067
* An initialization routine,
1071
Formats are placed in an dict by their format string for reference
1072
during bzrdir opening. These should be subclasses of BzrDirFormat
1075
Once a format is deprecated, just deprecate the initialize and open
1076
methods on the format class. Do not deprecate the object, as the
1077
object will be created every system load.
1080
_default_format = None
1081
"""The default format used for new .bzr dirs."""
1084
"""The known formats."""
1086
_control_formats = []
1087
"""The registered control formats - .bzr, ....
1089
This is a list of BzrDirFormat objects.
1092
_lock_file_name = 'branch-lock'
1094
# _lock_class must be set in subclasses to the lock type, typ.
1095
# TransportLock or LockDir
1098
def find_format(klass, transport):
1099
"""Return the format present at transport."""
1100
for format in klass._control_formats:
1102
return format.probe_transport(transport)
1103
except errors.NotBranchError:
1104
# this format does not find a control dir here.
1106
raise errors.NotBranchError(path=transport.base)
1109
def probe_transport(klass, transport):
1110
"""Return the .bzrdir style format present in a directory."""
1112
format_string = transport.get(".bzr/branch-format").read()
1113
except errors.NoSuchFile:
1114
raise errors.NotBranchError(path=transport.base)
1117
return klass._formats[format_string]
1119
raise errors.UnknownFormatError(format=format_string)
1122
def get_default_format(klass):
1123
"""Return the current default format."""
1124
return klass._default_format
1126
def get_format_string(self):
1127
"""Return the ASCII format string that identifies this format."""
1128
raise NotImplementedError(self.get_format_string)
1130
def get_format_description(self):
1131
"""Return the short description for this format."""
1132
raise NotImplementedError(self.get_format_description)
1134
def get_converter(self, format=None):
1135
"""Return the converter to use to convert bzrdirs needing converts.
1137
This returns a bzrlib.bzrdir.Converter object.
1139
This should return the best upgrader to step this format towards the
1140
current default format. In the case of plugins we can/should provide
1141
some means for them to extend the range of returnable converters.
1143
:param format: Optional format to override the default format of the
1146
raise NotImplementedError(self.get_converter)
1148
def initialize(self, url):
1149
"""Create a bzr control dir at this url and return an opened copy.
1151
Subclasses should typically override initialize_on_transport
1152
instead of this method.
1154
return self.initialize_on_transport(get_transport(url))
1156
def initialize_on_transport(self, transport):
1157
"""Initialize a new bzrdir in the base directory of a Transport."""
1158
# Since we don't have a .bzr directory, inherit the
1159
# mode from the root directory
1160
temp_control = lockable_files.LockableFiles(transport,
1161
'', lockable_files.TransportLock)
1162
temp_control._transport.mkdir('.bzr',
1163
# FIXME: RBC 20060121 don't peek under
1165
mode=temp_control._dir_mode)
1166
file_mode = temp_control._file_mode
1168
mutter('created control directory in ' + transport.base)
1169
control = transport.clone('.bzr')
1170
utf8_files = [('README',
1171
"This is a Bazaar-NG control directory.\n"
1172
"Do not change any files in this directory.\n"),
1173
('branch-format', self.get_format_string()),
1175
# NB: no need to escape relative paths that are url safe.
1176
control_files = lockable_files.LockableFiles(control,
1177
self._lock_file_name, self._lock_class)
1178
control_files.create_lock()
1179
control_files.lock_write()
1181
for file, content in utf8_files:
1182
control_files.put_utf8(file, content)
1184
control_files.unlock()
1185
return self.open(transport, _found=True)
1187
def is_supported(self):
1188
"""Is this format supported?
1190
Supported formats must be initializable and openable.
1191
Unsupported formats may not support initialization or committing or
1192
some other features depending on the reason for not being supported.
1196
def same_model(self, target_format):
1197
return (self.repository_format.rich_root_data ==
1198
target_format.rich_root_data)
1201
def known_formats(klass):
1202
"""Return all the known formats.
1204
Concrete formats should override _known_formats.
1206
# There is double indirection here to make sure that control
1207
# formats used by more than one dir format will only be probed
1208
# once. This can otherwise be quite expensive for remote connections.
1210
for format in klass._control_formats:
1211
result.update(format._known_formats())
1215
def _known_formats(klass):
1216
"""Return the known format instances for this control format."""
1217
return set(klass._formats.values())
1219
def open(self, transport, _found=False):
1220
"""Return an instance of this format for the dir transport points at.
1222
_found is a private parameter, do not use it.
1225
found_format = BzrDirFormat.find_format(transport)
1226
if not isinstance(found_format, self.__class__):
1227
raise AssertionError("%s was asked to open %s, but it seems to need "
1229
% (self, transport, found_format))
1230
return self._open(transport)
1232
def _open(self, transport):
1233
"""Template method helper for opening BzrDirectories.
1235
This performs the actual open and any additional logic or parameter
1238
raise NotImplementedError(self._open)
1241
def register_format(klass, format):
1242
klass._formats[format.get_format_string()] = format
1245
def register_control_format(klass, format):
1246
"""Register a format that does not use '.bzrdir' for its control dir.
1248
TODO: This should be pulled up into a 'ControlDirFormat' base class
1249
which BzrDirFormat can inherit from, and renamed to register_format
1250
there. It has been done without that for now for simplicity of
1253
klass._control_formats.append(format)
1256
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1257
def set_default_format(klass, format):
1258
klass._set_default_format(format)
1261
def _set_default_format(klass, format):
1262
"""Set default format (for testing behavior of defaults only)"""
1263
klass._default_format = format
1266
return self.get_format_string()[:-1]
1269
def unregister_format(klass, format):
1270
assert klass._formats[format.get_format_string()] is format
1271
del klass._formats[format.get_format_string()]
1274
def unregister_control_format(klass, format):
1275
klass._control_formats.remove(format)
1278
# register BzrDirFormat as a control format
1279
BzrDirFormat.register_control_format(BzrDirFormat)
1282
class BzrDirFormat4(BzrDirFormat):
1283
"""Bzr dir format 4.
1285
This format is a combined format for working tree, branch and repository.
1287
- Format 1 working trees [always]
1288
- Format 4 branches [always]
1289
- Format 4 repositories [always]
1291
This format is deprecated: it indexes texts using a text it which is
1292
removed in format 5; write support for this format has been removed.
1295
_lock_class = lockable_files.TransportLock
1297
def get_format_string(self):
1298
"""See BzrDirFormat.get_format_string()."""
1299
return "Bazaar-NG branch, format 0.0.4\n"
1301
def get_format_description(self):
1302
"""See BzrDirFormat.get_format_description()."""
1303
return "All-in-one format 4"
1305
def get_converter(self, format=None):
1306
"""See BzrDirFormat.get_converter()."""
1307
# there is one and only one upgrade path here.
1308
return ConvertBzrDir4To5()
1310
def initialize_on_transport(self, transport):
1311
"""Format 4 branches cannot be created."""
1312
raise errors.UninitializableFormat(self)
1314
def is_supported(self):
1315
"""Format 4 is not supported.
1317
It is not supported because the model changed from 4 to 5 and the
1318
conversion logic is expensive - so doing it on the fly was not
1323
def _open(self, transport):
1324
"""See BzrDirFormat._open."""
1325
return BzrDir4(transport, self)
1327
def __return_repository_format(self):
1328
"""Circular import protection."""
1329
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1330
return RepositoryFormat4()
1331
repository_format = property(__return_repository_format)
1334
class BzrDirFormat5(BzrDirFormat):
1335
"""Bzr control format 5.
1337
This format is a combined format for working tree, branch and repository.
1339
- Format 2 working trees [always]
1340
- Format 4 branches [always]
1341
- Format 5 repositories [always]
1342
Unhashed stores in the repository.
1345
_lock_class = lockable_files.TransportLock
1347
def get_format_string(self):
1348
"""See BzrDirFormat.get_format_string()."""
1349
return "Bazaar-NG branch, format 5\n"
1351
def get_format_description(self):
1352
"""See BzrDirFormat.get_format_description()."""
1353
return "All-in-one format 5"
1355
def get_converter(self, format=None):
1356
"""See BzrDirFormat.get_converter()."""
1357
# there is one and only one upgrade path here.
1358
return ConvertBzrDir5To6()
1360
def _initialize_for_clone(self, url):
1361
return self.initialize_on_transport(get_transport(url), _cloning=True)
1363
def initialize_on_transport(self, transport, _cloning=False):
1364
"""Format 5 dirs always have working tree, branch and repository.
1366
Except when they are being cloned.
1368
from bzrlib.branch import BzrBranchFormat4
1369
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1370
from bzrlib.workingtree import WorkingTreeFormat2
1371
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1372
RepositoryFormat5().initialize(result, _internal=True)
1374
branch = BzrBranchFormat4().initialize(result)
1376
WorkingTreeFormat2().initialize(result)
1377
except errors.NotLocalUrl:
1378
# Even though we can't access the working tree, we need to
1379
# create its control files.
1380
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1383
def _open(self, transport):
1384
"""See BzrDirFormat._open."""
1385
return BzrDir5(transport, self)
1387
def __return_repository_format(self):
1388
"""Circular import protection."""
1389
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1390
return RepositoryFormat5()
1391
repository_format = property(__return_repository_format)
1394
class BzrDirFormat6(BzrDirFormat):
1395
"""Bzr control format 6.
1397
This format is a combined format for working tree, branch and repository.
1399
- Format 2 working trees [always]
1400
- Format 4 branches [always]
1401
- Format 6 repositories [always]
1404
_lock_class = lockable_files.TransportLock
1406
def get_format_string(self):
1407
"""See BzrDirFormat.get_format_string()."""
1408
return "Bazaar-NG branch, format 6\n"
1410
def get_format_description(self):
1411
"""See BzrDirFormat.get_format_description()."""
1412
return "All-in-one format 6"
1414
def get_converter(self, format=None):
1415
"""See BzrDirFormat.get_converter()."""
1416
# there is one and only one upgrade path here.
1417
return ConvertBzrDir6ToMeta()
1419
def _initialize_for_clone(self, url):
1420
return self.initialize_on_transport(get_transport(url), _cloning=True)
1422
def initialize_on_transport(self, transport, _cloning=False):
1423
"""Format 6 dirs always have working tree, branch and repository.
1425
Except when they are being cloned.
1427
from bzrlib.branch import BzrBranchFormat4
1428
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1429
from bzrlib.workingtree import WorkingTreeFormat2
1430
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1431
RepositoryFormat6().initialize(result, _internal=True)
1433
branch = BzrBranchFormat4().initialize(result)
1435
WorkingTreeFormat2().initialize(result)
1436
except errors.NotLocalUrl:
1437
# Even though we can't access the working tree, we need to
1438
# create its control files.
1439
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1442
def _open(self, transport):
1443
"""See BzrDirFormat._open."""
1444
return BzrDir6(transport, self)
1446
def __return_repository_format(self):
1447
"""Circular import protection."""
1448
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1449
return RepositoryFormat6()
1450
repository_format = property(__return_repository_format)
1453
class BzrDirMetaFormat1(BzrDirFormat):
1454
"""Bzr meta control format 1
1456
This is the first format with split out working tree, branch and repository
1459
- Format 3 working trees [optional]
1460
- Format 5 branches [optional]
1461
- Format 7 repositories [optional]
1464
_lock_class = lockdir.LockDir
1467
self._branch_format = None
1469
def get_branch_format(self):
1470
if self._branch_format is None:
1471
from bzrlib.branch import BranchFormat
1472
self._branch_format = BranchFormat.get_default_format()
1473
return self._branch_format
1475
def set_branch_format(self, format):
1476
self._branch_format = format
1478
def get_converter(self, format=None):
1479
"""See BzrDirFormat.get_converter()."""
1481
format = BzrDirFormat.get_default_format()
1482
if not isinstance(self, format.__class__):
1483
# converting away from metadir is not implemented
1484
raise NotImplementedError(self.get_converter)
1485
return ConvertMetaToMeta(format)
1487
def get_format_string(self):
1488
"""See BzrDirFormat.get_format_string()."""
1489
return "Bazaar-NG meta directory, format 1\n"
1491
def get_format_description(self):
1492
"""See BzrDirFormat.get_format_description()."""
1493
return "Meta directory format 1"
1495
def _open(self, transport):
1496
"""See BzrDirFormat._open."""
1497
return BzrDirMeta1(transport, self)
1499
def __return_repository_format(self):
1500
"""Circular import protection."""
1501
if getattr(self, '_repository_format', None):
1502
return self._repository_format
1503
from bzrlib.repository import RepositoryFormat
1504
return RepositoryFormat.get_default_format()
1506
def __set_repository_format(self, value):
1507
"""Allow changint the repository format for metadir formats."""
1508
self._repository_format = value
1510
repository_format = property(__return_repository_format, __set_repository_format)
1513
BzrDirFormat.register_format(BzrDirFormat4())
1514
BzrDirFormat.register_format(BzrDirFormat5())
1515
BzrDirFormat.register_format(BzrDirFormat6())
1516
__default_format = BzrDirMetaFormat1()
1517
BzrDirFormat.register_format(__default_format)
1518
BzrDirFormat._default_format = __default_format
1521
class BzrDirTestProviderAdapter(object):
1522
"""A tool to generate a suite testing multiple bzrdir formats at once.
1524
This is done by copying the test once for each transport and injecting
1525
the transport_server, transport_readonly_server, and bzrdir_format
1526
classes into each copy. Each copy is also given a new id() to make it
1530
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1532
"""Create an object to adapt tests.
1534
:param vfs_server: A factory to create a Transport Server which has
1535
all the VFS methods working, and is writable.
1537
self._vfs_factory = vfs_factory
1538
self._transport_server = transport_server
1539
self._transport_readonly_server = transport_readonly_server
1540
self._formats = formats
1542
def adapt(self, test):
1543
result = unittest.TestSuite()
1544
for format in self._formats:
1545
new_test = deepcopy(test)
1546
new_test.vfs_transport_factory = self._vfs_factory
1547
new_test.transport_server = self._transport_server
1548
new_test.transport_readonly_server = self._transport_readonly_server
1549
new_test.bzrdir_format = format
1550
def make_new_test_id():
1551
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1552
return lambda: new_id
1553
new_test.id = make_new_test_id()
1554
result.addTest(new_test)
1558
class Converter(object):
1559
"""Converts a disk format object from one format to another."""
1561
def convert(self, to_convert, pb):
1562
"""Perform the conversion of to_convert, giving feedback via pb.
1564
:param to_convert: The disk object to convert.
1565
:param pb: a progress bar to use for progress information.
1568
def step(self, message):
1569
"""Update the pb by a step."""
1571
self.pb.update(message, self.count, self.total)
1574
class ConvertBzrDir4To5(Converter):
1575
"""Converts format 4 bzr dirs to format 5."""
1578
super(ConvertBzrDir4To5, self).__init__()
1579
self.converted_revs = set()
1580
self.absent_revisions = set()
1584
def convert(self, to_convert, pb):
1585
"""See Converter.convert()."""
1586
self.bzrdir = to_convert
1588
self.pb.note('starting upgrade from format 4 to 5')
1589
if isinstance(self.bzrdir.transport, LocalTransport):
1590
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1591
self._convert_to_weaves()
1592
return BzrDir.open(self.bzrdir.root_transport.base)
1594
def _convert_to_weaves(self):
1595
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1598
stat = self.bzrdir.transport.stat('weaves')
1599
if not S_ISDIR(stat.st_mode):
1600
self.bzrdir.transport.delete('weaves')
1601
self.bzrdir.transport.mkdir('weaves')
1602
except errors.NoSuchFile:
1603
self.bzrdir.transport.mkdir('weaves')
1604
# deliberately not a WeaveFile as we want to build it up slowly.
1605
self.inv_weave = Weave('inventory')
1606
# holds in-memory weaves for all files
1607
self.text_weaves = {}
1608
self.bzrdir.transport.delete('branch-format')
1609
self.branch = self.bzrdir.open_branch()
1610
self._convert_working_inv()
1611
rev_history = self.branch.revision_history()
1612
# to_read is a stack holding the revisions we still need to process;
1613
# appending to it adds new highest-priority revisions
1614
self.known_revisions = set(rev_history)
1615
self.to_read = rev_history[-1:]
1617
rev_id = self.to_read.pop()
1618
if (rev_id not in self.revisions
1619
and rev_id not in self.absent_revisions):
1620
self._load_one_rev(rev_id)
1622
to_import = self._make_order()
1623
for i, rev_id in enumerate(to_import):
1624
self.pb.update('converting revision', i, len(to_import))
1625
self._convert_one_rev(rev_id)
1627
self._write_all_weaves()
1628
self._write_all_revs()
1629
self.pb.note('upgraded to weaves:')
1630
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1631
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1632
self.pb.note(' %6d texts', self.text_count)
1633
self._cleanup_spare_files_after_format4()
1634
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1636
def _cleanup_spare_files_after_format4(self):
1637
# FIXME working tree upgrade foo.
1638
for n in 'merged-patches', 'pending-merged-patches':
1640
## assert os.path.getsize(p) == 0
1641
self.bzrdir.transport.delete(n)
1642
except errors.NoSuchFile:
1644
self.bzrdir.transport.delete_tree('inventory-store')
1645
self.bzrdir.transport.delete_tree('text-store')
1647
def _convert_working_inv(self):
1648
inv = xml4.serializer_v4.read_inventory(
1649
self.branch.control_files.get('inventory'))
1650
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1651
# FIXME inventory is a working tree change.
1652
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1654
def _write_all_weaves(self):
1655
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1656
weave_transport = self.bzrdir.transport.clone('weaves')
1657
weaves = WeaveStore(weave_transport, prefixed=False)
1658
transaction = WriteTransaction()
1662
for file_id, file_weave in self.text_weaves.items():
1663
self.pb.update('writing weave', i, len(self.text_weaves))
1664
weaves._put_weave(file_id, file_weave, transaction)
1666
self.pb.update('inventory', 0, 1)
1667
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1668
self.pb.update('inventory', 1, 1)
1672
def _write_all_revs(self):
1673
"""Write all revisions out in new form."""
1674
self.bzrdir.transport.delete_tree('revision-store')
1675
self.bzrdir.transport.mkdir('revision-store')
1676
revision_transport = self.bzrdir.transport.clone('revision-store')
1678
_revision_store = TextRevisionStore(TextStore(revision_transport,
1682
transaction = WriteTransaction()
1683
for i, rev_id in enumerate(self.converted_revs):
1684
self.pb.update('write revision', i, len(self.converted_revs))
1685
_revision_store.add_revision(self.revisions[rev_id], transaction)
1689
def _load_one_rev(self, rev_id):
1690
"""Load a revision object into memory.
1692
Any parents not either loaded or abandoned get queued to be
1694
self.pb.update('loading revision',
1695
len(self.revisions),
1696
len(self.known_revisions))
1697
if not self.branch.repository.has_revision(rev_id):
1699
self.pb.note('revision {%s} not present in branch; '
1700
'will be converted as a ghost',
1702
self.absent_revisions.add(rev_id)
1704
rev = self.branch.repository._revision_store.get_revision(rev_id,
1705
self.branch.repository.get_transaction())
1706
for parent_id in rev.parent_ids:
1707
self.known_revisions.add(parent_id)
1708
self.to_read.append(parent_id)
1709
self.revisions[rev_id] = rev
1711
def _load_old_inventory(self, rev_id):
1712
assert rev_id not in self.converted_revs
1713
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1714
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1715
inv.revision_id = rev_id
1716
rev = self.revisions[rev_id]
1717
if rev.inventory_sha1:
1718
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1719
'inventory sha mismatch for {%s}' % rev_id
1722
def _load_updated_inventory(self, rev_id):
1723
assert rev_id in self.converted_revs
1724
inv_xml = self.inv_weave.get_text(rev_id)
1725
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1728
def _convert_one_rev(self, rev_id):
1729
"""Convert revision and all referenced objects to new format."""
1730
rev = self.revisions[rev_id]
1731
inv = self._load_old_inventory(rev_id)
1732
present_parents = [p for p in rev.parent_ids
1733
if p not in self.absent_revisions]
1734
self._convert_revision_contents(rev, inv, present_parents)
1735
self._store_new_weave(rev, inv, present_parents)
1736
self.converted_revs.add(rev_id)
1738
def _store_new_weave(self, rev, inv, present_parents):
1739
# the XML is now updated with text versions
1741
entries = inv.iter_entries()
1743
for path, ie in entries:
1744
assert getattr(ie, 'revision', None) is not None, \
1745
'no revision on {%s} in {%s}' % \
1746
(file_id, rev.revision_id)
1747
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1748
new_inv_sha1 = sha_string(new_inv_xml)
1749
self.inv_weave.add_lines(rev.revision_id,
1751
new_inv_xml.splitlines(True))
1752
rev.inventory_sha1 = new_inv_sha1
1754
def _convert_revision_contents(self, rev, inv, present_parents):
1755
"""Convert all the files within a revision.
1757
Also upgrade the inventory to refer to the text revision ids."""
1758
rev_id = rev.revision_id
1759
mutter('converting texts of revision {%s}',
1761
parent_invs = map(self._load_updated_inventory, present_parents)
1762
entries = inv.iter_entries()
1764
for path, ie in entries:
1765
self._convert_file_version(rev, ie, parent_invs)
1767
def _convert_file_version(self, rev, ie, parent_invs):
1768
"""Convert one version of one file.
1770
The file needs to be added into the weave if it is a merge
1771
of >=2 parents or if it's changed from its parent.
1773
file_id = ie.file_id
1774
rev_id = rev.revision_id
1775
w = self.text_weaves.get(file_id)
1778
self.text_weaves[file_id] = w
1779
text_changed = False
1780
previous_entries = ie.find_previous_heads(parent_invs,
1784
for old_revision in previous_entries:
1785
# if this fails, its a ghost ?
1786
assert old_revision in self.converted_revs, \
1787
"Revision {%s} not in converted_revs" % old_revision
1788
self.snapshot_ie(previous_entries, ie, w, rev_id)
1790
assert getattr(ie, 'revision', None) is not None
1792
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1793
# TODO: convert this logic, which is ~= snapshot to
1794
# a call to:. This needs the path figured out. rather than a work_tree
1795
# a v4 revision_tree can be given, or something that looks enough like
1796
# one to give the file content to the entry if it needs it.
1797
# and we need something that looks like a weave store for snapshot to
1799
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1800
if len(previous_revisions) == 1:
1801
previous_ie = previous_revisions.values()[0]
1802
if ie._unchanged(previous_ie):
1803
ie.revision = previous_ie.revision
1806
text = self.branch.repository.text_store.get(ie.text_id)
1807
file_lines = text.readlines()
1808
assert sha_strings(file_lines) == ie.text_sha1
1809
assert sum(map(len, file_lines)) == ie.text_size
1810
w.add_lines(rev_id, previous_revisions, file_lines)
1811
self.text_count += 1
1813
w.add_lines(rev_id, previous_revisions, [])
1814
ie.revision = rev_id
1816
def _make_order(self):
1817
"""Return a suitable order for importing revisions.
1819
The order must be such that an revision is imported after all
1820
its (present) parents.
1822
todo = set(self.revisions.keys())
1823
done = self.absent_revisions.copy()
1826
# scan through looking for a revision whose parents
1828
for rev_id in sorted(list(todo)):
1829
rev = self.revisions[rev_id]
1830
parent_ids = set(rev.parent_ids)
1831
if parent_ids.issubset(done):
1832
# can take this one now
1833
order.append(rev_id)
1839
class ConvertBzrDir5To6(Converter):
1840
"""Converts format 5 bzr dirs to format 6."""
1842
def convert(self, to_convert, pb):
1843
"""See Converter.convert()."""
1844
self.bzrdir = to_convert
1846
self.pb.note('starting upgrade from format 5 to 6')
1847
self._convert_to_prefixed()
1848
return BzrDir.open(self.bzrdir.root_transport.base)
1850
def _convert_to_prefixed(self):
1851
from bzrlib.store import TransportStore
1852
self.bzrdir.transport.delete('branch-format')
1853
for store_name in ["weaves", "revision-store"]:
1854
self.pb.note("adding prefixes to %s" % store_name)
1855
store_transport = self.bzrdir.transport.clone(store_name)
1856
store = TransportStore(store_transport, prefixed=True)
1857
for urlfilename in store_transport.list_dir('.'):
1858
filename = urlutils.unescape(urlfilename)
1859
if (filename.endswith(".weave") or
1860
filename.endswith(".gz") or
1861
filename.endswith(".sig")):
1862
file_id = os.path.splitext(filename)[0]
1865
prefix_dir = store.hash_prefix(file_id)
1866
# FIXME keep track of the dirs made RBC 20060121
1868
store_transport.move(filename, prefix_dir + '/' + filename)
1869
except errors.NoSuchFile: # catches missing dirs strangely enough
1870
store_transport.mkdir(prefix_dir)
1871
store_transport.move(filename, prefix_dir + '/' + filename)
1872
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1875
class ConvertBzrDir6ToMeta(Converter):
1876
"""Converts format 6 bzr dirs to metadirs."""
1878
def convert(self, to_convert, pb):
1879
"""See Converter.convert()."""
1880
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1881
from bzrlib.branch import BzrBranchFormat5
1882
self.bzrdir = to_convert
1885
self.total = 20 # the steps we know about
1886
self.garbage_inventories = []
1888
self.pb.note('starting upgrade from format 6 to metadir')
1889
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1890
# its faster to move specific files around than to open and use the apis...
1891
# first off, nuke ancestry.weave, it was never used.
1893
self.step('Removing ancestry.weave')
1894
self.bzrdir.transport.delete('ancestry.weave')
1895
except errors.NoSuchFile:
1897
# find out whats there
1898
self.step('Finding branch files')
1899
last_revision = self.bzrdir.open_branch().last_revision()
1900
bzrcontents = self.bzrdir.transport.list_dir('.')
1901
for name in bzrcontents:
1902
if name.startswith('basis-inventory.'):
1903
self.garbage_inventories.append(name)
1904
# create new directories for repository, working tree and branch
1905
self.dir_mode = self.bzrdir._control_files._dir_mode
1906
self.file_mode = self.bzrdir._control_files._file_mode
1907
repository_names = [('inventory.weave', True),
1908
('revision-store', True),
1910
self.step('Upgrading repository ')
1911
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1912
self.make_lock('repository')
1913
# we hard code the formats here because we are converting into
1914
# the meta format. The meta format upgrader can take this to a
1915
# future format within each component.
1916
self.put_format('repository', RepositoryFormat7())
1917
for entry in repository_names:
1918
self.move_entry('repository', entry)
1920
self.step('Upgrading branch ')
1921
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1922
self.make_lock('branch')
1923
self.put_format('branch', BzrBranchFormat5())
1924
branch_files = [('revision-history', True),
1925
('branch-name', True),
1927
for entry in branch_files:
1928
self.move_entry('branch', entry)
1930
checkout_files = [('pending-merges', True),
1931
('inventory', True),
1932
('stat-cache', False)]
1933
# If a mandatory checkout file is not present, the branch does not have
1934
# a functional checkout. Do not create a checkout in the converted
1936
for name, mandatory in checkout_files:
1937
if mandatory and name not in bzrcontents:
1938
has_checkout = False
1942
if not has_checkout:
1943
self.pb.note('No working tree.')
1944
# If some checkout files are there, we may as well get rid of them.
1945
for name, mandatory in checkout_files:
1946
if name in bzrcontents:
1947
self.bzrdir.transport.delete(name)
1949
from bzrlib.workingtree import WorkingTreeFormat3
1950
self.step('Upgrading working tree')
1951
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1952
self.make_lock('checkout')
1954
'checkout', WorkingTreeFormat3())
1955
self.bzrdir.transport.delete_multi(
1956
self.garbage_inventories, self.pb)
1957
for entry in checkout_files:
1958
self.move_entry('checkout', entry)
1959
if last_revision is not None:
1960
self.bzrdir._control_files.put_utf8(
1961
'checkout/last-revision', last_revision)
1962
self.bzrdir._control_files.put_utf8(
1963
'branch-format', BzrDirMetaFormat1().get_format_string())
1964
return BzrDir.open(self.bzrdir.root_transport.base)
1966
def make_lock(self, name):
1967
"""Make a lock for the new control dir name."""
1968
self.step('Make %s lock' % name)
1969
ld = lockdir.LockDir(self.bzrdir.transport,
1971
file_modebits=self.file_mode,
1972
dir_modebits=self.dir_mode)
1975
def move_entry(self, new_dir, entry):
1976
"""Move then entry name into new_dir."""
1978
mandatory = entry[1]
1979
self.step('Moving %s' % name)
1981
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1982
except errors.NoSuchFile:
1986
def put_format(self, dirname, format):
1987
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1990
class ConvertMetaToMeta(Converter):
1991
"""Converts the components of metadirs."""
1993
def __init__(self, target_format):
1994
"""Create a metadir to metadir converter.
1996
:param target_format: The final metadir format that is desired.
1998
self.target_format = target_format
2000
def convert(self, to_convert, pb):
2001
"""See Converter.convert()."""
2002
self.bzrdir = to_convert
2006
self.step('checking repository format')
2008
repo = self.bzrdir.open_repository()
2009
except errors.NoRepositoryPresent:
2012
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2013
from bzrlib.repository import CopyConverter
2014
self.pb.note('starting repository conversion')
2015
converter = CopyConverter(self.target_format.repository_format)
2016
converter.convert(repo, pb)
2018
branch = self.bzrdir.open_branch()
2019
except errors.NotBranchError:
2022
# Avoid circular imports
2023
from bzrlib import branch as _mod_branch
2024
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2025
self.target_format.get_branch_format().__class__ is
2026
_mod_branch.BzrBranchFormat6):
2027
branch_converter = _mod_branch.Converter5to6()
2028
branch_converter.convert(branch)
2032
# This is not in remote.py because it's small, and needs to be registered.
2033
# Putting it in remote.py creates a circular import problem.
2034
# we can make it a lazy object if the control formats is turned into something
2036
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2037
"""Format representing bzrdirs accessed via a smart server"""
2039
def get_format_description(self):
2040
return 'bzr remote bzrdir'
2043
def probe_transport(klass, transport):
2044
"""Return a RemoteBzrDirFormat object if it looks possible."""
2046
transport.get_smart_client()
2047
except (NotImplementedError, AttributeError,
2048
errors.TransportNotPossible):
2049
# no smart server, so not a branch for this format type.
2050
raise errors.NotBranchError(path=transport.base)
2054
def initialize_on_transport(self, transport):
2055
# hand off the request to the smart server
2056
medium = transport.get_smart_medium()
2057
client = SmartClient(medium)
2058
path = client.remote_path_from_transport(transport)
2059
response = SmartClient(medium).call('BzrDirFormat.initialize', path)
2060
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2061
return remote.RemoteBzrDir(transport)
2063
def _open(self, transport):
2064
return remote.RemoteBzrDir(transport)
2066
def __eq__(self, other):
2067
if not isinstance(other, RemoteBzrDirFormat):
2069
return self.get_format_description() == other.get_format_description()
2072
# We can't use register_control_format because it adds it at a lower priority
2073
# than the existing branches, whereas this should take priority.
2074
BzrDirFormat._control_formats.insert(0, RemoteBzrDirFormat)
2077
class BzrDirFormatInfo(object):
2079
def __init__(self, native, deprecated):
2080
self.deprecated = deprecated
2081
self.native = native
2084
class BzrDirFormatRegistry(registry.Registry):
2085
"""Registry of user-selectable BzrDir subformats.
2087
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2088
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2091
def register_metadir(self, key, repo, help, native=True, deprecated=False,
2092
branch_format=None):
2093
"""Register a metadir subformat.
2095
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2096
by the Repository format.
2098
:param repo: The fully-qualified repository format class name as a
2101
# This should be expanded to support setting WorkingTree and Branch
2102
# formats, once BzrDirMetaFormat1 supports that.
2104
import bzrlib.branch
2105
mod_name, repo_factory_name = repo.rsplit('.', 1)
2107
mod = __import__(mod_name, globals(), locals(),
2108
[repo_factory_name])
2109
except ImportError, e:
2110
raise ImportError('failed to load repository %s: %s'
2113
repo_format_class = getattr(mod, repo_factory_name)
2114
except AttributeError:
2115
raise AttributeError('no repository format %r in module %r'
2117
bd = BzrDirMetaFormat1()
2118
bd.repository_format = repo_format_class()
2119
if branch_format is not None:
2120
bd.set_branch_format(getattr(bzrlib.branch, branch_format)())
2122
self.register(key, helper, help, native, deprecated)
2124
def register(self, key, factory, help, native=True, deprecated=False):
2125
"""Register a BzrDirFormat factory.
2127
The factory must be a callable that takes one parameter: the key.
2128
It must produce an instance of the BzrDirFormat when called.
2130
This function mainly exists to prevent the info object from being
2133
registry.Registry.register(self, key, factory, help,
2134
BzrDirFormatInfo(native, deprecated))
2136
def register_lazy(self, key, module_name, member_name, help, native=True,
2138
registry.Registry.register_lazy(self, key, module_name, member_name,
2139
help, BzrDirFormatInfo(native, deprecated))
2141
def set_default(self, key):
2142
"""Set the 'default' key to be a clone of the supplied key.
2144
This method must be called once and only once.
2146
registry.Registry.register(self, 'default', self.get(key),
2147
self.get_help(key), info=self.get_info(key))
2149
def set_default_repository(self, key):
2150
"""Set the FormatRegistry default and Repository default.
2152
This is a transitional method while Repository.set_default_format
2155
if 'default' in self:
2156
self.remove('default')
2157
self.set_default(key)
2158
format = self.get('default')()
2159
assert isinstance(format, BzrDirMetaFormat1)
2161
def make_bzrdir(self, key):
2162
return self.get(key)()
2164
def help_topic(self, topic):
2165
output = textwrap.dedent("""\
2166
Bazaar directory formats
2167
------------------------
2169
These formats can be used for creating branches, working trees, and
2173
default_help = self.get_help('default')
2175
for key in self.keys():
2176
if key == 'default':
2178
help = self.get_help(key)
2179
if help == default_help:
2180
default_realkey = key
2182
help_pairs.append((key, help))
2184
def wrapped(key, help, info):
2186
help = '(native) ' + help
2187
return ' %s:\n%s\n\n' % (key,
2188
textwrap.fill(help, initial_indent=' ',
2189
subsequent_indent=' '))
2190
output += wrapped('%s/default' % default_realkey, default_help,
2191
self.get_info('default'))
2192
deprecated_pairs = []
2193
for key, help in help_pairs:
2194
info = self.get_info(key)
2196
deprecated_pairs.append((key, help))
2198
output += wrapped(key, help, info)
2199
if len(deprecated_pairs) > 0:
2200
output += "Deprecated formats\n------------------\n\n"
2201
for key, help in deprecated_pairs:
2202
info = self.get_info(key)
2203
output += wrapped(key, help, info)
2208
format_registry = BzrDirFormatRegistry()
2209
format_registry.register('weave', BzrDirFormat6,
2210
'Pre-0.8 format. Slower than knit and does not'
2211
' support checkouts or shared repositories.',
2213
format_registry.register_metadir('knit',
2214
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2215
'Format using knits. Recommended.',
2216
branch_format='BzrBranchFormat5')
2217
format_registry.set_default('knit')
2218
format_registry.register_metadir('metaweave',
2219
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2220
'Transitional format in 0.8. Slower than knit.',
2223
format_registry.register_metadir('experimental-knit2',
2224
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
2225
'Experimental successor to knit. Use at your own risk.',
2226
branch_format='BzrBranchFormat5')
2227
format_registry.register_metadir('experimental-branch6',
2228
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2229
'Experimental successor to knit. Use at your own risk.',
2230
branch_format='BzrBranchFormat6')