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: The Format probe_transport seems a bit redundant with just trying to
26
# open the bzrdir. -- mbp
28
# TODO: Can we move specific formats into separate modules to make this file
31
from cStringIO import StringIO
35
from bzrlib.lazy_import import lazy_import
36
lazy_import(globals(), """
37
from copy import deepcopy
38
from stat import S_ISDIR
47
revision as _mod_revision,
55
from bzrlib.osutils import (
60
from bzrlib.store.revision.text import TextRevisionStore
61
from bzrlib.store.text import TextStore
62
from bzrlib.store.versioned import WeaveStore
63
from bzrlib.transactions import WriteTransaction
64
from bzrlib.transport import get_transport
65
from bzrlib.weave import Weave
68
from bzrlib.trace import mutter, note
69
from bzrlib.transport.local import LocalTransport
73
"""A .bzr control diretory.
75
BzrDir instances let you create or open any of the things that can be
76
found within .bzr - checkouts, branches and repositories.
79
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
81
a transport connected to the directory this bzr was opened from.
85
"""Invoke break_lock on the first object in the bzrdir.
87
If there is a tree, the tree is opened and break_lock() called.
88
Otherwise, branch is tried, and finally repository.
91
thing_to_unlock = self.open_workingtree()
92
except (errors.NotLocalUrl, errors.NoWorkingTree):
94
thing_to_unlock = self.open_branch()
95
except errors.NotBranchError:
97
thing_to_unlock = self.open_repository()
98
except errors.NoRepositoryPresent:
100
thing_to_unlock.break_lock()
102
def can_convert_format(self):
103
"""Return true if this bzrdir is one whose format we can convert from."""
106
def check_conversion_target(self, target_format):
107
target_repo_format = target_format.repository_format
108
source_repo_format = self._format.repository_format
109
source_repo_format.check_conversion_target(target_repo_format)
112
def _check_supported(format, allow_unsupported):
113
"""Check whether format is a supported format.
115
If allow_unsupported is True, this is a no-op.
117
if not allow_unsupported and not format.is_supported():
118
# see open_downlevel to open legacy branches.
119
raise errors.UnsupportedFormatError(format=format)
121
def clone(self, url, revision_id=None, force_new_repo=False):
122
"""Clone this bzrdir and its contents to url verbatim.
124
If urls last component does not exist, it will be created.
126
if revision_id is not None, then the clone operation may tune
127
itself to download less data.
128
:param force_new_repo: Do not use a shared repository for the target
129
even if one is available.
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)
143
result_repo.set_make_working_trees(local_repo.make_working_trees())
146
result_repo = result.find_repository()
147
# fetch content this dir needs.
148
result_repo.fetch(local_repo, revision_id=revision_id)
149
except errors.NoRepositoryPresent:
150
# needed to make one anyway.
151
result_repo = local_repo.clone(
153
revision_id=revision_id)
154
result_repo.set_make_working_trees(local_repo.make_working_trees())
155
# 1 if there is a branch present
156
# make sure its content is available in the target repository
159
self.open_branch().clone(result, revision_id=revision_id)
160
except errors.NotBranchError:
163
self.open_workingtree().clone(result)
164
except (errors.NoWorkingTree, errors.NotLocalUrl):
168
# TODO: This should be given a Transport, and should chdir up; otherwise
169
# this will open a new connection.
170
def _make_tail(self, url):
171
head, tail = urlutils.split(url)
172
if tail and tail != '.':
173
t = get_transport(head)
176
except errors.FileExists:
179
# TODO: Should take a Transport
181
def create(cls, base, format=None):
182
"""Create a new BzrDir at the url 'base'.
184
This will call the current default formats initialize with base
185
as the only parameter.
187
:param format: If supplied, the format of branch to create. If not
188
supplied, the default is used.
190
if cls is not BzrDir:
191
raise AssertionError("BzrDir.create always creates the default"
192
" format, not one of %r" % cls)
193
head, tail = urlutils.split(base)
194
if tail and tail != '.':
195
t = get_transport(head)
198
except errors.FileExists:
201
format = BzrDirFormat.get_default_format()
202
return format.initialize(safe_unicode(base))
204
def create_branch(self):
205
"""Create a branch in this BzrDir.
207
The bzrdirs format will control what branch format is created.
208
For more control see BranchFormatXX.create(a_bzrdir).
210
raise NotImplementedError(self.create_branch)
213
def create_branch_and_repo(base, force_new_repo=False, format=None):
214
"""Create a new BzrDir, Branch and Repository at the url 'base'.
216
This will use the current default BzrDirFormat, and use whatever
217
repository format that that uses via bzrdir.create_branch and
218
create_repository. If a shared repository is available that is used
221
The created Branch object is returned.
223
:param base: The URL to create the branch at.
224
:param force_new_repo: If True a new repository is always created.
226
bzrdir = BzrDir.create(base, format)
227
bzrdir._find_or_create_repository(force_new_repo)
228
return bzrdir.create_branch()
230
def _find_or_create_repository(self, force_new_repo):
231
"""Create a new repository if needed, returning the repository."""
233
return self.create_repository()
235
return self.find_repository()
236
except errors.NoRepositoryPresent:
237
return self.create_repository()
240
def create_branch_convenience(base, force_new_repo=False,
241
force_new_tree=None, format=None):
242
"""Create a new BzrDir, Branch and Repository at the url 'base'.
244
This is a convenience function - it will use an existing repository
245
if possible, can be told explicitly whether to create a working tree or
248
This will use the current default BzrDirFormat, and use whatever
249
repository format that that uses via bzrdir.create_branch and
250
create_repository. If a shared repository is available that is used
251
preferentially. Whatever repository is used, its tree creation policy
254
The created Branch object is returned.
255
If a working tree cannot be made due to base not being a file:// url,
256
no error is raised unless force_new_tree is True, in which case no
257
data is created on disk and NotLocalUrl is raised.
259
:param base: The URL to create the branch at.
260
:param force_new_repo: If True a new repository is always created.
261
:param force_new_tree: If True or False force creation of a tree or
262
prevent such creation respectively.
263
:param format: Override for the for the bzrdir format to create
266
# check for non local urls
267
t = get_transport(safe_unicode(base))
268
if not isinstance(t, LocalTransport):
269
raise errors.NotLocalUrl(base)
270
bzrdir = BzrDir.create(base, format)
271
repo = bzrdir._find_or_create_repository(force_new_repo)
272
result = bzrdir.create_branch()
273
if force_new_tree or (repo.make_working_trees() and
274
force_new_tree is None):
276
bzrdir.create_workingtree()
277
except errors.NotLocalUrl:
282
def create_repository(base, shared=False, format=None):
283
"""Create a new BzrDir and Repository at the url 'base'.
285
If no format is supplied, this will default to the current default
286
BzrDirFormat by default, and use whatever repository format that that
287
uses for bzrdirformat.create_repository.
289
:param shared: Create a shared repository rather than a standalone
291
The Repository object is returned.
293
This must be overridden as an instance method in child classes, where
294
it should take no parameters and construct whatever repository format
295
that child class desires.
297
bzrdir = BzrDir.create(base, format)
298
return bzrdir.create_repository(shared)
301
def create_standalone_workingtree(base, format=None):
302
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
304
'base' must be a local path or a file:// url.
306
This will use the current default BzrDirFormat, and use whatever
307
repository format that that uses for bzrdirformat.create_workingtree,
308
create_branch and create_repository.
310
:return: The WorkingTree object.
312
t = get_transport(safe_unicode(base))
313
if not isinstance(t, LocalTransport):
314
raise errors.NotLocalUrl(base)
315
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
317
format=format).bzrdir
318
return bzrdir.create_workingtree()
320
def create_workingtree(self, revision_id=None):
321
"""Create a working tree at this BzrDir.
323
revision_id: create it as of this revision id.
325
raise NotImplementedError(self.create_workingtree)
327
def retire_bzrdir(self):
328
"""Permanently disable the bzrdir.
330
This is done by renaming it to give the user some ability to recover
331
if there was a problem.
333
This will have horrible consequences if anyone has anything locked or
336
for i in xrange(10000):
338
to_path = '.bzr.retired.%d' % i
339
self.root_transport.rename('.bzr', to_path)
340
note("renamed %s to %s"
341
% (self.root_transport.abspath('.bzr'), to_path))
343
except (errors.TransportError, IOError, errors.PathError):
346
def destroy_workingtree(self):
347
"""Destroy the working tree at this BzrDir.
349
Formats that do not support this may raise UnsupportedOperation.
351
raise NotImplementedError(self.destroy_workingtree)
353
def destroy_workingtree_metadata(self):
354
"""Destroy the control files for the working tree at this BzrDir.
356
The contents of working tree files are not affected.
357
Formats that do not support this may raise UnsupportedOperation.
359
raise NotImplementedError(self.destroy_workingtree_metadata)
361
def find_repository(self):
362
"""Find the repository that should be used for a_bzrdir.
364
This does not require a branch as we use it to find the repo for
365
new branches as well as to hook existing branches up to their
369
return self.open_repository()
370
except errors.NoRepositoryPresent:
372
next_transport = self.root_transport.clone('..')
374
# find the next containing bzrdir
376
found_bzrdir = BzrDir.open_containing_from_transport(
378
except errors.NotBranchError:
380
raise errors.NoRepositoryPresent(self)
381
# does it have a repository ?
383
repository = found_bzrdir.open_repository()
384
except errors.NoRepositoryPresent:
385
next_transport = found_bzrdir.root_transport.clone('..')
386
if (found_bzrdir.root_transport.base == next_transport.base):
387
# top of the file system
391
if ((found_bzrdir.root_transport.base ==
392
self.root_transport.base) or repository.is_shared()):
395
raise errors.NoRepositoryPresent(self)
396
raise errors.NoRepositoryPresent(self)
398
def get_branch_transport(self, branch_format):
399
"""Get the transport for use by branch format in this BzrDir.
401
Note that bzr dirs that do not support format strings will raise
402
IncompatibleFormat if the branch format they are given has
403
a format string, and vice versa.
405
If branch_format is None, the transport is returned with no
406
checking. if it is not None, then the returned transport is
407
guaranteed to point to an existing directory ready for use.
409
raise NotImplementedError(self.get_branch_transport)
411
def get_repository_transport(self, repository_format):
412
"""Get the transport for use by repository format in this BzrDir.
414
Note that bzr dirs that do not support format strings will raise
415
IncompatibleFormat if the repository format they are given has
416
a format string, and vice versa.
418
If repository_format is None, the transport is returned with no
419
checking. if it is not None, then the returned transport is
420
guaranteed to point to an existing directory ready for use.
422
raise NotImplementedError(self.get_repository_transport)
424
def get_workingtree_transport(self, tree_format):
425
"""Get the transport for use by workingtree format in this BzrDir.
427
Note that bzr dirs that do not support format strings will raise
428
IncompatibleFormat if the workingtree format they are given has a
429
format string, and vice versa.
431
If workingtree_format is None, the transport is returned with no
432
checking. if it is not None, then the returned transport is
433
guaranteed to point to an existing directory ready for use.
435
raise NotImplementedError(self.get_workingtree_transport)
437
def __init__(self, _transport, _format):
438
"""Initialize a Bzr control dir object.
440
Only really common logic should reside here, concrete classes should be
441
made with varying behaviours.
443
:param _format: the format that is creating this BzrDir instance.
444
:param _transport: the transport this dir is based at.
446
self._format = _format
447
self.transport = _transport.clone('.bzr')
448
self.root_transport = _transport
450
def is_control_filename(self, filename):
451
"""True if filename is the name of a path which is reserved for bzrdir's.
453
:param filename: A filename within the root transport of this bzrdir.
455
This is true IF and ONLY IF the filename is part of the namespace reserved
456
for bzr control dirs. Currently this is the '.bzr' directory in the root
457
of the root_transport. it is expected that plugins will need to extend
458
this in the future - for instance to make bzr talk with svn working
461
# this might be better on the BzrDirFormat class because it refers to
462
# all the possible bzrdir disk formats.
463
# This method is tested via the workingtree is_control_filename tests-
464
# it was extracted from WorkingTree.is_control_filename. If the methods
465
# contract is extended beyond the current trivial implementation please
466
# add new tests for it to the appropriate place.
467
return filename == '.bzr' or filename.startswith('.bzr/')
469
def needs_format_conversion(self, format=None):
470
"""Return true if this bzrdir needs convert_format run on it.
472
For instance, if the repository format is out of date but the
473
branch and working tree are not, this should return True.
475
:param format: Optional parameter indicating a specific desired
476
format we plan to arrive at.
478
raise NotImplementedError(self.needs_format_conversion)
481
def open_unsupported(base):
482
"""Open a branch which is not supported."""
483
return BzrDir.open(base, _unsupported=True)
486
def open(base, _unsupported=False):
487
"""Open an existing bzrdir, rooted at 'base' (url)
489
_unsupported is a private parameter to the BzrDir class.
491
t = get_transport(base)
492
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
495
def open_from_transport(transport, _unsupported=False):
496
"""Open a bzrdir within a particular directory.
498
:param transport: Transport containing the bzrdir.
499
:param _unsupported: private.
501
format = BzrDirFormat.find_format(transport)
502
BzrDir._check_supported(format, _unsupported)
503
return format.open(transport, _found=True)
505
def open_branch(self, unsupported=False):
506
"""Open the branch object at this BzrDir if one is present.
508
If unsupported is True, then no longer supported branch formats can
511
TODO: static convenience version of this?
513
raise NotImplementedError(self.open_branch)
516
def open_containing(url):
517
"""Open an existing branch which contains url.
519
:param url: url to search from.
520
See open_containing_from_transport for more detail.
522
return BzrDir.open_containing_from_transport(get_transport(url))
525
def open_containing_from_transport(a_transport):
526
"""Open an existing branch which contains a_transport.base
528
This probes for a branch at a_transport, and searches upwards from there.
530
Basically we keep looking up until we find the control directory or
531
run into the root. If there isn't one, raises NotBranchError.
532
If there is one and it is either an unrecognised format or an unsupported
533
format, UnknownFormatError or UnsupportedFormatError are raised.
534
If there is one, it is returned, along with the unused portion of url.
536
:return: The BzrDir that contains the path, and a Unicode path
537
for the rest of the URL.
539
# this gets the normalised url back. I.e. '.' -> the full path.
540
url = a_transport.base
543
result = BzrDir.open_from_transport(a_transport)
544
return result, urlutils.unescape(a_transport.relpath(url))
545
except errors.NotBranchError, e:
547
new_t = a_transport.clone('..')
548
if new_t.base == a_transport.base:
549
# reached the root, whatever that may be
550
raise errors.NotBranchError(path=url)
554
def open_containing_tree_or_branch(klass, location):
555
"""Return the branch and working tree contained by a location.
557
Returns (tree, branch, relpath).
558
If there is no tree at containing the location, tree will be None.
559
If there is no branch containing the location, an exception will be
561
relpath is the portion of the path that is contained by the branch.
563
bzrdir, relpath = klass.open_containing(location)
565
tree = bzrdir.open_workingtree()
566
except (errors.NoWorkingTree, errors.NotLocalUrl):
568
branch = bzrdir.open_branch()
571
return tree, branch, relpath
573
def open_repository(self, _unsupported=False):
574
"""Open the repository object at this BzrDir if one is present.
576
This will not follow the Branch object pointer - its strictly a direct
577
open facility. Most client code should use open_branch().repository to
580
_unsupported is a private parameter, not part of the api.
581
TODO: static convenience version of this?
583
raise NotImplementedError(self.open_repository)
585
def open_workingtree(self, _unsupported=False):
586
"""Open the workingtree object at this BzrDir if one is present.
588
TODO: static convenience version of this?
590
raise NotImplementedError(self.open_workingtree)
592
def has_branch(self):
593
"""Tell if this bzrdir contains a branch.
595
Note: if you're going to open the branch, you should just go ahead
596
and try, and not ask permission first. (This method just opens the
597
branch and discards it, and that's somewhat expensive.)
602
except errors.NotBranchError:
605
def has_workingtree(self):
606
"""Tell if this bzrdir contains a working tree.
608
This will still raise an exception if the bzrdir has a workingtree that
609
is remote & inaccessible.
611
Note: if you're going to open the working tree, you should just go ahead
612
and try, and not ask permission first. (This method just opens the
613
workingtree and discards it, and that's somewhat expensive.)
616
self.open_workingtree()
618
except errors.NoWorkingTree:
621
def _cloning_metadir(self):
622
result_format = self._format.__class__()
625
branch = self.open_branch()
626
source_repository = branch.repository
627
except errors.NotBranchError:
629
source_repository = self.open_repository()
630
result_format.repository_format = source_repository._format
631
except errors.NoRepositoryPresent:
632
source_repository = None
634
tree = self.open_workingtree()
635
except (errors.NoWorkingTree, errors.NotLocalUrl):
636
result_format.workingtree_format = None
638
result_format.workingtree_format = tree._format.__class__()
639
return result_format, source_repository
641
def cloning_metadir(self):
642
"""Produce a metadir suitable for cloning or sprouting with.
644
These operations may produce workingtrees (yes, even though they're
645
"cloning" something that doesn't have a tree, so a viable workingtree
646
format must be selected.
648
format, repository = self._cloning_metadir()
649
if format._workingtree_format is None:
650
if repository is None:
652
tree_format = repository._format._matchingbzrdir.workingtree_format
653
format.workingtree_format = tree_format.__class__()
656
def checkout_metadir(self):
657
return self.cloning_metadir()
659
def sprout(self, url, revision_id=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)
678
source_branch = self.open_branch()
679
source_repository = source_branch.repository
680
except errors.NotBranchError:
683
source_repository = self.open_repository()
684
except errors.NoRepositoryPresent:
685
source_repository = None
690
result_repo = result.find_repository()
691
except errors.NoRepositoryPresent:
693
if source_repository is None and result_repo is not None:
695
elif source_repository is None and result_repo is None:
696
# no repo available, make a new one
697
result.create_repository()
698
elif source_repository is not None and result_repo is None:
699
# have source, and want to make a new target repo
700
# we don't clone the repo because that preserves attributes
701
# like is_shared(), and we have not yet implemented a
702
# repository sprout().
703
result_repo = result.create_repository()
704
if result_repo is not None:
705
# fetch needed content into target.
706
if source_repository is not None:
707
result_repo.fetch(source_repository, revision_id=revision_id)
708
if source_branch is not None:
709
source_branch.sprout(result, revision_id=revision_id)
711
result.create_branch()
712
# TODO: jam 20060426 we probably need a test in here in the
713
# case that the newly sprouted branch is a remote one
714
if result_repo is None or result_repo.make_working_trees():
715
wt = result.create_workingtree()
718
if wt.path2id('') is None:
720
wt.set_root_id(self.open_workingtree.get_root_id())
721
except errors.NoWorkingTree:
727
if recurse == 'down':
729
basis = wt.basis_tree()
731
subtrees = basis.iter_references()
732
recurse_branch = wt.branch
733
elif source_branch is not None:
734
basis = source_branch.basis_tree()
736
subtrees = basis.iter_references()
737
recurse_branch = source_branch
742
for path, file_id in subtrees:
743
target = urlutils.join(url, urlutils.escape(path))
744
sublocation = source_branch.reference_parent(file_id, path)
745
sublocation.bzrdir.sprout(target,
746
basis.get_reference_revision(file_id, path),
747
force_new_repo=force_new_repo, recurse=recurse)
749
if basis is not None:
754
class BzrDirPreSplitOut(BzrDir):
755
"""A common class for the all-in-one formats."""
757
def __init__(self, _transport, _format):
758
"""See BzrDir.__init__."""
759
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
760
assert self._format._lock_class == lockable_files.TransportLock
761
assert self._format._lock_file_name == 'branch-lock'
762
self._control_files = lockable_files.LockableFiles(
763
self.get_branch_transport(None),
764
self._format._lock_file_name,
765
self._format._lock_class)
767
def break_lock(self):
768
"""Pre-splitout bzrdirs do not suffer from stale locks."""
769
raise NotImplementedError(self.break_lock)
771
def clone(self, url, revision_id=None, force_new_repo=False):
772
"""See BzrDir.clone()."""
773
from bzrlib.workingtree import WorkingTreeFormat2
775
result = self._format._initialize_for_clone(url)
776
self.open_repository().clone(result, revision_id=revision_id)
777
from_branch = self.open_branch()
778
from_branch.clone(result, revision_id=revision_id)
780
self.open_workingtree().clone(result)
781
except errors.NotLocalUrl:
782
# make a new one, this format always has to have one.
784
WorkingTreeFormat2().initialize(result)
785
except errors.NotLocalUrl:
786
# but we cannot do it for remote trees.
787
to_branch = result.open_branch()
788
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
791
def create_branch(self):
792
"""See BzrDir.create_branch."""
793
return self.open_branch()
795
def create_repository(self, shared=False):
796
"""See BzrDir.create_repository."""
798
raise errors.IncompatibleFormat('shared repository', self._format)
799
return self.open_repository()
801
def create_workingtree(self, revision_id=None):
802
"""See BzrDir.create_workingtree."""
803
# this looks buggy but is not -really-
804
# clone and sprout will have set the revision_id
805
# and that will have set it for us, its only
806
# specific uses of create_workingtree in isolation
807
# that can do wonky stuff here, and that only
808
# happens for creating checkouts, which cannot be
809
# done on this format anyway. So - acceptable wart.
810
result = self.open_workingtree()
811
if revision_id is not None:
812
if revision_id == _mod_revision.NULL_REVISION:
813
result.set_parent_ids([])
815
result.set_parent_ids([revision_id])
818
def destroy_workingtree(self):
819
"""See BzrDir.destroy_workingtree."""
820
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
822
def destroy_workingtree_metadata(self):
823
"""See BzrDir.destroy_workingtree_metadata."""
824
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
827
def get_branch_transport(self, branch_format):
828
"""See BzrDir.get_branch_transport()."""
829
if branch_format is None:
830
return self.transport
832
branch_format.get_format_string()
833
except NotImplementedError:
834
return self.transport
835
raise errors.IncompatibleFormat(branch_format, self._format)
837
def get_repository_transport(self, repository_format):
838
"""See BzrDir.get_repository_transport()."""
839
if repository_format is None:
840
return self.transport
842
repository_format.get_format_string()
843
except NotImplementedError:
844
return self.transport
845
raise errors.IncompatibleFormat(repository_format, self._format)
847
def get_workingtree_transport(self, workingtree_format):
848
"""See BzrDir.get_workingtree_transport()."""
849
if workingtree_format is None:
850
return self.transport
852
workingtree_format.get_format_string()
853
except NotImplementedError:
854
return self.transport
855
raise errors.IncompatibleFormat(workingtree_format, self._format)
857
def needs_format_conversion(self, format=None):
858
"""See BzrDir.needs_format_conversion()."""
859
# if the format is not the same as the system default,
860
# an upgrade is needed.
862
format = BzrDirFormat.get_default_format()
863
return not isinstance(self._format, format.__class__)
865
def open_branch(self, unsupported=False):
866
"""See BzrDir.open_branch."""
867
from bzrlib.branch import BzrBranchFormat4
868
format = BzrBranchFormat4()
869
self._check_supported(format, unsupported)
870
return format.open(self, _found=True)
872
def sprout(self, url, revision_id=None, force_new_repo=False):
873
"""See BzrDir.sprout()."""
874
from bzrlib.workingtree import WorkingTreeFormat2
876
result = self._format._initialize_for_clone(url)
878
self.open_repository().clone(result, revision_id=revision_id)
879
except errors.NoRepositoryPresent:
882
self.open_branch().sprout(result, revision_id=revision_id)
883
except errors.NotBranchError:
885
# we always want a working tree
886
WorkingTreeFormat2().initialize(result)
890
class BzrDir4(BzrDirPreSplitOut):
891
"""A .bzr version 4 control object.
893
This is a deprecated format and may be removed after sept 2006.
896
def create_repository(self, shared=False):
897
"""See BzrDir.create_repository."""
898
return self._format.repository_format.initialize(self, shared)
900
def needs_format_conversion(self, format=None):
901
"""Format 4 dirs are always in need of conversion."""
904
def open_repository(self):
905
"""See BzrDir.open_repository."""
906
from bzrlib.repofmt.weaverepo import RepositoryFormat4
907
return RepositoryFormat4().open(self, _found=True)
910
class BzrDir5(BzrDirPreSplitOut):
911
"""A .bzr version 5 control object.
913
This is a deprecated format and may be removed after sept 2006.
916
def open_repository(self):
917
"""See BzrDir.open_repository."""
918
from bzrlib.repofmt.weaverepo import RepositoryFormat5
919
return RepositoryFormat5().open(self, _found=True)
921
def open_workingtree(self, _unsupported=False):
922
"""See BzrDir.create_workingtree."""
923
from bzrlib.workingtree import WorkingTreeFormat2
924
return WorkingTreeFormat2().open(self, _found=True)
927
class BzrDir6(BzrDirPreSplitOut):
928
"""A .bzr version 6 control object.
930
This is a deprecated format and may be removed after sept 2006.
933
def open_repository(self):
934
"""See BzrDir.open_repository."""
935
from bzrlib.repofmt.weaverepo import RepositoryFormat6
936
return RepositoryFormat6().open(self, _found=True)
938
def open_workingtree(self, _unsupported=False):
939
"""See BzrDir.create_workingtree."""
940
from bzrlib.workingtree import WorkingTreeFormat2
941
return WorkingTreeFormat2().open(self, _found=True)
944
class BzrDirMeta1(BzrDir):
945
"""A .bzr meta version 1 control object.
947
This is the first control object where the
948
individual aspects are really split out: there are separate repository,
949
workingtree and branch subdirectories and any subset of the three can be
950
present within a BzrDir.
953
def can_convert_format(self):
954
"""See BzrDir.can_convert_format()."""
957
def create_branch(self):
958
"""See BzrDir.create_branch."""
959
return self._format.get_branch_format().initialize(self)
961
def create_repository(self, shared=False):
962
"""See BzrDir.create_repository."""
963
return self._format.repository_format.initialize(self, shared)
965
def create_workingtree(self, revision_id=None):
966
"""See BzrDir.create_workingtree."""
967
from bzrlib.workingtree import WorkingTreeFormat
968
return self._format.workingtree_format.initialize(self, revision_id)
970
def destroy_workingtree(self):
971
"""See BzrDir.destroy_workingtree."""
972
wt = self.open_workingtree()
973
repository = wt.branch.repository
974
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
975
wt.revert([], old_tree=empty)
976
self.destroy_workingtree_metadata()
978
def destroy_workingtree_metadata(self):
979
self.transport.delete_tree('checkout')
981
def _get_mkdir_mode(self):
982
"""Figure out the mode to use when creating a bzrdir subdir."""
983
temp_control = lockable_files.LockableFiles(self.transport, '',
984
lockable_files.TransportLock)
985
return temp_control._dir_mode
987
def get_branch_transport(self, branch_format):
988
"""See BzrDir.get_branch_transport()."""
989
if branch_format is None:
990
return self.transport.clone('branch')
992
branch_format.get_format_string()
993
except NotImplementedError:
994
raise errors.IncompatibleFormat(branch_format, self._format)
996
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
997
except errors.FileExists:
999
return self.transport.clone('branch')
1001
def get_repository_transport(self, repository_format):
1002
"""See BzrDir.get_repository_transport()."""
1003
if repository_format is None:
1004
return self.transport.clone('repository')
1006
repository_format.get_format_string()
1007
except NotImplementedError:
1008
raise errors.IncompatibleFormat(repository_format, self._format)
1010
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1011
except errors.FileExists:
1013
return self.transport.clone('repository')
1015
def get_workingtree_transport(self, workingtree_format):
1016
"""See BzrDir.get_workingtree_transport()."""
1017
if workingtree_format is None:
1018
return self.transport.clone('checkout')
1020
workingtree_format.get_format_string()
1021
except NotImplementedError:
1022
raise errors.IncompatibleFormat(workingtree_format, self._format)
1024
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1025
except errors.FileExists:
1027
return self.transport.clone('checkout')
1029
def needs_format_conversion(self, format=None):
1030
"""See BzrDir.needs_format_conversion()."""
1032
format = BzrDirFormat.get_default_format()
1033
if not isinstance(self._format, format.__class__):
1034
# it is not a meta dir format, conversion is needed.
1036
# we might want to push this down to the repository?
1038
if not isinstance(self.open_repository()._format,
1039
format.repository_format.__class__):
1040
# the repository needs an upgrade.
1042
except errors.NoRepositoryPresent:
1045
if not isinstance(self.open_branch()._format,
1046
format.get_branch_format().__class__):
1047
# the branch needs an upgrade.
1049
except errors.NotBranchError:
1052
if not isinstance(self.open_workingtree()._format,
1053
format.workingtree_format.__class__):
1054
# the workingtree needs an upgrade.
1056
except (errors.NoWorkingTree, errors.NotLocalUrl):
1060
def open_branch(self, unsupported=False):
1061
"""See BzrDir.open_branch."""
1062
from bzrlib.branch import BranchFormat
1063
format = BranchFormat.find_format(self)
1064
self._check_supported(format, unsupported)
1065
return format.open(self, _found=True)
1067
def open_repository(self, unsupported=False):
1068
"""See BzrDir.open_repository."""
1069
from bzrlib.repository import RepositoryFormat
1070
format = RepositoryFormat.find_format(self)
1071
self._check_supported(format, unsupported)
1072
return format.open(self, _found=True)
1074
def open_workingtree(self, unsupported=False):
1075
"""See BzrDir.open_workingtree."""
1076
from bzrlib.workingtree import WorkingTreeFormat
1077
format = WorkingTreeFormat.find_format(self)
1078
self._check_supported(format, unsupported)
1079
return format.open(self, _found=True)
1082
class BzrDirFormat(object):
1083
"""An encapsulation of the initialization and open routines for a format.
1085
Formats provide three things:
1086
* An initialization routine,
1090
Formats are placed in an dict by their format string for reference
1091
during bzrdir opening. These should be subclasses of BzrDirFormat
1094
Once a format is deprecated, just deprecate the initialize and open
1095
methods on the format class. Do not deprecate the object, as the
1096
object will be created every system load.
1099
_default_format = None
1100
"""The default format used for new .bzr dirs."""
1103
"""The known formats."""
1105
_control_formats = []
1106
"""The registered control formats - .bzr, ....
1108
This is a list of BzrDirFormat objects.
1111
_lock_file_name = 'branch-lock'
1113
# _lock_class must be set in subclasses to the lock type, typ.
1114
# TransportLock or LockDir
1117
def find_format(klass, transport):
1118
"""Return the format present at transport."""
1119
for format in klass._control_formats:
1121
return format.probe_transport(transport)
1122
except errors.NotBranchError:
1123
# this format does not find a control dir here.
1125
raise errors.NotBranchError(path=transport.base)
1128
def probe_transport(klass, transport):
1129
"""Return the .bzrdir style transport present at URL."""
1131
format_string = transport.get(".bzr/branch-format").read()
1132
except errors.NoSuchFile:
1133
raise errors.NotBranchError(path=transport.base)
1136
return klass._formats[format_string]
1138
raise errors.UnknownFormatError(format=format_string)
1141
def get_default_format(klass):
1142
"""Return the current default format."""
1143
return klass._default_format
1145
def get_format_string(self):
1146
"""Return the ASCII format string that identifies this format."""
1147
raise NotImplementedError(self.get_format_string)
1149
def get_format_description(self):
1150
"""Return the short description for this format."""
1151
raise NotImplementedError(self.get_format_description)
1153
def get_converter(self, format=None):
1154
"""Return the converter to use to convert bzrdirs needing converts.
1156
This returns a bzrlib.bzrdir.Converter object.
1158
This should return the best upgrader to step this format towards the
1159
current default format. In the case of plugins we can/should provide
1160
some means for them to extend the range of returnable converters.
1162
:param format: Optional format to override the default format of the
1165
raise NotImplementedError(self.get_converter)
1167
def initialize(self, url):
1168
"""Create a bzr control dir at this url and return an opened copy.
1170
Subclasses should typically override initialize_on_transport
1171
instead of this method.
1173
return self.initialize_on_transport(get_transport(url))
1175
def initialize_on_transport(self, transport):
1176
"""Initialize a new bzrdir in the base directory of a Transport."""
1177
# Since we don't have a .bzr directory, inherit the
1178
# mode from the root directory
1179
temp_control = lockable_files.LockableFiles(transport,
1180
'', lockable_files.TransportLock)
1181
temp_control._transport.mkdir('.bzr',
1182
# FIXME: RBC 20060121 don't peek under
1184
mode=temp_control._dir_mode)
1185
file_mode = temp_control._file_mode
1187
mutter('created control directory in ' + transport.base)
1188
control = transport.clone('.bzr')
1189
utf8_files = [('README',
1190
"This is a Bazaar-NG control directory.\n"
1191
"Do not change any files in this directory.\n"),
1192
('branch-format', self.get_format_string()),
1194
# NB: no need to escape relative paths that are url safe.
1195
control_files = lockable_files.LockableFiles(control,
1196
self._lock_file_name, self._lock_class)
1197
control_files.create_lock()
1198
control_files.lock_write()
1200
for file, content in utf8_files:
1201
control_files.put_utf8(file, content)
1203
control_files.unlock()
1204
return self.open(transport, _found=True)
1206
def is_supported(self):
1207
"""Is this format supported?
1209
Supported formats must be initializable and openable.
1210
Unsupported formats may not support initialization or committing or
1211
some other features depending on the reason for not being supported.
1215
def same_model(self, target_format):
1216
return (self.repository_format.rich_root_data ==
1217
target_format.rich_root_data)
1220
def known_formats(klass):
1221
"""Return all the known formats.
1223
Concrete formats should override _known_formats.
1225
# There is double indirection here to make sure that control
1226
# formats used by more than one dir format will only be probed
1227
# once. This can otherwise be quite expensive for remote connections.
1229
for format in klass._control_formats:
1230
result.update(format._known_formats())
1234
def _known_formats(klass):
1235
"""Return the known format instances for this control format."""
1236
return set(klass._formats.values())
1238
def open(self, transport, _found=False):
1239
"""Return an instance of this format for the dir transport points at.
1241
_found is a private parameter, do not use it.
1244
found_format = BzrDirFormat.find_format(transport)
1245
if not isinstance(found_format, self.__class__):
1246
raise AssertionError("%s was asked to open %s, but it seems to need "
1248
% (self, transport, found_format))
1249
return self._open(transport)
1251
def _open(self, transport):
1252
"""Template method helper for opening BzrDirectories.
1254
This performs the actual open and any additional logic or parameter
1257
raise NotImplementedError(self._open)
1260
def register_format(klass, format):
1261
klass._formats[format.get_format_string()] = format
1264
def register_control_format(klass, format):
1265
"""Register a format that does not use '.bzrdir' for its control dir.
1267
TODO: This should be pulled up into a 'ControlDirFormat' base class
1268
which BzrDirFormat can inherit from, and renamed to register_format
1269
there. It has been done without that for now for simplicity of
1272
klass._control_formats.append(format)
1275
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1276
def set_default_format(klass, format):
1277
klass._set_default_format(format)
1280
def _set_default_format(klass, format):
1281
"""Set default format (for testing behavior of defaults only)"""
1282
klass._default_format = format
1285
return self.get_format_string()[:-1]
1288
def unregister_format(klass, format):
1289
assert klass._formats[format.get_format_string()] is format
1290
del klass._formats[format.get_format_string()]
1293
def unregister_control_format(klass, format):
1294
klass._control_formats.remove(format)
1297
# register BzrDirFormat as a control format
1298
BzrDirFormat.register_control_format(BzrDirFormat)
1301
class BzrDirFormat4(BzrDirFormat):
1302
"""Bzr dir format 4.
1304
This format is a combined format for working tree, branch and repository.
1306
- Format 1 working trees [always]
1307
- Format 4 branches [always]
1308
- Format 4 repositories [always]
1310
This format is deprecated: it indexes texts using a text it which is
1311
removed in format 5; write support for this format has been removed.
1314
_lock_class = lockable_files.TransportLock
1316
def get_format_string(self):
1317
"""See BzrDirFormat.get_format_string()."""
1318
return "Bazaar-NG branch, format 0.0.4\n"
1320
def get_format_description(self):
1321
"""See BzrDirFormat.get_format_description()."""
1322
return "All-in-one format 4"
1324
def get_converter(self, format=None):
1325
"""See BzrDirFormat.get_converter()."""
1326
# there is one and only one upgrade path here.
1327
return ConvertBzrDir4To5()
1329
def initialize_on_transport(self, transport):
1330
"""Format 4 branches cannot be created."""
1331
raise errors.UninitializableFormat(self)
1333
def is_supported(self):
1334
"""Format 4 is not supported.
1336
It is not supported because the model changed from 4 to 5 and the
1337
conversion logic is expensive - so doing it on the fly was not
1342
def _open(self, transport):
1343
"""See BzrDirFormat._open."""
1344
return BzrDir4(transport, self)
1346
def __return_repository_format(self):
1347
"""Circular import protection."""
1348
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1349
return RepositoryFormat4()
1350
repository_format = property(__return_repository_format)
1353
class BzrDirFormat5(BzrDirFormat):
1354
"""Bzr control format 5.
1356
This format is a combined format for working tree, branch and repository.
1358
- Format 2 working trees [always]
1359
- Format 4 branches [always]
1360
- Format 5 repositories [always]
1361
Unhashed stores in the repository.
1364
_lock_class = lockable_files.TransportLock
1366
def get_format_string(self):
1367
"""See BzrDirFormat.get_format_string()."""
1368
return "Bazaar-NG branch, format 5\n"
1370
def get_format_description(self):
1371
"""See BzrDirFormat.get_format_description()."""
1372
return "All-in-one format 5"
1374
def get_converter(self, format=None):
1375
"""See BzrDirFormat.get_converter()."""
1376
# there is one and only one upgrade path here.
1377
return ConvertBzrDir5To6()
1379
def _initialize_for_clone(self, url):
1380
return self.initialize_on_transport(get_transport(url), _cloning=True)
1382
def initialize_on_transport(self, transport, _cloning=False):
1383
"""Format 5 dirs always have working tree, branch and repository.
1385
Except when they are being cloned.
1387
from bzrlib.branch import BzrBranchFormat4
1388
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1389
from bzrlib.workingtree import WorkingTreeFormat2
1390
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1391
RepositoryFormat5().initialize(result, _internal=True)
1393
branch = BzrBranchFormat4().initialize(result)
1395
WorkingTreeFormat2().initialize(result)
1396
except errors.NotLocalUrl:
1397
# Even though we can't access the working tree, we need to
1398
# create its control files.
1399
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1402
def _open(self, transport):
1403
"""See BzrDirFormat._open."""
1404
return BzrDir5(transport, self)
1406
def __return_repository_format(self):
1407
"""Circular import protection."""
1408
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1409
return RepositoryFormat5()
1410
repository_format = property(__return_repository_format)
1413
class BzrDirFormat6(BzrDirFormat):
1414
"""Bzr control format 6.
1416
This format is a combined format for working tree, branch and repository.
1418
- Format 2 working trees [always]
1419
- Format 4 branches [always]
1420
- Format 6 repositories [always]
1423
_lock_class = lockable_files.TransportLock
1425
def get_format_string(self):
1426
"""See BzrDirFormat.get_format_string()."""
1427
return "Bazaar-NG branch, format 6\n"
1429
def get_format_description(self):
1430
"""See BzrDirFormat.get_format_description()."""
1431
return "All-in-one format 6"
1433
def get_converter(self, format=None):
1434
"""See BzrDirFormat.get_converter()."""
1435
# there is one and only one upgrade path here.
1436
return ConvertBzrDir6ToMeta()
1438
def _initialize_for_clone(self, url):
1439
return self.initialize_on_transport(get_transport(url), _cloning=True)
1441
def initialize_on_transport(self, transport, _cloning=False):
1442
"""Format 6 dirs always have working tree, branch and repository.
1444
Except when they are being cloned.
1446
from bzrlib.branch import BzrBranchFormat4
1447
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1448
from bzrlib.workingtree import WorkingTreeFormat2
1449
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1450
RepositoryFormat6().initialize(result, _internal=True)
1452
branch = BzrBranchFormat4().initialize(result)
1454
WorkingTreeFormat2().initialize(result)
1455
except errors.NotLocalUrl:
1456
# Even though we can't access the working tree, we need to
1457
# create its control files.
1458
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1461
def _open(self, transport):
1462
"""See BzrDirFormat._open."""
1463
return BzrDir6(transport, self)
1465
def __return_repository_format(self):
1466
"""Circular import protection."""
1467
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1468
return RepositoryFormat6()
1469
repository_format = property(__return_repository_format)
1472
class BzrDirMetaFormat1(BzrDirFormat):
1473
"""Bzr meta control format 1
1475
This is the first format with split out working tree, branch and repository
1478
- Format 3 working trees [optional]
1479
- Format 5 branches [optional]
1480
- Format 7 repositories [optional]
1483
_lock_class = lockdir.LockDir
1486
self._workingtree_format = None
1487
self._branch_format = None
1489
def __eq__(self, other):
1490
if other.__class__ is not self.__class__:
1492
if other.repository_format != self.repository_format:
1494
if other.workingtree_format != self.workingtree_format:
1498
def __ne__(self, other):
1499
return not self == other
1501
def get_branch_format(self):
1502
if self._branch_format is None:
1503
from bzrlib.branch import BranchFormat
1504
self._branch_format = BranchFormat.get_default_format()
1505
return self._branch_format
1507
def set_branch_format(self, format):
1508
self._branch_format = format
1510
def get_converter(self, format=None):
1511
"""See BzrDirFormat.get_converter()."""
1513
format = BzrDirFormat.get_default_format()
1514
if not isinstance(self, format.__class__):
1515
# converting away from metadir is not implemented
1516
raise NotImplementedError(self.get_converter)
1517
return ConvertMetaToMeta(format)
1519
def get_format_string(self):
1520
"""See BzrDirFormat.get_format_string()."""
1521
return "Bazaar-NG meta directory, format 1\n"
1523
def get_format_description(self):
1524
"""See BzrDirFormat.get_format_description()."""
1525
return "Meta directory format 1"
1527
def _open(self, transport):
1528
"""See BzrDirFormat._open."""
1529
return BzrDirMeta1(transport, self)
1531
def __return_repository_format(self):
1532
"""Circular import protection."""
1533
if getattr(self, '_repository_format', None):
1534
return self._repository_format
1535
from bzrlib.repository import RepositoryFormat
1536
return RepositoryFormat.get_default_format()
1538
def __set_repository_format(self, value):
1539
"""Allow changint the repository format for metadir formats."""
1540
self._repository_format = value
1542
repository_format = property(__return_repository_format, __set_repository_format)
1544
def __get_workingtree_format(self):
1545
if self._workingtree_format is None:
1546
from bzrlib.workingtree import WorkingTreeFormat
1547
self._workingtree_format = WorkingTreeFormat.get_default_format()
1548
return self._workingtree_format
1550
def __set_workingtree_format(self, wt_format):
1551
self._workingtree_format = wt_format
1553
workingtree_format = property(__get_workingtree_format,
1554
__set_workingtree_format)
1557
BzrDirFormat.register_format(BzrDirFormat4())
1558
BzrDirFormat.register_format(BzrDirFormat5())
1559
BzrDirFormat.register_format(BzrDirFormat6())
1560
__default_format = BzrDirMetaFormat1()
1561
BzrDirFormat.register_format(__default_format)
1562
BzrDirFormat._default_format = __default_format
1565
class BzrDirTestProviderAdapter(object):
1566
"""A tool to generate a suite testing multiple bzrdir formats at once.
1568
This is done by copying the test once for each transport and injecting
1569
the transport_server, transport_readonly_server, and bzrdir_format
1570
classes into each copy. Each copy is also given a new id() to make it
1574
def __init__(self, transport_server, transport_readonly_server, formats):
1575
self._transport_server = transport_server
1576
self._transport_readonly_server = transport_readonly_server
1577
self._formats = formats
1579
def adapt(self, test):
1580
result = unittest.TestSuite()
1581
for format in self._formats:
1582
new_test = deepcopy(test)
1583
new_test.transport_server = self._transport_server
1584
new_test.transport_readonly_server = self._transport_readonly_server
1585
new_test.bzrdir_format = format
1586
def make_new_test_id():
1587
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1588
return lambda: new_id
1589
new_test.id = make_new_test_id()
1590
result.addTest(new_test)
1594
class Converter(object):
1595
"""Converts a disk format object from one format to another."""
1597
def convert(self, to_convert, pb):
1598
"""Perform the conversion of to_convert, giving feedback via pb.
1600
:param to_convert: The disk object to convert.
1601
:param pb: a progress bar to use for progress information.
1604
def step(self, message):
1605
"""Update the pb by a step."""
1607
self.pb.update(message, self.count, self.total)
1610
class ConvertBzrDir4To5(Converter):
1611
"""Converts format 4 bzr dirs to format 5."""
1614
super(ConvertBzrDir4To5, self).__init__()
1615
self.converted_revs = set()
1616
self.absent_revisions = set()
1620
def convert(self, to_convert, pb):
1621
"""See Converter.convert()."""
1622
self.bzrdir = to_convert
1624
self.pb.note('starting upgrade from format 4 to 5')
1625
if isinstance(self.bzrdir.transport, LocalTransport):
1626
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1627
self._convert_to_weaves()
1628
return BzrDir.open(self.bzrdir.root_transport.base)
1630
def _convert_to_weaves(self):
1631
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1634
stat = self.bzrdir.transport.stat('weaves')
1635
if not S_ISDIR(stat.st_mode):
1636
self.bzrdir.transport.delete('weaves')
1637
self.bzrdir.transport.mkdir('weaves')
1638
except errors.NoSuchFile:
1639
self.bzrdir.transport.mkdir('weaves')
1640
# deliberately not a WeaveFile as we want to build it up slowly.
1641
self.inv_weave = Weave('inventory')
1642
# holds in-memory weaves for all files
1643
self.text_weaves = {}
1644
self.bzrdir.transport.delete('branch-format')
1645
self.branch = self.bzrdir.open_branch()
1646
self._convert_working_inv()
1647
rev_history = self.branch.revision_history()
1648
# to_read is a stack holding the revisions we still need to process;
1649
# appending to it adds new highest-priority revisions
1650
self.known_revisions = set(rev_history)
1651
self.to_read = rev_history[-1:]
1653
rev_id = self.to_read.pop()
1654
if (rev_id not in self.revisions
1655
and rev_id not in self.absent_revisions):
1656
self._load_one_rev(rev_id)
1658
to_import = self._make_order()
1659
for i, rev_id in enumerate(to_import):
1660
self.pb.update('converting revision', i, len(to_import))
1661
self._convert_one_rev(rev_id)
1663
self._write_all_weaves()
1664
self._write_all_revs()
1665
self.pb.note('upgraded to weaves:')
1666
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1667
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1668
self.pb.note(' %6d texts', self.text_count)
1669
self._cleanup_spare_files_after_format4()
1670
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1672
def _cleanup_spare_files_after_format4(self):
1673
# FIXME working tree upgrade foo.
1674
for n in 'merged-patches', 'pending-merged-patches':
1676
## assert os.path.getsize(p) == 0
1677
self.bzrdir.transport.delete(n)
1678
except errors.NoSuchFile:
1680
self.bzrdir.transport.delete_tree('inventory-store')
1681
self.bzrdir.transport.delete_tree('text-store')
1683
def _convert_working_inv(self):
1684
inv = xml4.serializer_v4.read_inventory(
1685
self.branch.control_files.get('inventory'))
1686
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1687
# FIXME inventory is a working tree change.
1688
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1690
def _write_all_weaves(self):
1691
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1692
weave_transport = self.bzrdir.transport.clone('weaves')
1693
weaves = WeaveStore(weave_transport, prefixed=False)
1694
transaction = WriteTransaction()
1698
for file_id, file_weave in self.text_weaves.items():
1699
self.pb.update('writing weave', i, len(self.text_weaves))
1700
weaves._put_weave(file_id, file_weave, transaction)
1702
self.pb.update('inventory', 0, 1)
1703
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1704
self.pb.update('inventory', 1, 1)
1708
def _write_all_revs(self):
1709
"""Write all revisions out in new form."""
1710
self.bzrdir.transport.delete_tree('revision-store')
1711
self.bzrdir.transport.mkdir('revision-store')
1712
revision_transport = self.bzrdir.transport.clone('revision-store')
1714
_revision_store = TextRevisionStore(TextStore(revision_transport,
1718
transaction = WriteTransaction()
1719
for i, rev_id in enumerate(self.converted_revs):
1720
self.pb.update('write revision', i, len(self.converted_revs))
1721
_revision_store.add_revision(self.revisions[rev_id], transaction)
1725
def _load_one_rev(self, rev_id):
1726
"""Load a revision object into memory.
1728
Any parents not either loaded or abandoned get queued to be
1730
self.pb.update('loading revision',
1731
len(self.revisions),
1732
len(self.known_revisions))
1733
if not self.branch.repository.has_revision(rev_id):
1735
self.pb.note('revision {%s} not present in branch; '
1736
'will be converted as a ghost',
1738
self.absent_revisions.add(rev_id)
1740
rev = self.branch.repository._revision_store.get_revision(rev_id,
1741
self.branch.repository.get_transaction())
1742
for parent_id in rev.parent_ids:
1743
self.known_revisions.add(parent_id)
1744
self.to_read.append(parent_id)
1745
self.revisions[rev_id] = rev
1747
def _load_old_inventory(self, rev_id):
1748
assert rev_id not in self.converted_revs
1749
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1750
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1751
inv.revision_id = rev_id
1752
rev = self.revisions[rev_id]
1753
if rev.inventory_sha1:
1754
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1755
'inventory sha mismatch for {%s}' % rev_id
1758
def _load_updated_inventory(self, rev_id):
1759
assert rev_id in self.converted_revs
1760
inv_xml = self.inv_weave.get_text(rev_id)
1761
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1764
def _convert_one_rev(self, rev_id):
1765
"""Convert revision and all referenced objects to new format."""
1766
rev = self.revisions[rev_id]
1767
inv = self._load_old_inventory(rev_id)
1768
present_parents = [p for p in rev.parent_ids
1769
if p not in self.absent_revisions]
1770
self._convert_revision_contents(rev, inv, present_parents)
1771
self._store_new_weave(rev, inv, present_parents)
1772
self.converted_revs.add(rev_id)
1774
def _store_new_weave(self, rev, inv, present_parents):
1775
# the XML is now updated with text versions
1777
entries = inv.iter_entries()
1779
for path, ie in entries:
1780
assert getattr(ie, 'revision', None) is not None, \
1781
'no revision on {%s} in {%s}' % \
1782
(file_id, rev.revision_id)
1783
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1784
new_inv_sha1 = sha_string(new_inv_xml)
1785
self.inv_weave.add_lines(rev.revision_id,
1787
new_inv_xml.splitlines(True))
1788
rev.inventory_sha1 = new_inv_sha1
1790
def _convert_revision_contents(self, rev, inv, present_parents):
1791
"""Convert all the files within a revision.
1793
Also upgrade the inventory to refer to the text revision ids."""
1794
rev_id = rev.revision_id
1795
mutter('converting texts of revision {%s}',
1797
parent_invs = map(self._load_updated_inventory, present_parents)
1798
entries = inv.iter_entries()
1800
for path, ie in entries:
1801
self._convert_file_version(rev, ie, parent_invs)
1803
def _convert_file_version(self, rev, ie, parent_invs):
1804
"""Convert one version of one file.
1806
The file needs to be added into the weave if it is a merge
1807
of >=2 parents or if it's changed from its parent.
1809
file_id = ie.file_id
1810
rev_id = rev.revision_id
1811
w = self.text_weaves.get(file_id)
1814
self.text_weaves[file_id] = w
1815
text_changed = False
1816
previous_entries = ie.find_previous_heads(parent_invs,
1820
for old_revision in previous_entries:
1821
# if this fails, its a ghost ?
1822
assert old_revision in self.converted_revs, \
1823
"Revision {%s} not in converted_revs" % old_revision
1824
self.snapshot_ie(previous_entries, ie, w, rev_id)
1826
assert getattr(ie, 'revision', None) is not None
1828
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1829
# TODO: convert this logic, which is ~= snapshot to
1830
# a call to:. This needs the path figured out. rather than a work_tree
1831
# a v4 revision_tree can be given, or something that looks enough like
1832
# one to give the file content to the entry if it needs it.
1833
# and we need something that looks like a weave store for snapshot to
1835
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1836
if len(previous_revisions) == 1:
1837
previous_ie = previous_revisions.values()[0]
1838
if ie._unchanged(previous_ie):
1839
ie.revision = previous_ie.revision
1842
text = self.branch.repository.text_store.get(ie.text_id)
1843
file_lines = text.readlines()
1844
assert sha_strings(file_lines) == ie.text_sha1
1845
assert sum(map(len, file_lines)) == ie.text_size
1846
w.add_lines(rev_id, previous_revisions, file_lines)
1847
self.text_count += 1
1849
w.add_lines(rev_id, previous_revisions, [])
1850
ie.revision = rev_id
1852
def _make_order(self):
1853
"""Return a suitable order for importing revisions.
1855
The order must be such that an revision is imported after all
1856
its (present) parents.
1858
todo = set(self.revisions.keys())
1859
done = self.absent_revisions.copy()
1862
# scan through looking for a revision whose parents
1864
for rev_id in sorted(list(todo)):
1865
rev = self.revisions[rev_id]
1866
parent_ids = set(rev.parent_ids)
1867
if parent_ids.issubset(done):
1868
# can take this one now
1869
order.append(rev_id)
1875
class ConvertBzrDir5To6(Converter):
1876
"""Converts format 5 bzr dirs to format 6."""
1878
def convert(self, to_convert, pb):
1879
"""See Converter.convert()."""
1880
self.bzrdir = to_convert
1882
self.pb.note('starting upgrade from format 5 to 6')
1883
self._convert_to_prefixed()
1884
return BzrDir.open(self.bzrdir.root_transport.base)
1886
def _convert_to_prefixed(self):
1887
from bzrlib.store import TransportStore
1888
self.bzrdir.transport.delete('branch-format')
1889
for store_name in ["weaves", "revision-store"]:
1890
self.pb.note("adding prefixes to %s" % store_name)
1891
store_transport = self.bzrdir.transport.clone(store_name)
1892
store = TransportStore(store_transport, prefixed=True)
1893
for urlfilename in store_transport.list_dir('.'):
1894
filename = urlutils.unescape(urlfilename)
1895
if (filename.endswith(".weave") or
1896
filename.endswith(".gz") or
1897
filename.endswith(".sig")):
1898
file_id = os.path.splitext(filename)[0]
1901
prefix_dir = store.hash_prefix(file_id)
1902
# FIXME keep track of the dirs made RBC 20060121
1904
store_transport.move(filename, prefix_dir + '/' + filename)
1905
except errors.NoSuchFile: # catches missing dirs strangely enough
1906
store_transport.mkdir(prefix_dir)
1907
store_transport.move(filename, prefix_dir + '/' + filename)
1908
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1911
class ConvertBzrDir6ToMeta(Converter):
1912
"""Converts format 6 bzr dirs to metadirs."""
1914
def convert(self, to_convert, pb):
1915
"""See Converter.convert()."""
1916
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1917
from bzrlib.branch import BzrBranchFormat5
1918
self.bzrdir = to_convert
1921
self.total = 20 # the steps we know about
1922
self.garbage_inventories = []
1924
self.pb.note('starting upgrade from format 6 to metadir')
1925
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1926
# its faster to move specific files around than to open and use the apis...
1927
# first off, nuke ancestry.weave, it was never used.
1929
self.step('Removing ancestry.weave')
1930
self.bzrdir.transport.delete('ancestry.weave')
1931
except errors.NoSuchFile:
1933
# find out whats there
1934
self.step('Finding branch files')
1935
last_revision = self.bzrdir.open_branch().last_revision()
1936
bzrcontents = self.bzrdir.transport.list_dir('.')
1937
for name in bzrcontents:
1938
if name.startswith('basis-inventory.'):
1939
self.garbage_inventories.append(name)
1940
# create new directories for repository, working tree and branch
1941
self.dir_mode = self.bzrdir._control_files._dir_mode
1942
self.file_mode = self.bzrdir._control_files._file_mode
1943
repository_names = [('inventory.weave', True),
1944
('revision-store', True),
1946
self.step('Upgrading repository ')
1947
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1948
self.make_lock('repository')
1949
# we hard code the formats here because we are converting into
1950
# the meta format. The meta format upgrader can take this to a
1951
# future format within each component.
1952
self.put_format('repository', RepositoryFormat7())
1953
for entry in repository_names:
1954
self.move_entry('repository', entry)
1956
self.step('Upgrading branch ')
1957
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1958
self.make_lock('branch')
1959
self.put_format('branch', BzrBranchFormat5())
1960
branch_files = [('revision-history', True),
1961
('branch-name', True),
1963
for entry in branch_files:
1964
self.move_entry('branch', entry)
1966
checkout_files = [('pending-merges', True),
1967
('inventory', True),
1968
('stat-cache', False)]
1969
# If a mandatory checkout file is not present, the branch does not have
1970
# a functional checkout. Do not create a checkout in the converted
1972
for name, mandatory in checkout_files:
1973
if mandatory and name not in bzrcontents:
1974
has_checkout = False
1978
if not has_checkout:
1979
self.pb.note('No working tree.')
1980
# If some checkout files are there, we may as well get rid of them.
1981
for name, mandatory in checkout_files:
1982
if name in bzrcontents:
1983
self.bzrdir.transport.delete(name)
1985
from bzrlib.workingtree import WorkingTreeFormat3
1986
self.step('Upgrading working tree')
1987
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1988
self.make_lock('checkout')
1990
'checkout', WorkingTreeFormat3())
1991
self.bzrdir.transport.delete_multi(
1992
self.garbage_inventories, self.pb)
1993
for entry in checkout_files:
1994
self.move_entry('checkout', entry)
1995
if last_revision is not None:
1996
self.bzrdir._control_files.put_utf8(
1997
'checkout/last-revision', last_revision)
1998
self.bzrdir._control_files.put_utf8(
1999
'branch-format', BzrDirMetaFormat1().get_format_string())
2000
return BzrDir.open(self.bzrdir.root_transport.base)
2002
def make_lock(self, name):
2003
"""Make a lock for the new control dir name."""
2004
self.step('Make %s lock' % name)
2005
ld = lockdir.LockDir(self.bzrdir.transport,
2007
file_modebits=self.file_mode,
2008
dir_modebits=self.dir_mode)
2011
def move_entry(self, new_dir, entry):
2012
"""Move then entry name into new_dir."""
2014
mandatory = entry[1]
2015
self.step('Moving %s' % name)
2017
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2018
except errors.NoSuchFile:
2022
def put_format(self, dirname, format):
2023
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2026
class ConvertMetaToMeta(Converter):
2027
"""Converts the components of metadirs."""
2029
def __init__(self, target_format):
2030
"""Create a metadir to metadir converter.
2032
:param target_format: The final metadir format that is desired.
2034
self.target_format = target_format
2036
def convert(self, to_convert, pb):
2037
"""See Converter.convert()."""
2038
self.bzrdir = to_convert
2042
self.step('checking repository format')
2044
repo = self.bzrdir.open_repository()
2045
except errors.NoRepositoryPresent:
2048
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2049
from bzrlib.repository import CopyConverter
2050
self.pb.note('starting repository conversion')
2051
converter = CopyConverter(self.target_format.repository_format)
2052
converter.convert(repo, pb)
2054
branch = self.bzrdir.open_branch()
2055
except errors.NotBranchError:
2058
# TODO: conversions of Branch and Tree should be done by
2059
# InterXFormat lookups
2060
# Avoid circular imports
2061
from bzrlib import branch as _mod_branch
2062
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2063
self.target_format.get_branch_format().__class__ is
2064
_mod_branch.BzrBranchFormat6):
2065
branch_converter = _mod_branch.Converter5to6()
2066
branch_converter.convert(branch)
2068
tree = self.bzrdir.open_workingtree()
2069
except (errors.NoWorkingTree, errors.NotLocalUrl):
2072
# TODO: conversions of Branch and Tree should be done by
2073
# InterXFormat lookups
2074
if (isinstance(tree, workingtree.WorkingTree3) and
2075
not isinstance(tree, workingtree_4.WorkingTree4) and
2076
isinstance(self.target_format.workingtree_format,
2077
workingtree_4.WorkingTreeFormat4)):
2078
workingtree_4.Converter3to4().convert(tree)
2082
class BzrDirFormatInfo(object):
2084
def __init__(self, native, deprecated, hidden):
2085
self.deprecated = deprecated
2086
self.native = native
2087
self.hidden = hidden
2090
class BzrDirFormatRegistry(registry.Registry):
2091
"""Registry of user-selectable BzrDir subformats.
2093
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2094
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2097
def register_metadir(self, key,
2098
repository_format, help, native=True, deprecated=False,
2102
"""Register a metadir subformat.
2104
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2105
by the Repository format.
2107
:param repository_format: The fully-qualified repository format class
2109
:param branch_format: Fully-qualified branch format class name as
2111
:param tree_format: Fully-qualified tree format class name as
2114
# This should be expanded to support setting WorkingTree and Branch
2115
# formats, once BzrDirMetaFormat1 supports that.
2116
def _load(full_name):
2117
mod_name, factory_name = full_name.rsplit('.', 1)
2119
mod = __import__(mod_name, globals(), locals(),
2121
except ImportError, e:
2122
raise ImportError('failed to load %s: %s' % (full_name, e))
2124
factory = getattr(mod, factory_name)
2125
except AttributeError:
2126
raise AttributeError('no factory %s in module %r'
2131
bd = BzrDirMetaFormat1()
2132
if branch_format is not None:
2133
bd.set_branch_format(_load(branch_format))
2134
if tree_format is not None:
2135
bd.workingtree_format = _load(tree_format)
2136
if repository_format is not None:
2137
bd.repository_format = _load(repository_format)
2139
self.register(key, helper, help, native, deprecated, hidden)
2141
def register(self, key, factory, help, native=True, deprecated=False,
2143
"""Register a BzrDirFormat factory.
2145
The factory must be a callable that takes one parameter: the key.
2146
It must produce an instance of the BzrDirFormat when called.
2148
This function mainly exists to prevent the info object from being
2151
registry.Registry.register(self, key, factory, help,
2152
BzrDirFormatInfo(native, deprecated, hidden))
2154
def register_lazy(self, key, module_name, member_name, help, native=True,
2155
deprecated=False, hidden=False):
2156
registry.Registry.register_lazy(self, key, module_name, member_name,
2157
help, BzrDirFormatInfo(native, deprecated, hidden))
2159
def set_default(self, key):
2160
"""Set the 'default' key to be a clone of the supplied key.
2162
This method must be called once and only once.
2164
registry.Registry.register(self, 'default', self.get(key),
2165
self.get_help(key), info=self.get_info(key))
2167
def set_default_repository(self, key):
2168
"""Set the FormatRegistry default and Repository default.
2170
This is a transitional method while Repository.set_default_format
2173
if 'default' in self:
2174
self.remove('default')
2175
self.set_default(key)
2176
format = self.get('default')()
2177
assert isinstance(format, BzrDirMetaFormat1)
2179
def make_bzrdir(self, key):
2180
return self.get(key)()
2182
def help_topic(self, topic):
2183
output = textwrap.dedent("""\
2184
Bazaar directory formats
2185
------------------------
2187
These formats can be used for creating branches, working trees, and
2191
default_help = self.get_help('default')
2193
for key in self.keys():
2194
if key == 'default':
2196
help = self.get_help(key)
2197
if help == default_help:
2198
default_realkey = key
2200
help_pairs.append((key, help))
2202
def wrapped(key, help, info):
2204
help = '(native) ' + help
2205
return ' %s:\n%s\n\n' % (key,
2206
textwrap.fill(help, initial_indent=' ',
2207
subsequent_indent=' '))
2208
output += wrapped('%s/default' % default_realkey, default_help,
2209
self.get_info('default'))
2210
deprecated_pairs = []
2211
for key, help in help_pairs:
2212
info = self.get_info(key)
2215
elif info.deprecated:
2216
deprecated_pairs.append((key, help))
2218
output += wrapped(key, help, info)
2219
if len(deprecated_pairs) > 0:
2220
output += "Deprecated formats\n------------------\n\n"
2221
for key, help in deprecated_pairs:
2222
info = self.get_info(key)
2223
output += wrapped(key, help, info)
2228
format_registry = BzrDirFormatRegistry()
2229
format_registry.register('weave', BzrDirFormat6,
2230
'Pre-0.8 format. Slower than knit and does not'
2231
' support checkouts or shared repositories.',
2233
format_registry.register_metadir('knit',
2234
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2235
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2236
branch_format='bzrlib.branch.BzrBranchFormat5',
2237
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2238
format_registry.register_metadir('metaweave',
2239
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2240
'Transitional format in 0.8. Slower than knit.',
2241
branch_format='bzrlib.branch.BzrBranchFormat5',
2242
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2244
format_registry.register_metadir('dirstate',
2245
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2246
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2247
'above when accessed over the network.',
2248
branch_format='bzrlib.branch.BzrBranchFormat5',
2249
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2250
# directly from workingtree_4 triggers a circular import.
2251
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2253
format_registry.register_metadir('dirstate-tags',
2254
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2255
help='New in 0.15: Fast local operations and improved scaling for '
2256
'network operations. Additionally adds support for tags.'
2257
' Incompatible with bzr < 0.15.',
2258
branch_format='bzrlib.branch.BzrBranchFormat6',
2259
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2261
format_registry.register_metadir('dirstate-with-subtree',
2262
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2263
help='New in 0.15: Fast local operations and improved scaling for '
2264
'network operations. Additionally adds support for versioning nested '
2265
'bzr branches. Incompatible with bzr < 0.15.',
2266
branch_format='bzrlib.branch.BzrBranchFormat6',
2267
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2270
format_registry.set_default('dirstate')