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
result_format.repository_format = source_repository._format
660
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
661
"""Create a copy of this bzrdir prepared for use as a new line of
664
If urls last component does not exist, it will be created.
666
Attributes related to the identity of the source branch like
667
branch nickname will be cleaned, a working tree is created
668
whether one existed before or not; and a local branch is always
671
if revision_id is not None, then the clone operation may tune
672
itself to download less data.
675
cloning_format = self.cloning_metadir()
676
result = cloning_format.initialize(url)
677
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
679
source_branch = self.open_branch()
680
source_repository = source_branch.repository
681
except errors.NotBranchError:
684
source_repository = self.open_repository()
685
except errors.NoRepositoryPresent:
686
# copy the entire basis one if there is one
687
# but there is no repository.
688
source_repository = basis_repo
693
result_repo = result.find_repository()
694
except errors.NoRepositoryPresent:
696
if source_repository is None and result_repo is not None:
698
elif source_repository is None and result_repo is None:
699
# no repo available, make a new one
700
result.create_repository()
701
elif source_repository is not None and result_repo is None:
702
# have source, and want to make a new target repo
703
# we don't clone the repo because that preserves attributes
704
# like is_shared(), and we have not yet implemented a
705
# repository sprout().
706
result_repo = result.create_repository()
707
if result_repo is not None:
708
# fetch needed content into target.
710
# XXX FIXME RBC 20060214 need tests for this when the basis
712
result_repo.fetch(basis_repo, revision_id=revision_id)
713
if source_repository is not None:
714
result_repo.fetch(source_repository, revision_id=revision_id)
715
if source_branch is not None:
716
source_branch.sprout(result, revision_id=revision_id)
718
result.create_branch()
719
# TODO: jam 20060426 we probably need a test in here in the
720
# case that the newly sprouted branch is a remote one
721
if result_repo is None or result_repo.make_working_trees():
722
wt = result.create_workingtree()
723
if wt.inventory.root is None:
725
wt.set_root_id(self.open_workingtree.get_root_id())
726
except errors.NoWorkingTree:
731
class BzrDirPreSplitOut(BzrDir):
732
"""A common class for the all-in-one formats."""
734
def __init__(self, _transport, _format):
735
"""See BzrDir.__init__."""
736
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
737
assert self._format._lock_class == lockable_files.TransportLock
738
assert self._format._lock_file_name == 'branch-lock'
739
self._control_files = lockable_files.LockableFiles(
740
self.get_branch_transport(None),
741
self._format._lock_file_name,
742
self._format._lock_class)
744
def break_lock(self):
745
"""Pre-splitout bzrdirs do not suffer from stale locks."""
746
raise NotImplementedError(self.break_lock)
748
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
749
"""See BzrDir.clone()."""
750
from bzrlib.workingtree import WorkingTreeFormat2
752
result = self._format._initialize_for_clone(url)
753
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
754
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
755
from_branch = self.open_branch()
756
from_branch.clone(result, revision_id=revision_id)
758
self.open_workingtree().clone(result, basis=basis_tree)
759
except errors.NotLocalUrl:
760
# make a new one, this format always has to have one.
762
WorkingTreeFormat2().initialize(result)
763
except errors.NotLocalUrl:
764
# but we cannot do it for remote trees.
765
to_branch = result.open_branch()
766
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
769
def create_branch(self):
770
"""See BzrDir.create_branch."""
771
return self.open_branch()
773
def create_repository(self, shared=False):
774
"""See BzrDir.create_repository."""
776
raise errors.IncompatibleFormat('shared repository', self._format)
777
return self.open_repository()
779
def create_workingtree(self, revision_id=None):
780
"""See BzrDir.create_workingtree."""
781
# this looks buggy but is not -really-
782
# clone and sprout will have set the revision_id
783
# and that will have set it for us, its only
784
# specific uses of create_workingtree in isolation
785
# that can do wonky stuff here, and that only
786
# happens for creating checkouts, which cannot be
787
# done on this format anyway. So - acceptable wart.
788
result = self.open_workingtree()
789
if revision_id is not None:
790
if revision_id == _mod_revision.NULL_REVISION:
791
result.set_parent_ids([])
793
result.set_parent_ids([revision_id])
796
def destroy_workingtree(self):
797
"""See BzrDir.destroy_workingtree."""
798
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
800
def destroy_workingtree_metadata(self):
801
"""See BzrDir.destroy_workingtree_metadata."""
802
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
805
def get_branch_transport(self, branch_format):
806
"""See BzrDir.get_branch_transport()."""
807
if branch_format is None:
808
return self.transport
810
branch_format.get_format_string()
811
except NotImplementedError:
812
return self.transport
813
raise errors.IncompatibleFormat(branch_format, self._format)
815
def get_repository_transport(self, repository_format):
816
"""See BzrDir.get_repository_transport()."""
817
if repository_format is None:
818
return self.transport
820
repository_format.get_format_string()
821
except NotImplementedError:
822
return self.transport
823
raise errors.IncompatibleFormat(repository_format, self._format)
825
def get_workingtree_transport(self, workingtree_format):
826
"""See BzrDir.get_workingtree_transport()."""
827
if workingtree_format is None:
828
return self.transport
830
workingtree_format.get_format_string()
831
except NotImplementedError:
832
return self.transport
833
raise errors.IncompatibleFormat(workingtree_format, self._format)
835
def needs_format_conversion(self, format=None):
836
"""See BzrDir.needs_format_conversion()."""
837
# if the format is not the same as the system default,
838
# an upgrade is needed.
840
format = BzrDirFormat.get_default_format()
841
return not isinstance(self._format, format.__class__)
843
def open_branch(self, unsupported=False):
844
"""See BzrDir.open_branch."""
845
from bzrlib.branch import BzrBranchFormat4
846
format = BzrBranchFormat4()
847
self._check_supported(format, unsupported)
848
return format.open(self, _found=True)
850
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
851
"""See BzrDir.sprout()."""
852
from bzrlib.workingtree import WorkingTreeFormat2
854
result = self._format._initialize_for_clone(url)
855
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
857
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
858
except errors.NoRepositoryPresent:
861
self.open_branch().sprout(result, revision_id=revision_id)
862
except errors.NotBranchError:
864
# we always want a working tree
865
WorkingTreeFormat2().initialize(result)
869
class BzrDir4(BzrDirPreSplitOut):
870
"""A .bzr version 4 control object.
872
This is a deprecated format and may be removed after sept 2006.
875
def create_repository(self, shared=False):
876
"""See BzrDir.create_repository."""
877
return self._format.repository_format.initialize(self, shared)
879
def needs_format_conversion(self, format=None):
880
"""Format 4 dirs are always in need of conversion."""
883
def open_repository(self):
884
"""See BzrDir.open_repository."""
885
from bzrlib.repofmt.weaverepo import RepositoryFormat4
886
return RepositoryFormat4().open(self, _found=True)
889
class BzrDir5(BzrDirPreSplitOut):
890
"""A .bzr version 5 control object.
892
This is a deprecated format and may be removed after sept 2006.
895
def open_repository(self):
896
"""See BzrDir.open_repository."""
897
from bzrlib.repofmt.weaverepo import RepositoryFormat5
898
return RepositoryFormat5().open(self, _found=True)
900
def open_workingtree(self, _unsupported=False):
901
"""See BzrDir.create_workingtree."""
902
from bzrlib.workingtree import WorkingTreeFormat2
903
return WorkingTreeFormat2().open(self, _found=True)
906
class BzrDir6(BzrDirPreSplitOut):
907
"""A .bzr version 6 control object.
909
This is a deprecated format and may be removed after sept 2006.
912
def open_repository(self):
913
"""See BzrDir.open_repository."""
914
from bzrlib.repofmt.weaverepo import RepositoryFormat6
915
return RepositoryFormat6().open(self, _found=True)
917
def open_workingtree(self, _unsupported=False):
918
"""See BzrDir.create_workingtree."""
919
from bzrlib.workingtree import WorkingTreeFormat2
920
return WorkingTreeFormat2().open(self, _found=True)
923
class BzrDirMeta1(BzrDir):
924
"""A .bzr meta version 1 control object.
926
This is the first control object where the
927
individual aspects are really split out: there are separate repository,
928
workingtree and branch subdirectories and any subset of the three can be
929
present within a BzrDir.
932
def can_convert_format(self):
933
"""See BzrDir.can_convert_format()."""
936
def create_branch(self):
937
"""See BzrDir.create_branch."""
938
return self._format.get_branch_format().initialize(self)
940
def create_repository(self, shared=False):
941
"""See BzrDir.create_repository."""
942
return self._format.repository_format.initialize(self, shared)
944
def create_workingtree(self, revision_id=None):
945
"""See BzrDir.create_workingtree."""
946
from bzrlib.workingtree import WorkingTreeFormat
947
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
949
def destroy_workingtree(self):
950
"""See BzrDir.destroy_workingtree."""
951
wt = self.open_workingtree()
952
repository = wt.branch.repository
953
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
954
wt.revert([], old_tree=empty)
955
self.destroy_workingtree_metadata()
957
def destroy_workingtree_metadata(self):
958
self.transport.delete_tree('checkout')
960
def _get_mkdir_mode(self):
961
"""Figure out the mode to use when creating a bzrdir subdir."""
962
temp_control = lockable_files.LockableFiles(self.transport, '',
963
lockable_files.TransportLock)
964
return temp_control._dir_mode
966
def get_branch_reference(self):
967
"""See BzrDir.get_branch_reference()."""
968
from bzrlib.branch import BranchFormat
969
format = BranchFormat.find_format(self)
970
return format.get_reference(self)
972
def get_branch_transport(self, branch_format):
973
"""See BzrDir.get_branch_transport()."""
974
if branch_format is None:
975
return self.transport.clone('branch')
977
branch_format.get_format_string()
978
except NotImplementedError:
979
raise errors.IncompatibleFormat(branch_format, self._format)
981
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
982
except errors.FileExists:
984
return self.transport.clone('branch')
986
def get_repository_transport(self, repository_format):
987
"""See BzrDir.get_repository_transport()."""
988
if repository_format is None:
989
return self.transport.clone('repository')
991
repository_format.get_format_string()
992
except NotImplementedError:
993
raise errors.IncompatibleFormat(repository_format, self._format)
995
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
996
except errors.FileExists:
998
return self.transport.clone('repository')
1000
def get_workingtree_transport(self, workingtree_format):
1001
"""See BzrDir.get_workingtree_transport()."""
1002
if workingtree_format is None:
1003
return self.transport.clone('checkout')
1005
workingtree_format.get_format_string()
1006
except NotImplementedError:
1007
raise errors.IncompatibleFormat(workingtree_format, self._format)
1009
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1010
except errors.FileExists:
1012
return self.transport.clone('checkout')
1014
def needs_format_conversion(self, format=None):
1015
"""See BzrDir.needs_format_conversion()."""
1017
format = BzrDirFormat.get_default_format()
1018
if not isinstance(self._format, format.__class__):
1019
# it is not a meta dir format, conversion is needed.
1021
# we might want to push this down to the repository?
1023
if not isinstance(self.open_repository()._format,
1024
format.repository_format.__class__):
1025
# the repository needs an upgrade.
1027
except errors.NoRepositoryPresent:
1030
if not isinstance(self.open_branch()._format,
1031
format.get_branch_format().__class__):
1032
# the repository needs an upgrade.
1034
except errors.NotBranchError:
1036
# currently there are no other possible conversions for meta1 formats.
1039
def open_branch(self, unsupported=False):
1040
"""See BzrDir.open_branch."""
1041
from bzrlib.branch import BranchFormat
1042
format = BranchFormat.find_format(self)
1043
self._check_supported(format, unsupported)
1044
return format.open(self, _found=True)
1046
def open_repository(self, unsupported=False):
1047
"""See BzrDir.open_repository."""
1048
from bzrlib.repository import RepositoryFormat
1049
format = RepositoryFormat.find_format(self)
1050
self._check_supported(format, unsupported)
1051
return format.open(self, _found=True)
1053
def open_workingtree(self, unsupported=False):
1054
"""See BzrDir.open_workingtree."""
1055
from bzrlib.workingtree import WorkingTreeFormat
1056
format = WorkingTreeFormat.find_format(self)
1057
self._check_supported(format, unsupported)
1058
return format.open(self, _found=True)
1061
class BzrDirFormat(object):
1062
"""An encapsulation of the initialization and open routines for a format.
1064
Formats provide three things:
1065
* An initialization routine,
1069
Formats are placed in an dict by their format string for reference
1070
during bzrdir opening. These should be subclasses of BzrDirFormat
1073
Once a format is deprecated, just deprecate the initialize and open
1074
methods on the format class. Do not deprecate the object, as the
1075
object will be created every system load.
1078
_default_format = None
1079
"""The default format used for new .bzr dirs."""
1082
"""The known formats."""
1084
_control_formats = []
1085
"""The registered control formats - .bzr, ....
1087
This is a list of BzrDirFormat objects.
1090
_lock_file_name = 'branch-lock'
1092
# _lock_class must be set in subclasses to the lock type, typ.
1093
# TransportLock or LockDir
1096
def find_format(klass, transport):
1097
"""Return the format present at transport."""
1098
for format in klass._control_formats:
1100
return format.probe_transport(transport)
1101
except errors.NotBranchError:
1102
# this format does not find a control dir here.
1104
raise errors.NotBranchError(path=transport.base)
1107
def probe_transport(klass, transport):
1108
"""Return the .bzrdir style format present in a directory."""
1110
format_string = transport.get(".bzr/branch-format").read()
1111
except errors.NoSuchFile:
1112
raise errors.NotBranchError(path=transport.base)
1115
return klass._formats[format_string]
1117
raise errors.UnknownFormatError(format=format_string)
1120
def get_default_format(klass):
1121
"""Return the current default format."""
1122
return klass._default_format
1124
def get_format_string(self):
1125
"""Return the ASCII format string that identifies this format."""
1126
raise NotImplementedError(self.get_format_string)
1128
def get_format_description(self):
1129
"""Return the short description for this format."""
1130
raise NotImplementedError(self.get_format_description)
1132
def get_converter(self, format=None):
1133
"""Return the converter to use to convert bzrdirs needing converts.
1135
This returns a bzrlib.bzrdir.Converter object.
1137
This should return the best upgrader to step this format towards the
1138
current default format. In the case of plugins we can/should provide
1139
some means for them to extend the range of returnable converters.
1141
:param format: Optional format to override the default format of the
1144
raise NotImplementedError(self.get_converter)
1146
def initialize(self, url):
1147
"""Create a bzr control dir at this url and return an opened copy.
1149
Subclasses should typically override initialize_on_transport
1150
instead of this method.
1152
return self.initialize_on_transport(get_transport(url))
1154
def initialize_on_transport(self, transport):
1155
"""Initialize a new bzrdir in the base directory of a Transport."""
1156
# Since we don't have a .bzr directory, inherit the
1157
# mode from the root directory
1158
temp_control = lockable_files.LockableFiles(transport,
1159
'', lockable_files.TransportLock)
1160
temp_control._transport.mkdir('.bzr',
1161
# FIXME: RBC 20060121 don't peek under
1163
mode=temp_control._dir_mode)
1164
file_mode = temp_control._file_mode
1166
mutter('created control directory in ' + transport.base)
1167
control = transport.clone('.bzr')
1168
utf8_files = [('README',
1169
"This is a Bazaar-NG control directory.\n"
1170
"Do not change any files in this directory.\n"),
1171
('branch-format', self.get_format_string()),
1173
# NB: no need to escape relative paths that are url safe.
1174
control_files = lockable_files.LockableFiles(control,
1175
self._lock_file_name, self._lock_class)
1176
control_files.create_lock()
1177
control_files.lock_write()
1179
for file, content in utf8_files:
1180
control_files.put_utf8(file, content)
1182
control_files.unlock()
1183
return self.open(transport, _found=True)
1185
def is_supported(self):
1186
"""Is this format supported?
1188
Supported formats must be initializable and openable.
1189
Unsupported formats may not support initialization or committing or
1190
some other features depending on the reason for not being supported.
1194
def same_model(self, target_format):
1195
return (self.repository_format.rich_root_data ==
1196
target_format.rich_root_data)
1199
def known_formats(klass):
1200
"""Return all the known formats.
1202
Concrete formats should override _known_formats.
1204
# There is double indirection here to make sure that control
1205
# formats used by more than one dir format will only be probed
1206
# once. This can otherwise be quite expensive for remote connections.
1208
for format in klass._control_formats:
1209
result.update(format._known_formats())
1213
def _known_formats(klass):
1214
"""Return the known format instances for this control format."""
1215
return set(klass._formats.values())
1217
def open(self, transport, _found=False):
1218
"""Return an instance of this format for the dir transport points at.
1220
_found is a private parameter, do not use it.
1223
found_format = BzrDirFormat.find_format(transport)
1224
if not isinstance(found_format, self.__class__):
1225
raise AssertionError("%s was asked to open %s, but it seems to need "
1227
% (self, transport, found_format))
1228
return self._open(transport)
1230
def _open(self, transport):
1231
"""Template method helper for opening BzrDirectories.
1233
This performs the actual open and any additional logic or parameter
1236
raise NotImplementedError(self._open)
1239
def register_format(klass, format):
1240
klass._formats[format.get_format_string()] = format
1243
def register_control_format(klass, format):
1244
"""Register a format that does not use '.bzrdir' for its control dir.
1246
TODO: This should be pulled up into a 'ControlDirFormat' base class
1247
which BzrDirFormat can inherit from, and renamed to register_format
1248
there. It has been done without that for now for simplicity of
1251
klass._control_formats.append(format)
1254
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1255
def set_default_format(klass, format):
1256
klass._set_default_format(format)
1259
def _set_default_format(klass, format):
1260
"""Set default format (for testing behavior of defaults only)"""
1261
klass._default_format = format
1264
return self.get_format_string()[:-1]
1267
def unregister_format(klass, format):
1268
assert klass._formats[format.get_format_string()] is format
1269
del klass._formats[format.get_format_string()]
1272
def unregister_control_format(klass, format):
1273
klass._control_formats.remove(format)
1276
# register BzrDirFormat as a control format
1277
BzrDirFormat.register_control_format(BzrDirFormat)
1280
class BzrDirFormat4(BzrDirFormat):
1281
"""Bzr dir format 4.
1283
This format is a combined format for working tree, branch and repository.
1285
- Format 1 working trees [always]
1286
- Format 4 branches [always]
1287
- Format 4 repositories [always]
1289
This format is deprecated: it indexes texts using a text it which is
1290
removed in format 5; write support for this format has been removed.
1293
_lock_class = lockable_files.TransportLock
1295
def get_format_string(self):
1296
"""See BzrDirFormat.get_format_string()."""
1297
return "Bazaar-NG branch, format 0.0.4\n"
1299
def get_format_description(self):
1300
"""See BzrDirFormat.get_format_description()."""
1301
return "All-in-one format 4"
1303
def get_converter(self, format=None):
1304
"""See BzrDirFormat.get_converter()."""
1305
# there is one and only one upgrade path here.
1306
return ConvertBzrDir4To5()
1308
def initialize_on_transport(self, transport):
1309
"""Format 4 branches cannot be created."""
1310
raise errors.UninitializableFormat(self)
1312
def is_supported(self):
1313
"""Format 4 is not supported.
1315
It is not supported because the model changed from 4 to 5 and the
1316
conversion logic is expensive - so doing it on the fly was not
1321
def _open(self, transport):
1322
"""See BzrDirFormat._open."""
1323
return BzrDir4(transport, self)
1325
def __return_repository_format(self):
1326
"""Circular import protection."""
1327
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1328
return RepositoryFormat4()
1329
repository_format = property(__return_repository_format)
1332
class BzrDirFormat5(BzrDirFormat):
1333
"""Bzr control format 5.
1335
This format is a combined format for working tree, branch and repository.
1337
- Format 2 working trees [always]
1338
- Format 4 branches [always]
1339
- Format 5 repositories [always]
1340
Unhashed stores in the repository.
1343
_lock_class = lockable_files.TransportLock
1345
def get_format_string(self):
1346
"""See BzrDirFormat.get_format_string()."""
1347
return "Bazaar-NG branch, format 5\n"
1349
def get_format_description(self):
1350
"""See BzrDirFormat.get_format_description()."""
1351
return "All-in-one format 5"
1353
def get_converter(self, format=None):
1354
"""See BzrDirFormat.get_converter()."""
1355
# there is one and only one upgrade path here.
1356
return ConvertBzrDir5To6()
1358
def _initialize_for_clone(self, url):
1359
return self.initialize_on_transport(get_transport(url), _cloning=True)
1361
def initialize_on_transport(self, transport, _cloning=False):
1362
"""Format 5 dirs always have working tree, branch and repository.
1364
Except when they are being cloned.
1366
from bzrlib.branch import BzrBranchFormat4
1367
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1368
from bzrlib.workingtree import WorkingTreeFormat2
1369
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1370
RepositoryFormat5().initialize(result, _internal=True)
1372
branch = BzrBranchFormat4().initialize(result)
1374
WorkingTreeFormat2().initialize(result)
1375
except errors.NotLocalUrl:
1376
# Even though we can't access the working tree, we need to
1377
# create its control files.
1378
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1381
def _open(self, transport):
1382
"""See BzrDirFormat._open."""
1383
return BzrDir5(transport, self)
1385
def __return_repository_format(self):
1386
"""Circular import protection."""
1387
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1388
return RepositoryFormat5()
1389
repository_format = property(__return_repository_format)
1392
class BzrDirFormat6(BzrDirFormat):
1393
"""Bzr control format 6.
1395
This format is a combined format for working tree, branch and repository.
1397
- Format 2 working trees [always]
1398
- Format 4 branches [always]
1399
- Format 6 repositories [always]
1402
_lock_class = lockable_files.TransportLock
1404
def get_format_string(self):
1405
"""See BzrDirFormat.get_format_string()."""
1406
return "Bazaar-NG branch, format 6\n"
1408
def get_format_description(self):
1409
"""See BzrDirFormat.get_format_description()."""
1410
return "All-in-one format 6"
1412
def get_converter(self, format=None):
1413
"""See BzrDirFormat.get_converter()."""
1414
# there is one and only one upgrade path here.
1415
return ConvertBzrDir6ToMeta()
1417
def _initialize_for_clone(self, url):
1418
return self.initialize_on_transport(get_transport(url), _cloning=True)
1420
def initialize_on_transport(self, transport, _cloning=False):
1421
"""Format 6 dirs always have working tree, branch and repository.
1423
Except when they are being cloned.
1425
from bzrlib.branch import BzrBranchFormat4
1426
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1427
from bzrlib.workingtree import WorkingTreeFormat2
1428
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1429
RepositoryFormat6().initialize(result, _internal=True)
1431
branch = BzrBranchFormat4().initialize(result)
1433
WorkingTreeFormat2().initialize(result)
1434
except errors.NotLocalUrl:
1435
# Even though we can't access the working tree, we need to
1436
# create its control files.
1437
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1440
def _open(self, transport):
1441
"""See BzrDirFormat._open."""
1442
return BzrDir6(transport, self)
1444
def __return_repository_format(self):
1445
"""Circular import protection."""
1446
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1447
return RepositoryFormat6()
1448
repository_format = property(__return_repository_format)
1451
class BzrDirMetaFormat1(BzrDirFormat):
1452
"""Bzr meta control format 1
1454
This is the first format with split out working tree, branch and repository
1457
- Format 3 working trees [optional]
1458
- Format 5 branches [optional]
1459
- Format 7 repositories [optional]
1462
_lock_class = lockdir.LockDir
1465
self._branch_format = None
1467
def get_branch_format(self):
1468
if self._branch_format is None:
1469
from bzrlib.branch import BranchFormat
1470
self._branch_format = BranchFormat.get_default_format()
1471
return self._branch_format
1473
def set_branch_format(self, format):
1474
self._branch_format = format
1476
def get_converter(self, format=None):
1477
"""See BzrDirFormat.get_converter()."""
1479
format = BzrDirFormat.get_default_format()
1480
if not isinstance(self, format.__class__):
1481
# converting away from metadir is not implemented
1482
raise NotImplementedError(self.get_converter)
1483
return ConvertMetaToMeta(format)
1485
def get_format_string(self):
1486
"""See BzrDirFormat.get_format_string()."""
1487
return "Bazaar-NG meta directory, format 1\n"
1489
def get_format_description(self):
1490
"""See BzrDirFormat.get_format_description()."""
1491
return "Meta directory format 1"
1493
def _open(self, transport):
1494
"""See BzrDirFormat._open."""
1495
return BzrDirMeta1(transport, self)
1497
def __return_repository_format(self):
1498
"""Circular import protection."""
1499
if getattr(self, '_repository_format', None):
1500
return self._repository_format
1501
from bzrlib.repository import RepositoryFormat
1502
return RepositoryFormat.get_default_format()
1504
def __set_repository_format(self, value):
1505
"""Allow changint the repository format for metadir formats."""
1506
self._repository_format = value
1508
repository_format = property(__return_repository_format, __set_repository_format)
1511
BzrDirFormat.register_format(BzrDirFormat4())
1512
BzrDirFormat.register_format(BzrDirFormat5())
1513
BzrDirFormat.register_format(BzrDirFormat6())
1514
__default_format = BzrDirMetaFormat1()
1515
BzrDirFormat.register_format(__default_format)
1516
BzrDirFormat._default_format = __default_format
1519
class BzrDirTestProviderAdapter(object):
1520
"""A tool to generate a suite testing multiple bzrdir formats at once.
1522
This is done by copying the test once for each transport and injecting
1523
the transport_server, transport_readonly_server, and bzrdir_format
1524
classes into each copy. Each copy is also given a new id() to make it
1528
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1530
"""Create an object to adapt tests.
1532
:param vfs_server: A factory to create a Transport Server which has
1533
all the VFS methods working, and is writable.
1535
self._vfs_factory = vfs_factory
1536
self._transport_server = transport_server
1537
self._transport_readonly_server = transport_readonly_server
1538
self._formats = formats
1540
def adapt(self, test):
1541
result = unittest.TestSuite()
1542
for format in self._formats:
1543
new_test = deepcopy(test)
1544
new_test.vfs_transport_factory = self._vfs_factory
1545
new_test.transport_server = self._transport_server
1546
new_test.transport_readonly_server = self._transport_readonly_server
1547
new_test.bzrdir_format = format
1548
def make_new_test_id():
1549
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1550
return lambda: new_id
1551
new_test.id = make_new_test_id()
1552
result.addTest(new_test)
1556
class Converter(object):
1557
"""Converts a disk format object from one format to another."""
1559
def convert(self, to_convert, pb):
1560
"""Perform the conversion of to_convert, giving feedback via pb.
1562
:param to_convert: The disk object to convert.
1563
:param pb: a progress bar to use for progress information.
1566
def step(self, message):
1567
"""Update the pb by a step."""
1569
self.pb.update(message, self.count, self.total)
1572
class ConvertBzrDir4To5(Converter):
1573
"""Converts format 4 bzr dirs to format 5."""
1576
super(ConvertBzrDir4To5, self).__init__()
1577
self.converted_revs = set()
1578
self.absent_revisions = set()
1582
def convert(self, to_convert, pb):
1583
"""See Converter.convert()."""
1584
self.bzrdir = to_convert
1586
self.pb.note('starting upgrade from format 4 to 5')
1587
if isinstance(self.bzrdir.transport, LocalTransport):
1588
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1589
self._convert_to_weaves()
1590
return BzrDir.open(self.bzrdir.root_transport.base)
1592
def _convert_to_weaves(self):
1593
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1596
stat = self.bzrdir.transport.stat('weaves')
1597
if not S_ISDIR(stat.st_mode):
1598
self.bzrdir.transport.delete('weaves')
1599
self.bzrdir.transport.mkdir('weaves')
1600
except errors.NoSuchFile:
1601
self.bzrdir.transport.mkdir('weaves')
1602
# deliberately not a WeaveFile as we want to build it up slowly.
1603
self.inv_weave = Weave('inventory')
1604
# holds in-memory weaves for all files
1605
self.text_weaves = {}
1606
self.bzrdir.transport.delete('branch-format')
1607
self.branch = self.bzrdir.open_branch()
1608
self._convert_working_inv()
1609
rev_history = self.branch.revision_history()
1610
# to_read is a stack holding the revisions we still need to process;
1611
# appending to it adds new highest-priority revisions
1612
self.known_revisions = set(rev_history)
1613
self.to_read = rev_history[-1:]
1615
rev_id = self.to_read.pop()
1616
if (rev_id not in self.revisions
1617
and rev_id not in self.absent_revisions):
1618
self._load_one_rev(rev_id)
1620
to_import = self._make_order()
1621
for i, rev_id in enumerate(to_import):
1622
self.pb.update('converting revision', i, len(to_import))
1623
self._convert_one_rev(rev_id)
1625
self._write_all_weaves()
1626
self._write_all_revs()
1627
self.pb.note('upgraded to weaves:')
1628
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1629
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1630
self.pb.note(' %6d texts', self.text_count)
1631
self._cleanup_spare_files_after_format4()
1632
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1634
def _cleanup_spare_files_after_format4(self):
1635
# FIXME working tree upgrade foo.
1636
for n in 'merged-patches', 'pending-merged-patches':
1638
## assert os.path.getsize(p) == 0
1639
self.bzrdir.transport.delete(n)
1640
except errors.NoSuchFile:
1642
self.bzrdir.transport.delete_tree('inventory-store')
1643
self.bzrdir.transport.delete_tree('text-store')
1645
def _convert_working_inv(self):
1646
inv = xml4.serializer_v4.read_inventory(
1647
self.branch.control_files.get('inventory'))
1648
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1649
# FIXME inventory is a working tree change.
1650
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1652
def _write_all_weaves(self):
1653
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1654
weave_transport = self.bzrdir.transport.clone('weaves')
1655
weaves = WeaveStore(weave_transport, prefixed=False)
1656
transaction = WriteTransaction()
1660
for file_id, file_weave in self.text_weaves.items():
1661
self.pb.update('writing weave', i, len(self.text_weaves))
1662
weaves._put_weave(file_id, file_weave, transaction)
1664
self.pb.update('inventory', 0, 1)
1665
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1666
self.pb.update('inventory', 1, 1)
1670
def _write_all_revs(self):
1671
"""Write all revisions out in new form."""
1672
self.bzrdir.transport.delete_tree('revision-store')
1673
self.bzrdir.transport.mkdir('revision-store')
1674
revision_transport = self.bzrdir.transport.clone('revision-store')
1676
_revision_store = TextRevisionStore(TextStore(revision_transport,
1680
transaction = WriteTransaction()
1681
for i, rev_id in enumerate(self.converted_revs):
1682
self.pb.update('write revision', i, len(self.converted_revs))
1683
_revision_store.add_revision(self.revisions[rev_id], transaction)
1687
def _load_one_rev(self, rev_id):
1688
"""Load a revision object into memory.
1690
Any parents not either loaded or abandoned get queued to be
1692
self.pb.update('loading revision',
1693
len(self.revisions),
1694
len(self.known_revisions))
1695
if not self.branch.repository.has_revision(rev_id):
1697
self.pb.note('revision {%s} not present in branch; '
1698
'will be converted as a ghost',
1700
self.absent_revisions.add(rev_id)
1702
rev = self.branch.repository._revision_store.get_revision(rev_id,
1703
self.branch.repository.get_transaction())
1704
for parent_id in rev.parent_ids:
1705
self.known_revisions.add(parent_id)
1706
self.to_read.append(parent_id)
1707
self.revisions[rev_id] = rev
1709
def _load_old_inventory(self, rev_id):
1710
assert rev_id not in self.converted_revs
1711
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1712
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1713
inv.revision_id = rev_id
1714
rev = self.revisions[rev_id]
1715
if rev.inventory_sha1:
1716
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1717
'inventory sha mismatch for {%s}' % rev_id
1720
def _load_updated_inventory(self, rev_id):
1721
assert rev_id in self.converted_revs
1722
inv_xml = self.inv_weave.get_text(rev_id)
1723
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1726
def _convert_one_rev(self, rev_id):
1727
"""Convert revision and all referenced objects to new format."""
1728
rev = self.revisions[rev_id]
1729
inv = self._load_old_inventory(rev_id)
1730
present_parents = [p for p in rev.parent_ids
1731
if p not in self.absent_revisions]
1732
self._convert_revision_contents(rev, inv, present_parents)
1733
self._store_new_weave(rev, inv, present_parents)
1734
self.converted_revs.add(rev_id)
1736
def _store_new_weave(self, rev, inv, present_parents):
1737
# the XML is now updated with text versions
1739
entries = inv.iter_entries()
1741
for path, ie in entries:
1742
assert getattr(ie, 'revision', None) is not None, \
1743
'no revision on {%s} in {%s}' % \
1744
(file_id, rev.revision_id)
1745
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1746
new_inv_sha1 = sha_string(new_inv_xml)
1747
self.inv_weave.add_lines(rev.revision_id,
1749
new_inv_xml.splitlines(True))
1750
rev.inventory_sha1 = new_inv_sha1
1752
def _convert_revision_contents(self, rev, inv, present_parents):
1753
"""Convert all the files within a revision.
1755
Also upgrade the inventory to refer to the text revision ids."""
1756
rev_id = rev.revision_id
1757
mutter('converting texts of revision {%s}',
1759
parent_invs = map(self._load_updated_inventory, present_parents)
1760
entries = inv.iter_entries()
1762
for path, ie in entries:
1763
self._convert_file_version(rev, ie, parent_invs)
1765
def _convert_file_version(self, rev, ie, parent_invs):
1766
"""Convert one version of one file.
1768
The file needs to be added into the weave if it is a merge
1769
of >=2 parents or if it's changed from its parent.
1771
file_id = ie.file_id
1772
rev_id = rev.revision_id
1773
w = self.text_weaves.get(file_id)
1776
self.text_weaves[file_id] = w
1777
text_changed = False
1778
previous_entries = ie.find_previous_heads(parent_invs,
1782
for old_revision in previous_entries:
1783
# if this fails, its a ghost ?
1784
assert old_revision in self.converted_revs, \
1785
"Revision {%s} not in converted_revs" % old_revision
1786
self.snapshot_ie(previous_entries, ie, w, rev_id)
1788
assert getattr(ie, 'revision', None) is not None
1790
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1791
# TODO: convert this logic, which is ~= snapshot to
1792
# a call to:. This needs the path figured out. rather than a work_tree
1793
# a v4 revision_tree can be given, or something that looks enough like
1794
# one to give the file content to the entry if it needs it.
1795
# and we need something that looks like a weave store for snapshot to
1797
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1798
if len(previous_revisions) == 1:
1799
previous_ie = previous_revisions.values()[0]
1800
if ie._unchanged(previous_ie):
1801
ie.revision = previous_ie.revision
1804
text = self.branch.repository.text_store.get(ie.text_id)
1805
file_lines = text.readlines()
1806
assert sha_strings(file_lines) == ie.text_sha1
1807
assert sum(map(len, file_lines)) == ie.text_size
1808
w.add_lines(rev_id, previous_revisions, file_lines)
1809
self.text_count += 1
1811
w.add_lines(rev_id, previous_revisions, [])
1812
ie.revision = rev_id
1814
def _make_order(self):
1815
"""Return a suitable order for importing revisions.
1817
The order must be such that an revision is imported after all
1818
its (present) parents.
1820
todo = set(self.revisions.keys())
1821
done = self.absent_revisions.copy()
1824
# scan through looking for a revision whose parents
1826
for rev_id in sorted(list(todo)):
1827
rev = self.revisions[rev_id]
1828
parent_ids = set(rev.parent_ids)
1829
if parent_ids.issubset(done):
1830
# can take this one now
1831
order.append(rev_id)
1837
class ConvertBzrDir5To6(Converter):
1838
"""Converts format 5 bzr dirs to format 6."""
1840
def convert(self, to_convert, pb):
1841
"""See Converter.convert()."""
1842
self.bzrdir = to_convert
1844
self.pb.note('starting upgrade from format 5 to 6')
1845
self._convert_to_prefixed()
1846
return BzrDir.open(self.bzrdir.root_transport.base)
1848
def _convert_to_prefixed(self):
1849
from bzrlib.store import TransportStore
1850
self.bzrdir.transport.delete('branch-format')
1851
for store_name in ["weaves", "revision-store"]:
1852
self.pb.note("adding prefixes to %s" % store_name)
1853
store_transport = self.bzrdir.transport.clone(store_name)
1854
store = TransportStore(store_transport, prefixed=True)
1855
for urlfilename in store_transport.list_dir('.'):
1856
filename = urlutils.unescape(urlfilename)
1857
if (filename.endswith(".weave") or
1858
filename.endswith(".gz") or
1859
filename.endswith(".sig")):
1860
file_id = os.path.splitext(filename)[0]
1863
prefix_dir = store.hash_prefix(file_id)
1864
# FIXME keep track of the dirs made RBC 20060121
1866
store_transport.move(filename, prefix_dir + '/' + filename)
1867
except errors.NoSuchFile: # catches missing dirs strangely enough
1868
store_transport.mkdir(prefix_dir)
1869
store_transport.move(filename, prefix_dir + '/' + filename)
1870
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1873
class ConvertBzrDir6ToMeta(Converter):
1874
"""Converts format 6 bzr dirs to metadirs."""
1876
def convert(self, to_convert, pb):
1877
"""See Converter.convert()."""
1878
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1879
from bzrlib.branch import BzrBranchFormat5
1880
self.bzrdir = to_convert
1883
self.total = 20 # the steps we know about
1884
self.garbage_inventories = []
1886
self.pb.note('starting upgrade from format 6 to metadir')
1887
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1888
# its faster to move specific files around than to open and use the apis...
1889
# first off, nuke ancestry.weave, it was never used.
1891
self.step('Removing ancestry.weave')
1892
self.bzrdir.transport.delete('ancestry.weave')
1893
except errors.NoSuchFile:
1895
# find out whats there
1896
self.step('Finding branch files')
1897
last_revision = self.bzrdir.open_branch().last_revision()
1898
bzrcontents = self.bzrdir.transport.list_dir('.')
1899
for name in bzrcontents:
1900
if name.startswith('basis-inventory.'):
1901
self.garbage_inventories.append(name)
1902
# create new directories for repository, working tree and branch
1903
self.dir_mode = self.bzrdir._control_files._dir_mode
1904
self.file_mode = self.bzrdir._control_files._file_mode
1905
repository_names = [('inventory.weave', True),
1906
('revision-store', True),
1908
self.step('Upgrading repository ')
1909
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1910
self.make_lock('repository')
1911
# we hard code the formats here because we are converting into
1912
# the meta format. The meta format upgrader can take this to a
1913
# future format within each component.
1914
self.put_format('repository', RepositoryFormat7())
1915
for entry in repository_names:
1916
self.move_entry('repository', entry)
1918
self.step('Upgrading branch ')
1919
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1920
self.make_lock('branch')
1921
self.put_format('branch', BzrBranchFormat5())
1922
branch_files = [('revision-history', True),
1923
('branch-name', True),
1925
for entry in branch_files:
1926
self.move_entry('branch', entry)
1928
checkout_files = [('pending-merges', True),
1929
('inventory', True),
1930
('stat-cache', False)]
1931
# If a mandatory checkout file is not present, the branch does not have
1932
# a functional checkout. Do not create a checkout in the converted
1934
for name, mandatory in checkout_files:
1935
if mandatory and name not in bzrcontents:
1936
has_checkout = False
1940
if not has_checkout:
1941
self.pb.note('No working tree.')
1942
# If some checkout files are there, we may as well get rid of them.
1943
for name, mandatory in checkout_files:
1944
if name in bzrcontents:
1945
self.bzrdir.transport.delete(name)
1947
from bzrlib.workingtree import WorkingTreeFormat3
1948
self.step('Upgrading working tree')
1949
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1950
self.make_lock('checkout')
1952
'checkout', WorkingTreeFormat3())
1953
self.bzrdir.transport.delete_multi(
1954
self.garbage_inventories, self.pb)
1955
for entry in checkout_files:
1956
self.move_entry('checkout', entry)
1957
if last_revision is not None:
1958
self.bzrdir._control_files.put_utf8(
1959
'checkout/last-revision', last_revision)
1960
self.bzrdir._control_files.put_utf8(
1961
'branch-format', BzrDirMetaFormat1().get_format_string())
1962
return BzrDir.open(self.bzrdir.root_transport.base)
1964
def make_lock(self, name):
1965
"""Make a lock for the new control dir name."""
1966
self.step('Make %s lock' % name)
1967
ld = lockdir.LockDir(self.bzrdir.transport,
1969
file_modebits=self.file_mode,
1970
dir_modebits=self.dir_mode)
1973
def move_entry(self, new_dir, entry):
1974
"""Move then entry name into new_dir."""
1976
mandatory = entry[1]
1977
self.step('Moving %s' % name)
1979
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1980
except errors.NoSuchFile:
1984
def put_format(self, dirname, format):
1985
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1988
class ConvertMetaToMeta(Converter):
1989
"""Converts the components of metadirs."""
1991
def __init__(self, target_format):
1992
"""Create a metadir to metadir converter.
1994
:param target_format: The final metadir format that is desired.
1996
self.target_format = target_format
1998
def convert(self, to_convert, pb):
1999
"""See Converter.convert()."""
2000
self.bzrdir = to_convert
2004
self.step('checking repository format')
2006
repo = self.bzrdir.open_repository()
2007
except errors.NoRepositoryPresent:
2010
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2011
from bzrlib.repository import CopyConverter
2012
self.pb.note('starting repository conversion')
2013
converter = CopyConverter(self.target_format.repository_format)
2014
converter.convert(repo, pb)
2016
branch = self.bzrdir.open_branch()
2017
except errors.NotBranchError:
2020
# Avoid circular imports
2021
from bzrlib import branch as _mod_branch
2022
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2023
self.target_format.get_branch_format().__class__ is
2024
_mod_branch.BzrBranchFormat6):
2025
branch_converter = _mod_branch.Converter5to6()
2026
branch_converter.convert(branch)
2030
# This is not in remote.py because it's small, and needs to be registered.
2031
# Putting it in remote.py creates a circular import problem.
2032
# we can make it a lazy object if the control formats is turned into something
2034
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2035
"""Format representing bzrdirs accessed via a smart server"""
2037
def get_format_description(self):
2038
return 'bzr remote bzrdir'
2041
def probe_transport(klass, transport):
2042
"""Return a RemoteBzrDirFormat object if it looks possible."""
2044
transport.get_smart_client()
2045
except (NotImplementedError, AttributeError,
2046
errors.TransportNotPossible):
2047
# no smart server, so not a branch for this format type.
2048
raise errors.NotBranchError(path=transport.base)
2052
def initialize_on_transport(self, transport):
2053
# hand off the request to the smart server
2054
medium = transport.get_smart_medium()
2055
client = SmartClient(medium)
2056
path = client.remote_path_from_transport(transport)
2057
response = SmartClient(medium).call('BzrDirFormat.initialize', path)
2058
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2059
return remote.RemoteBzrDir(transport)
2061
def _open(self, transport):
2062
return remote.RemoteBzrDir(transport)
2064
def __eq__(self, other):
2065
if not isinstance(other, RemoteBzrDirFormat):
2067
return self.get_format_description() == other.get_format_description()
2070
# We can't use register_control_format because it adds it at a lower priority
2071
# than the existing branches, whereas this should take priority.
2072
BzrDirFormat._control_formats.insert(0, RemoteBzrDirFormat)
2075
class BzrDirFormatInfo(object):
2077
def __init__(self, native, deprecated):
2078
self.deprecated = deprecated
2079
self.native = native
2082
class BzrDirFormatRegistry(registry.Registry):
2083
"""Registry of user-selectable BzrDir subformats.
2085
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2086
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2089
def register_metadir(self, key, repo, help, native=True, deprecated=False,
2090
branch_format=None):
2091
"""Register a metadir subformat.
2093
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2094
by the Repository format.
2096
:param repo: The fully-qualified repository format class name as a
2099
# This should be expanded to support setting WorkingTree and Branch
2100
# formats, once BzrDirMetaFormat1 supports that.
2102
import bzrlib.branch
2103
mod_name, repo_factory_name = repo.rsplit('.', 1)
2105
mod = __import__(mod_name, globals(), locals(),
2106
[repo_factory_name])
2107
except ImportError, e:
2108
raise ImportError('failed to load repository %s: %s'
2111
repo_format_class = getattr(mod, repo_factory_name)
2112
except AttributeError:
2113
raise AttributeError('no repository format %r in module %r'
2115
bd = BzrDirMetaFormat1()
2116
bd.repository_format = repo_format_class()
2117
if branch_format is not None:
2118
bd.set_branch_format(getattr(bzrlib.branch, branch_format)())
2120
self.register(key, helper, help, native, deprecated)
2122
def register(self, key, factory, help, native=True, deprecated=False):
2123
"""Register a BzrDirFormat factory.
2125
The factory must be a callable that takes one parameter: the key.
2126
It must produce an instance of the BzrDirFormat when called.
2128
This function mainly exists to prevent the info object from being
2131
registry.Registry.register(self, key, factory, help,
2132
BzrDirFormatInfo(native, deprecated))
2134
def register_lazy(self, key, module_name, member_name, help, native=True,
2136
registry.Registry.register_lazy(self, key, module_name, member_name,
2137
help, BzrDirFormatInfo(native, deprecated))
2139
def set_default(self, key):
2140
"""Set the 'default' key to be a clone of the supplied key.
2142
This method must be called once and only once.
2144
registry.Registry.register(self, 'default', self.get(key),
2145
self.get_help(key), info=self.get_info(key))
2147
def set_default_repository(self, key):
2148
"""Set the FormatRegistry default and Repository default.
2150
This is a transitional method while Repository.set_default_format
2153
if 'default' in self:
2154
self.remove('default')
2155
self.set_default(key)
2156
format = self.get('default')()
2157
assert isinstance(format, BzrDirMetaFormat1)
2159
def make_bzrdir(self, key):
2160
return self.get(key)()
2162
def help_topic(self, topic):
2163
output = textwrap.dedent("""\
2164
Bazaar directory formats
2165
------------------------
2167
These formats can be used for creating branches, working trees, and
2171
default_help = self.get_help('default')
2173
for key in self.keys():
2174
if key == 'default':
2176
help = self.get_help(key)
2177
if help == default_help:
2178
default_realkey = key
2180
help_pairs.append((key, help))
2182
def wrapped(key, help, info):
2184
help = '(native) ' + help
2185
return ' %s:\n%s\n\n' % (key,
2186
textwrap.fill(help, initial_indent=' ',
2187
subsequent_indent=' '))
2188
output += wrapped('%s/default' % default_realkey, default_help,
2189
self.get_info('default'))
2190
deprecated_pairs = []
2191
for key, help in help_pairs:
2192
info = self.get_info(key)
2194
deprecated_pairs.append((key, help))
2196
output += wrapped(key, help, info)
2197
if len(deprecated_pairs) > 0:
2198
output += "Deprecated formats\n------------------\n\n"
2199
for key, help in deprecated_pairs:
2200
info = self.get_info(key)
2201
output += wrapped(key, help, info)
2206
format_registry = BzrDirFormatRegistry()
2207
format_registry.register('weave', BzrDirFormat6,
2208
'Pre-0.8 format. Slower than knit and does not'
2209
' support checkouts or shared repositories.',
2211
format_registry.register_metadir('knit',
2212
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2213
'Format using knits. Recommended.',
2214
branch_format='BzrBranchFormat5')
2215
format_registry.set_default('knit')
2216
format_registry.register_metadir('metaweave',
2217
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2218
'Transitional format in 0.8. Slower than knit.',
2221
format_registry.register_metadir('experimental-knit2',
2222
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
2223
'Experimental successor to knit. Use at your own risk.',
2224
branch_format='BzrBranchFormat5')
2225
format_registry.register_metadir('experimental-branch6',
2226
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2227
'Experimental successor to knit. Use at your own risk.',
2228
branch_format='BzrBranchFormat6')