1
# Copyright (C) 2005, 2006 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
# TODO: remove unittest dependency; put that stuff inside the test suite
25
# TODO: 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,
48
repository as _mod_repository,
54
from bzrlib.osutils import (
59
from bzrlib.store.revision.text import TextRevisionStore
60
from bzrlib.store.text import TextStore
61
from bzrlib.store.versioned import WeaveStore
62
from bzrlib.transactions import WriteTransaction
63
from bzrlib.transport import get_transport
64
from bzrlib.weave import Weave
67
from bzrlib.trace import mutter
68
from bzrlib.transport.local import LocalTransport
72
"""A .bzr control diretory.
74
BzrDir instances let you create or open any of the things that can be
75
found within .bzr - checkouts, branches and repositories.
78
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
80
a transport connected to the directory this bzr was opened from.
84
"""Invoke break_lock on the first object in the bzrdir.
86
If there is a tree, the tree is opened and break_lock() called.
87
Otherwise, branch is tried, and finally repository.
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_transport(self, branch_format):
407
"""Get the transport for use by branch format in this BzrDir.
409
Note that bzr dirs that do not support format strings will raise
410
IncompatibleFormat if the branch format they are given has
411
a format string, and vice versa.
413
If branch_format is None, the transport is returned with no
414
checking. if it is not None, then the returned transport is
415
guaranteed to point to an existing directory ready for use.
417
raise NotImplementedError(self.get_branch_transport)
419
def get_repository_transport(self, repository_format):
420
"""Get the transport for use by repository format in this BzrDir.
422
Note that bzr dirs that do not support format strings will raise
423
IncompatibleFormat if the repository format they are given has
424
a format string, and vice versa.
426
If repository_format is None, the transport is returned with no
427
checking. if it is not None, then the returned transport is
428
guaranteed to point to an existing directory ready for use.
430
raise NotImplementedError(self.get_repository_transport)
432
def get_workingtree_transport(self, tree_format):
433
"""Get the transport for use by workingtree format in this BzrDir.
435
Note that bzr dirs that do not support format strings will raise
436
IncompatibleFormat if the workingtree format they are given has a
437
format string, and vice versa.
439
If workingtree_format is None, the transport is returned with no
440
checking. if it is not None, then the returned transport is
441
guaranteed to point to an existing directory ready for use.
443
raise NotImplementedError(self.get_workingtree_transport)
445
def __init__(self, _transport, _format):
446
"""Initialize a Bzr control dir object.
448
Only really common logic should reside here, concrete classes should be
449
made with varying behaviours.
451
:param _format: the format that is creating this BzrDir instance.
452
:param _transport: the transport this dir is based at.
454
self._format = _format
455
self.transport = _transport.clone('.bzr')
456
self.root_transport = _transport
458
def is_control_filename(self, filename):
459
"""True if filename is the name of a path which is reserved for bzrdir's.
461
:param filename: A filename within the root transport of this bzrdir.
463
This is true IF and ONLY IF the filename is part of the namespace reserved
464
for bzr control dirs. Currently this is the '.bzr' directory in the root
465
of the root_transport. it is expected that plugins will need to extend
466
this in the future - for instance to make bzr talk with svn working
469
# this might be better on the BzrDirFormat class because it refers to
470
# all the possible bzrdir disk formats.
471
# This method is tested via the workingtree is_control_filename tests-
472
# it was extracted from WorkingTree.is_control_filename. If the methods
473
# contract is extended beyond the current trivial implementation please
474
# add new tests for it to the appropriate place.
475
return filename == '.bzr' or filename.startswith('.bzr/')
477
def needs_format_conversion(self, format=None):
478
"""Return true if this bzrdir needs convert_format run on it.
480
For instance, if the repository format is out of date but the
481
branch and working tree are not, this should return True.
483
:param format: Optional parameter indicating a specific desired
484
format we plan to arrive at.
486
raise NotImplementedError(self.needs_format_conversion)
489
def open_unsupported(base):
490
"""Open a branch which is not supported."""
491
return BzrDir.open(base, _unsupported=True)
494
def open(base, _unsupported=False):
495
"""Open an existing bzrdir, rooted at 'base' (url)
497
_unsupported is a private parameter to the BzrDir class.
499
t = get_transport(base)
500
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
503
def open_from_transport(transport, _unsupported=False):
504
"""Open a bzrdir within a particular directory.
506
:param transport: Transport containing the bzrdir.
507
:param _unsupported: private.
509
format = BzrDirFormat.find_format(transport)
510
BzrDir._check_supported(format, _unsupported)
511
return format.open(transport, _found=True)
513
def open_branch(self, unsupported=False):
514
"""Open the branch object at this BzrDir if one is present.
516
If unsupported is True, then no longer supported branch formats can
519
TODO: static convenience version of this?
521
raise NotImplementedError(self.open_branch)
524
def open_containing(url):
525
"""Open an existing branch which contains url.
527
:param url: url to search from.
528
See open_containing_from_transport for more detail.
530
return BzrDir.open_containing_from_transport(get_transport(url))
533
def open_containing_from_transport(a_transport):
534
"""Open an existing branch which contains a_transport.base
536
This probes for a branch at a_transport, and searches upwards from there.
538
Basically we keep looking up until we find the control directory or
539
run into the root. If there isn't one, raises NotBranchError.
540
If there is one and it is either an unrecognised format or an unsupported
541
format, UnknownFormatError or UnsupportedFormatError are raised.
542
If there is one, it is returned, along with the unused portion of url.
544
:return: The BzrDir that contains the path, and a Unicode path
545
for the rest of the URL.
547
# this gets the normalised url back. I.e. '.' -> the full path.
548
url = a_transport.base
551
result = BzrDir.open_from_transport(a_transport)
552
return result, urlutils.unescape(a_transport.relpath(url))
553
except errors.NotBranchError, e:
555
new_t = a_transport.clone('..')
556
if new_t.base == a_transport.base:
557
# reached the root, whatever that may be
558
raise errors.NotBranchError(path=url)
561
def open_repository(self, _unsupported=False):
562
"""Open the repository object at this BzrDir if one is present.
564
This will not follow the Branch object pointer - its strictly a direct
565
open facility. Most client code should use open_branch().repository to
568
_unsupported is a private parameter, not part of the api.
569
TODO: static convenience version of this?
571
raise NotImplementedError(self.open_repository)
573
def open_workingtree(self, _unsupported=False):
574
"""Open the workingtree object at this BzrDir if one is present.
576
TODO: static convenience version of this?
578
raise NotImplementedError(self.open_workingtree)
580
def has_branch(self):
581
"""Tell if this bzrdir contains a branch.
583
Note: if you're going to open the branch, you should just go ahead
584
and try, and not ask permission first. (This method just opens the
585
branch and discards it, and that's somewhat expensive.)
590
except errors.NotBranchError:
593
def has_workingtree(self):
594
"""Tell if this bzrdir contains a working tree.
596
This will still raise an exception if the bzrdir has a workingtree that
597
is remote & inaccessible.
599
Note: if you're going to open the working tree, you should just go ahead
600
and try, and not ask permission first. (This method just opens the
601
workingtree and discards it, and that's somewhat expensive.)
604
self.open_workingtree()
606
except errors.NoWorkingTree:
609
def cloning_metadir(self, basis=None):
610
"""Produce a metadir suitable for cloning with"""
611
def related_repository(bzrdir):
613
branch = bzrdir.open_branch()
614
return branch.repository
615
except errors.NotBranchError:
617
return bzrdir.open_repository()
618
result_format = self._format.__class__()
621
source_repository = related_repository(self)
622
except errors.NoRepositoryPresent:
625
source_repository = related_repository(self)
626
result_format.repository_format = source_repository._format
627
except errors.NoRepositoryPresent:
631
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
632
"""Create a copy of this bzrdir prepared for use as a new line of
635
If urls last component does not exist, it will be created.
637
Attributes related to the identity of the source branch like
638
branch nickname will be cleaned, a working tree is created
639
whether one existed before or not; and a local branch is always
642
if revision_id is not None, then the clone operation may tune
643
itself to download less data.
646
cloning_format = self.cloning_metadir(basis)
647
result = cloning_format.initialize(url)
648
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
650
source_branch = self.open_branch()
651
source_repository = source_branch.repository
652
except errors.NotBranchError:
655
source_repository = self.open_repository()
656
except errors.NoRepositoryPresent:
657
# copy the entire basis one if there is one
658
# but there is no repository.
659
source_repository = basis_repo
664
result_repo = result.find_repository()
665
except errors.NoRepositoryPresent:
667
if source_repository is None and result_repo is not None:
669
elif source_repository is None and result_repo is None:
670
# no repo available, make a new one
671
result.create_repository()
672
elif source_repository is not None and result_repo is None:
673
# have source, and want to make a new target repo
674
# we don't clone the repo because that preserves attributes
675
# like is_shared(), and we have not yet implemented a
676
# repository sprout().
677
result_repo = result.create_repository()
678
if result_repo is not None:
679
# fetch needed content into target.
681
# XXX FIXME RBC 20060214 need tests for this when the basis
683
result_repo.fetch(basis_repo, revision_id=revision_id)
684
if source_repository is not None:
685
result_repo.fetch(source_repository, revision_id=revision_id)
686
if source_branch is not None:
687
source_branch.sprout(result, revision_id=revision_id)
689
result.create_branch()
690
# TODO: jam 20060426 we probably need a test in here in the
691
# case that the newly sprouted branch is a remote one
692
if result_repo is None or result_repo.make_working_trees():
693
wt = result.create_workingtree()
694
if wt.inventory.root is None:
696
wt.set_root_id(self.open_workingtree.get_root_id())
697
except errors.NoWorkingTree:
702
class BzrDirPreSplitOut(BzrDir):
703
"""A common class for the all-in-one formats."""
705
def __init__(self, _transport, _format):
706
"""See BzrDir.__init__."""
707
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
708
assert self._format._lock_class == lockable_files.TransportLock
709
assert self._format._lock_file_name == 'branch-lock'
710
self._control_files = lockable_files.LockableFiles(
711
self.get_branch_transport(None),
712
self._format._lock_file_name,
713
self._format._lock_class)
715
def break_lock(self):
716
"""Pre-splitout bzrdirs do not suffer from stale locks."""
717
raise NotImplementedError(self.break_lock)
719
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
720
"""See BzrDir.clone()."""
721
from bzrlib.workingtree import WorkingTreeFormat2
723
result = self._format._initialize_for_clone(url)
724
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
725
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
726
from_branch = self.open_branch()
727
from_branch.clone(result, revision_id=revision_id)
729
self.open_workingtree().clone(result, basis=basis_tree)
730
except errors.NotLocalUrl:
731
# make a new one, this format always has to have one.
733
WorkingTreeFormat2().initialize(result)
734
except errors.NotLocalUrl:
735
# but we cannot do it for remote trees.
736
to_branch = result.open_branch()
737
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
740
def create_branch(self):
741
"""See BzrDir.create_branch."""
742
return self.open_branch()
744
def create_repository(self, shared=False):
745
"""See BzrDir.create_repository."""
747
raise errors.IncompatibleFormat('shared repository', self._format)
748
return self.open_repository()
750
def create_workingtree(self, revision_id=None):
751
"""See BzrDir.create_workingtree."""
752
# this looks buggy but is not -really-
753
# clone and sprout will have set the revision_id
754
# and that will have set it for us, its only
755
# specific uses of create_workingtree in isolation
756
# that can do wonky stuff here, and that only
757
# happens for creating checkouts, which cannot be
758
# done on this format anyway. So - acceptable wart.
759
result = self.open_workingtree()
760
if revision_id is not None:
761
if revision_id == _mod_revision.NULL_REVISION:
762
result.set_parent_ids([])
764
result.set_parent_ids([revision_id])
767
def destroy_workingtree(self):
768
"""See BzrDir.destroy_workingtree."""
769
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
771
def destroy_workingtree_metadata(self):
772
"""See BzrDir.destroy_workingtree_metadata."""
773
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
776
def get_branch_transport(self, branch_format):
777
"""See BzrDir.get_branch_transport()."""
778
if branch_format is None:
779
return self.transport
781
branch_format.get_format_string()
782
except NotImplementedError:
783
return self.transport
784
raise errors.IncompatibleFormat(branch_format, self._format)
786
def get_repository_transport(self, repository_format):
787
"""See BzrDir.get_repository_transport()."""
788
if repository_format is None:
789
return self.transport
791
repository_format.get_format_string()
792
except NotImplementedError:
793
return self.transport
794
raise errors.IncompatibleFormat(repository_format, self._format)
796
def get_workingtree_transport(self, workingtree_format):
797
"""See BzrDir.get_workingtree_transport()."""
798
if workingtree_format is None:
799
return self.transport
801
workingtree_format.get_format_string()
802
except NotImplementedError:
803
return self.transport
804
raise errors.IncompatibleFormat(workingtree_format, self._format)
806
def needs_format_conversion(self, format=None):
807
"""See BzrDir.needs_format_conversion()."""
808
# if the format is not the same as the system default,
809
# an upgrade is needed.
811
format = BzrDirFormat.get_default_format()
812
return not isinstance(self._format, format.__class__)
814
def open_branch(self, unsupported=False):
815
"""See BzrDir.open_branch."""
816
from bzrlib.branch import BzrBranchFormat4
817
format = BzrBranchFormat4()
818
self._check_supported(format, unsupported)
819
return format.open(self, _found=True)
821
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
822
"""See BzrDir.sprout()."""
823
from bzrlib.workingtree import WorkingTreeFormat2
825
result = self._format._initialize_for_clone(url)
826
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
828
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
829
except errors.NoRepositoryPresent:
832
self.open_branch().sprout(result, revision_id=revision_id)
833
except errors.NotBranchError:
835
# we always want a working tree
836
WorkingTreeFormat2().initialize(result)
840
class BzrDir4(BzrDirPreSplitOut):
841
"""A .bzr version 4 control object.
843
This is a deprecated format and may be removed after sept 2006.
846
def create_repository(self, shared=False):
847
"""See BzrDir.create_repository."""
848
return self._format.repository_format.initialize(self, shared)
850
def needs_format_conversion(self, format=None):
851
"""Format 4 dirs are always in need of conversion."""
854
def open_repository(self):
855
"""See BzrDir.open_repository."""
856
from bzrlib.repository import RepositoryFormat4
857
return RepositoryFormat4().open(self, _found=True)
860
class BzrDir5(BzrDirPreSplitOut):
861
"""A .bzr version 5 control object.
863
This is a deprecated format and may be removed after sept 2006.
866
def open_repository(self):
867
"""See BzrDir.open_repository."""
868
from bzrlib.repository import RepositoryFormat5
869
return RepositoryFormat5().open(self, _found=True)
871
def open_workingtree(self, _unsupported=False):
872
"""See BzrDir.create_workingtree."""
873
from bzrlib.workingtree import WorkingTreeFormat2
874
return WorkingTreeFormat2().open(self, _found=True)
877
class BzrDir6(BzrDirPreSplitOut):
878
"""A .bzr version 6 control object.
880
This is a deprecated format and may be removed after sept 2006.
883
def open_repository(self):
884
"""See BzrDir.open_repository."""
885
from bzrlib.repository import RepositoryFormat6
886
return RepositoryFormat6().open(self, _found=True)
888
def open_workingtree(self, _unsupported=False):
889
"""See BzrDir.create_workingtree."""
890
from bzrlib.workingtree import WorkingTreeFormat2
891
return WorkingTreeFormat2().open(self, _found=True)
894
class BzrDirMeta1(BzrDir):
895
"""A .bzr meta version 1 control object.
897
This is the first control object where the
898
individual aspects are really split out: there are separate repository,
899
workingtree and branch subdirectories and any subset of the three can be
900
present within a BzrDir.
903
def can_convert_format(self):
904
"""See BzrDir.can_convert_format()."""
907
def create_branch(self):
908
"""See BzrDir.create_branch."""
909
from bzrlib.branch import BranchFormat
910
return BranchFormat.get_default_format().initialize(self)
912
def create_repository(self, shared=False):
913
"""See BzrDir.create_repository."""
914
return self._format.repository_format.initialize(self, shared)
916
def create_workingtree(self, revision_id=None):
917
"""See BzrDir.create_workingtree."""
918
from bzrlib.workingtree import WorkingTreeFormat
919
return self._format.workingtree_format.initialize(self, revision_id)
921
def destroy_workingtree(self):
922
"""See BzrDir.destroy_workingtree."""
923
wt = self.open_workingtree()
924
repository = wt.branch.repository
925
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
926
wt.revert([], old_tree=empty)
927
self.destroy_workingtree_metadata()
929
def destroy_workingtree_metadata(self):
930
self.transport.delete_tree('checkout')
932
def _get_mkdir_mode(self):
933
"""Figure out the mode to use when creating a bzrdir subdir."""
934
temp_control = lockable_files.LockableFiles(self.transport, '',
935
lockable_files.TransportLock)
936
return temp_control._dir_mode
938
def get_branch_transport(self, branch_format):
939
"""See BzrDir.get_branch_transport()."""
940
if branch_format is None:
941
return self.transport.clone('branch')
943
branch_format.get_format_string()
944
except NotImplementedError:
945
raise errors.IncompatibleFormat(branch_format, self._format)
947
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
948
except errors.FileExists:
950
return self.transport.clone('branch')
952
def get_repository_transport(self, repository_format):
953
"""See BzrDir.get_repository_transport()."""
954
if repository_format is None:
955
return self.transport.clone('repository')
957
repository_format.get_format_string()
958
except NotImplementedError:
959
raise errors.IncompatibleFormat(repository_format, self._format)
961
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
962
except errors.FileExists:
964
return self.transport.clone('repository')
966
def get_workingtree_transport(self, workingtree_format):
967
"""See BzrDir.get_workingtree_transport()."""
968
if workingtree_format is None:
969
return self.transport.clone('checkout')
971
workingtree_format.get_format_string()
972
except NotImplementedError:
973
raise errors.IncompatibleFormat(workingtree_format, self._format)
975
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
976
except errors.FileExists:
978
return self.transport.clone('checkout')
980
def needs_format_conversion(self, format=None):
981
"""See BzrDir.needs_format_conversion()."""
983
format = BzrDirFormat.get_default_format()
984
if not isinstance(self._format, format.__class__):
985
# it is not a meta dir format, conversion is needed.
987
# we might want to push this down to the repository?
989
if not isinstance(self.open_repository()._format,
990
format.repository_format.__class__):
991
# the repository needs an upgrade.
993
except errors.NoRepositoryPresent:
995
# currently there are no other possible conversions for meta1 formats.
998
def open_branch(self, unsupported=False):
999
"""See BzrDir.open_branch."""
1000
from bzrlib.branch import BranchFormat
1001
format = BranchFormat.find_format(self)
1002
self._check_supported(format, unsupported)
1003
return format.open(self, _found=True)
1005
def open_repository(self, unsupported=False):
1006
"""See BzrDir.open_repository."""
1007
from bzrlib.repository import RepositoryFormat
1008
format = RepositoryFormat.find_format(self)
1009
self._check_supported(format, unsupported)
1010
return format.open(self, _found=True)
1012
def open_workingtree(self, unsupported=False):
1013
"""See BzrDir.open_workingtree."""
1014
from bzrlib.workingtree import WorkingTreeFormat
1015
format = WorkingTreeFormat.find_format(self)
1016
self._check_supported(format, unsupported)
1017
return format.open(self, _found=True)
1020
class BzrDirFormat(object):
1021
"""An encapsulation of the initialization and open routines for a format.
1023
Formats provide three things:
1024
* An initialization routine,
1028
Formats are placed in an dict by their format string for reference
1029
during bzrdir opening. These should be subclasses of BzrDirFormat
1032
Once a format is deprecated, just deprecate the initialize and open
1033
methods on the format class. Do not deprecate the object, as the
1034
object will be created every system load.
1037
_default_format = None
1038
"""The default format used for new .bzr dirs."""
1041
"""The known formats."""
1043
_control_formats = []
1044
"""The registered control formats - .bzr, ....
1046
This is a list of BzrDirFormat objects.
1049
_lock_file_name = 'branch-lock'
1051
# _lock_class must be set in subclasses to the lock type, typ.
1052
# TransportLock or LockDir
1055
def find_format(klass, transport):
1056
"""Return the format present at transport."""
1057
for format in klass._control_formats:
1059
return format.probe_transport(transport)
1060
except errors.NotBranchError:
1061
# this format does not find a control dir here.
1063
raise errors.NotBranchError(path=transport.base)
1066
def probe_transport(klass, transport):
1067
"""Return the .bzrdir style transport present at URL."""
1069
format_string = transport.get(".bzr/branch-format").read()
1070
except errors.NoSuchFile:
1071
raise errors.NotBranchError(path=transport.base)
1074
return klass._formats[format_string]
1076
raise errors.UnknownFormatError(format=format_string)
1079
def get_default_format(klass):
1080
"""Return the current default format."""
1081
return klass._default_format
1083
def get_format_string(self):
1084
"""Return the ASCII format string that identifies this format."""
1085
raise NotImplementedError(self.get_format_string)
1087
def get_format_description(self):
1088
"""Return the short description for this format."""
1089
raise NotImplementedError(self.get_format_description)
1091
def get_converter(self, format=None):
1092
"""Return the converter to use to convert bzrdirs needing converts.
1094
This returns a bzrlib.bzrdir.Converter object.
1096
This should return the best upgrader to step this format towards the
1097
current default format. In the case of plugins we can/should provide
1098
some means for them to extend the range of returnable converters.
1100
:param format: Optional format to override the default format of the
1103
raise NotImplementedError(self.get_converter)
1105
def initialize(self, url):
1106
"""Create a bzr control dir at this url and return an opened copy.
1108
Subclasses should typically override initialize_on_transport
1109
instead of this method.
1111
return self.initialize_on_transport(get_transport(url))
1113
def initialize_on_transport(self, transport):
1114
"""Initialize a new bzrdir in the base directory of a Transport."""
1115
# Since we don't have a .bzr directory, inherit the
1116
# mode from the root directory
1117
temp_control = lockable_files.LockableFiles(transport,
1118
'', lockable_files.TransportLock)
1119
temp_control._transport.mkdir('.bzr',
1120
# FIXME: RBC 20060121 don't peek under
1122
mode=temp_control._dir_mode)
1123
file_mode = temp_control._file_mode
1125
mutter('created control directory in ' + transport.base)
1126
control = transport.clone('.bzr')
1127
utf8_files = [('README',
1128
"This is a Bazaar-NG control directory.\n"
1129
"Do not change any files in this directory.\n"),
1130
('branch-format', self.get_format_string()),
1132
# NB: no need to escape relative paths that are url safe.
1133
control_files = lockable_files.LockableFiles(control,
1134
self._lock_file_name, self._lock_class)
1135
control_files.create_lock()
1136
control_files.lock_write()
1138
for file, content in utf8_files:
1139
control_files.put_utf8(file, content)
1141
control_files.unlock()
1142
return self.open(transport, _found=True)
1144
def is_supported(self):
1145
"""Is this format supported?
1147
Supported formats must be initializable and openable.
1148
Unsupported formats may not support initialization or committing or
1149
some other features depending on the reason for not being supported.
1153
def same_model(self, target_format):
1154
return (self.repository_format.rich_root_data ==
1155
target_format.rich_root_data)
1158
def known_formats(klass):
1159
"""Return all the known formats.
1161
Concrete formats should override _known_formats.
1163
# There is double indirection here to make sure that control
1164
# formats used by more than one dir format will only be probed
1165
# once. This can otherwise be quite expensive for remote connections.
1167
for format in klass._control_formats:
1168
result.update(format._known_formats())
1172
def _known_formats(klass):
1173
"""Return the known format instances for this control format."""
1174
return set(klass._formats.values())
1176
def open(self, transport, _found=False):
1177
"""Return an instance of this format for the dir transport points at.
1179
_found is a private parameter, do not use it.
1182
found_format = BzrDirFormat.find_format(transport)
1183
if not isinstance(found_format, self.__class__):
1184
raise AssertionError("%s was asked to open %s, but it seems to need "
1186
% (self, transport, found_format))
1187
return self._open(transport)
1189
def _open(self, transport):
1190
"""Template method helper for opening BzrDirectories.
1192
This performs the actual open and any additional logic or parameter
1195
raise NotImplementedError(self._open)
1198
def register_format(klass, format):
1199
klass._formats[format.get_format_string()] = format
1202
def register_control_format(klass, format):
1203
"""Register a format that does not use '.bzrdir' for its control dir.
1205
TODO: This should be pulled up into a 'ControlDirFormat' base class
1206
which BzrDirFormat can inherit from, and renamed to register_format
1207
there. It has been done without that for now for simplicity of
1210
klass._control_formats.append(format)
1213
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1214
def set_default_format(klass, format):
1215
klass._set_default_format(format)
1218
def _set_default_format(klass, format):
1219
"""Set default format (for testing behavior of defaults only)"""
1220
klass._default_format = format
1223
return self.get_format_string()[:-1]
1226
def unregister_format(klass, format):
1227
assert klass._formats[format.get_format_string()] is format
1228
del klass._formats[format.get_format_string()]
1231
def unregister_control_format(klass, format):
1232
klass._control_formats.remove(format)
1235
# register BzrDirFormat as a control format
1236
BzrDirFormat.register_control_format(BzrDirFormat)
1239
class BzrDirFormat4(BzrDirFormat):
1240
"""Bzr dir format 4.
1242
This format is a combined format for working tree, branch and repository.
1244
- Format 1 working trees [always]
1245
- Format 4 branches [always]
1246
- Format 4 repositories [always]
1248
This format is deprecated: it indexes texts using a text it which is
1249
removed in format 5; write support for this format has been removed.
1252
_lock_class = lockable_files.TransportLock
1254
def get_format_string(self):
1255
"""See BzrDirFormat.get_format_string()."""
1256
return "Bazaar-NG branch, format 0.0.4\n"
1258
def get_format_description(self):
1259
"""See BzrDirFormat.get_format_description()."""
1260
return "All-in-one format 4"
1262
def get_converter(self, format=None):
1263
"""See BzrDirFormat.get_converter()."""
1264
# there is one and only one upgrade path here.
1265
return ConvertBzrDir4To5()
1267
def initialize_on_transport(self, transport):
1268
"""Format 4 branches cannot be created."""
1269
raise errors.UninitializableFormat(self)
1271
def is_supported(self):
1272
"""Format 4 is not supported.
1274
It is not supported because the model changed from 4 to 5 and the
1275
conversion logic is expensive - so doing it on the fly was not
1280
def _open(self, transport):
1281
"""See BzrDirFormat._open."""
1282
return BzrDir4(transport, self)
1284
def __return_repository_format(self):
1285
"""Circular import protection."""
1286
from bzrlib.repository import RepositoryFormat4
1287
return RepositoryFormat4()
1288
repository_format = property(__return_repository_format)
1291
class BzrDirFormat5(BzrDirFormat):
1292
"""Bzr control format 5.
1294
This format is a combined format for working tree, branch and repository.
1296
- Format 2 working trees [always]
1297
- Format 4 branches [always]
1298
- Format 5 repositories [always]
1299
Unhashed stores in the repository.
1302
_lock_class = lockable_files.TransportLock
1304
def get_format_string(self):
1305
"""See BzrDirFormat.get_format_string()."""
1306
return "Bazaar-NG branch, format 5\n"
1308
def get_format_description(self):
1309
"""See BzrDirFormat.get_format_description()."""
1310
return "All-in-one format 5"
1312
def get_converter(self, format=None):
1313
"""See BzrDirFormat.get_converter()."""
1314
# there is one and only one upgrade path here.
1315
return ConvertBzrDir5To6()
1317
def _initialize_for_clone(self, url):
1318
return self.initialize_on_transport(get_transport(url), _cloning=True)
1320
def initialize_on_transport(self, transport, _cloning=False):
1321
"""Format 5 dirs always have working tree, branch and repository.
1323
Except when they are being cloned.
1325
from bzrlib.branch import BzrBranchFormat4
1326
from bzrlib.repository import RepositoryFormat5
1327
from bzrlib.workingtree import WorkingTreeFormat2
1328
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1329
RepositoryFormat5().initialize(result, _internal=True)
1331
branch = BzrBranchFormat4().initialize(result)
1333
WorkingTreeFormat2().initialize(result)
1334
except errors.NotLocalUrl:
1335
# Even though we can't access the working tree, we need to
1336
# create its control files.
1337
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1340
def _open(self, transport):
1341
"""See BzrDirFormat._open."""
1342
return BzrDir5(transport, self)
1344
def __return_repository_format(self):
1345
"""Circular import protection."""
1346
from bzrlib.repository import RepositoryFormat5
1347
return RepositoryFormat5()
1348
repository_format = property(__return_repository_format)
1351
class BzrDirFormat6(BzrDirFormat):
1352
"""Bzr control format 6.
1354
This format is a combined format for working tree, branch and repository.
1356
- Format 2 working trees [always]
1357
- Format 4 branches [always]
1358
- Format 6 repositories [always]
1361
_lock_class = lockable_files.TransportLock
1363
def get_format_string(self):
1364
"""See BzrDirFormat.get_format_string()."""
1365
return "Bazaar-NG branch, format 6\n"
1367
def get_format_description(self):
1368
"""See BzrDirFormat.get_format_description()."""
1369
return "All-in-one format 6"
1371
def get_converter(self, format=None):
1372
"""See BzrDirFormat.get_converter()."""
1373
# there is one and only one upgrade path here.
1374
return ConvertBzrDir6ToMeta()
1376
def _initialize_for_clone(self, url):
1377
return self.initialize_on_transport(get_transport(url), _cloning=True)
1379
def initialize_on_transport(self, transport, _cloning=False):
1380
"""Format 6 dirs always have working tree, branch and repository.
1382
Except when they are being cloned.
1384
from bzrlib.branch import BzrBranchFormat4
1385
from bzrlib.repository import RepositoryFormat6
1386
from bzrlib.workingtree import WorkingTreeFormat2
1387
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1388
RepositoryFormat6().initialize(result, _internal=True)
1390
branch = BzrBranchFormat4().initialize(result)
1392
WorkingTreeFormat2().initialize(result)
1393
except errors.NotLocalUrl:
1394
# Even though we can't access the working tree, we need to
1395
# create its control files.
1396
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1399
def _open(self, transport):
1400
"""See BzrDirFormat._open."""
1401
return BzrDir6(transport, self)
1403
def __return_repository_format(self):
1404
"""Circular import protection."""
1405
from bzrlib.repository import RepositoryFormat6
1406
return RepositoryFormat6()
1407
repository_format = property(__return_repository_format)
1410
class BzrDirMetaFormat1(BzrDirFormat):
1411
"""Bzr meta control format 1
1413
This is the first format with split out working tree, branch and repository
1416
- Format 3 working trees [optional]
1417
- Format 5 branches [optional]
1418
- Format 7 repositories [optional]
1421
_lock_class = lockdir.LockDir
1424
self._workingtree_format = None
1426
def __eq__(self, other):
1427
if other.__class__ is not self.__class__:
1429
if other.repository_format != self.repository_format:
1431
if other.workingtree_format != self.workingtree_format:
1435
def get_converter(self, format=None):
1436
"""See BzrDirFormat.get_converter()."""
1438
format = BzrDirFormat.get_default_format()
1439
if not isinstance(self, format.__class__):
1440
# converting away from metadir is not implemented
1441
raise NotImplementedError(self.get_converter)
1442
return ConvertMetaToMeta(format)
1444
def get_format_string(self):
1445
"""See BzrDirFormat.get_format_string()."""
1446
return "Bazaar-NG meta directory, format 1\n"
1448
def get_format_description(self):
1449
"""See BzrDirFormat.get_format_description()."""
1450
return "Meta directory format 1"
1452
def _open(self, transport):
1453
"""See BzrDirFormat._open."""
1454
return BzrDirMeta1(transport, self)
1456
def __return_repository_format(self):
1457
"""Circular import protection."""
1458
if getattr(self, '_repository_format', None):
1459
return self._repository_format
1460
from bzrlib.repository import RepositoryFormat
1461
return RepositoryFormat.get_default_format()
1463
def __set_repository_format(self, value):
1464
"""Allow changint the repository format for metadir formats."""
1465
self._repository_format = value
1467
repository_format = property(__return_repository_format, __set_repository_format)
1469
def __get_workingtree_format(self):
1470
if self._workingtree_format is None:
1471
from bzrlib.workingtree import WorkingTreeFormat
1472
self._workingtree_format = WorkingTreeFormat.get_default_format()
1473
return self._workingtree_format
1475
def __set_workingtree_format(self, wt_format):
1476
self._workingtree_format = wt_format
1478
workingtree_format = property(__get_workingtree_format,
1479
__set_workingtree_format)
1482
BzrDirFormat.register_format(BzrDirFormat4())
1483
BzrDirFormat.register_format(BzrDirFormat5())
1484
BzrDirFormat.register_format(BzrDirFormat6())
1485
__default_format = BzrDirMetaFormat1()
1486
BzrDirFormat.register_format(__default_format)
1487
BzrDirFormat._default_format = __default_format
1490
class BzrDirTestProviderAdapter(object):
1491
"""A tool to generate a suite testing multiple bzrdir formats at once.
1493
This is done by copying the test once for each transport and injecting
1494
the transport_server, transport_readonly_server, and bzrdir_format
1495
classes into each copy. Each copy is also given a new id() to make it
1499
def __init__(self, transport_server, transport_readonly_server, formats):
1500
self._transport_server = transport_server
1501
self._transport_readonly_server = transport_readonly_server
1502
self._formats = formats
1504
def adapt(self, test):
1505
result = unittest.TestSuite()
1506
for format in self._formats:
1507
new_test = deepcopy(test)
1508
new_test.transport_server = self._transport_server
1509
new_test.transport_readonly_server = self._transport_readonly_server
1510
new_test.bzrdir_format = format
1511
def make_new_test_id():
1512
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1513
return lambda: new_id
1514
new_test.id = make_new_test_id()
1515
result.addTest(new_test)
1519
class Converter(object):
1520
"""Converts a disk format object from one format to another."""
1522
def convert(self, to_convert, pb):
1523
"""Perform the conversion of to_convert, giving feedback via pb.
1525
:param to_convert: The disk object to convert.
1526
:param pb: a progress bar to use for progress information.
1529
def step(self, message):
1530
"""Update the pb by a step."""
1532
self.pb.update(message, self.count, self.total)
1535
class ConvertBzrDir4To5(Converter):
1536
"""Converts format 4 bzr dirs to format 5."""
1539
super(ConvertBzrDir4To5, self).__init__()
1540
self.converted_revs = set()
1541
self.absent_revisions = set()
1545
def convert(self, to_convert, pb):
1546
"""See Converter.convert()."""
1547
self.bzrdir = to_convert
1549
self.pb.note('starting upgrade from format 4 to 5')
1550
if isinstance(self.bzrdir.transport, LocalTransport):
1551
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1552
self._convert_to_weaves()
1553
return BzrDir.open(self.bzrdir.root_transport.base)
1555
def _convert_to_weaves(self):
1556
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1559
stat = self.bzrdir.transport.stat('weaves')
1560
if not S_ISDIR(stat.st_mode):
1561
self.bzrdir.transport.delete('weaves')
1562
self.bzrdir.transport.mkdir('weaves')
1563
except errors.NoSuchFile:
1564
self.bzrdir.transport.mkdir('weaves')
1565
# deliberately not a WeaveFile as we want to build it up slowly.
1566
self.inv_weave = Weave('inventory')
1567
# holds in-memory weaves for all files
1568
self.text_weaves = {}
1569
self.bzrdir.transport.delete('branch-format')
1570
self.branch = self.bzrdir.open_branch()
1571
self._convert_working_inv()
1572
rev_history = self.branch.revision_history()
1573
# to_read is a stack holding the revisions we still need to process;
1574
# appending to it adds new highest-priority revisions
1575
self.known_revisions = set(rev_history)
1576
self.to_read = rev_history[-1:]
1578
rev_id = self.to_read.pop()
1579
if (rev_id not in self.revisions
1580
and rev_id not in self.absent_revisions):
1581
self._load_one_rev(rev_id)
1583
to_import = self._make_order()
1584
for i, rev_id in enumerate(to_import):
1585
self.pb.update('converting revision', i, len(to_import))
1586
self._convert_one_rev(rev_id)
1588
self._write_all_weaves()
1589
self._write_all_revs()
1590
self.pb.note('upgraded to weaves:')
1591
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1592
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1593
self.pb.note(' %6d texts', self.text_count)
1594
self._cleanup_spare_files_after_format4()
1595
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1597
def _cleanup_spare_files_after_format4(self):
1598
# FIXME working tree upgrade foo.
1599
for n in 'merged-patches', 'pending-merged-patches':
1601
## assert os.path.getsize(p) == 0
1602
self.bzrdir.transport.delete(n)
1603
except errors.NoSuchFile:
1605
self.bzrdir.transport.delete_tree('inventory-store')
1606
self.bzrdir.transport.delete_tree('text-store')
1608
def _convert_working_inv(self):
1609
inv = xml4.serializer_v4.read_inventory(
1610
self.branch.control_files.get('inventory'))
1611
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1612
# FIXME inventory is a working tree change.
1613
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1615
def _write_all_weaves(self):
1616
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1617
weave_transport = self.bzrdir.transport.clone('weaves')
1618
weaves = WeaveStore(weave_transport, prefixed=False)
1619
transaction = WriteTransaction()
1623
for file_id, file_weave in self.text_weaves.items():
1624
self.pb.update('writing weave', i, len(self.text_weaves))
1625
weaves._put_weave(file_id, file_weave, transaction)
1627
self.pb.update('inventory', 0, 1)
1628
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1629
self.pb.update('inventory', 1, 1)
1633
def _write_all_revs(self):
1634
"""Write all revisions out in new form."""
1635
self.bzrdir.transport.delete_tree('revision-store')
1636
self.bzrdir.transport.mkdir('revision-store')
1637
revision_transport = self.bzrdir.transport.clone('revision-store')
1639
_revision_store = TextRevisionStore(TextStore(revision_transport,
1643
transaction = WriteTransaction()
1644
for i, rev_id in enumerate(self.converted_revs):
1645
self.pb.update('write revision', i, len(self.converted_revs))
1646
_revision_store.add_revision(self.revisions[rev_id], transaction)
1650
def _load_one_rev(self, rev_id):
1651
"""Load a revision object into memory.
1653
Any parents not either loaded or abandoned get queued to be
1655
self.pb.update('loading revision',
1656
len(self.revisions),
1657
len(self.known_revisions))
1658
if not self.branch.repository.has_revision(rev_id):
1660
self.pb.note('revision {%s} not present in branch; '
1661
'will be converted as a ghost',
1663
self.absent_revisions.add(rev_id)
1665
rev = self.branch.repository._revision_store.get_revision(rev_id,
1666
self.branch.repository.get_transaction())
1667
for parent_id in rev.parent_ids:
1668
self.known_revisions.add(parent_id)
1669
self.to_read.append(parent_id)
1670
self.revisions[rev_id] = rev
1672
def _load_old_inventory(self, rev_id):
1673
assert rev_id not in self.converted_revs
1674
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1675
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1676
inv.revision_id = rev_id
1677
rev = self.revisions[rev_id]
1678
if rev.inventory_sha1:
1679
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1680
'inventory sha mismatch for {%s}' % rev_id
1683
def _load_updated_inventory(self, rev_id):
1684
assert rev_id in self.converted_revs
1685
inv_xml = self.inv_weave.get_text(rev_id)
1686
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1689
def _convert_one_rev(self, rev_id):
1690
"""Convert revision and all referenced objects to new format."""
1691
rev = self.revisions[rev_id]
1692
inv = self._load_old_inventory(rev_id)
1693
present_parents = [p for p in rev.parent_ids
1694
if p not in self.absent_revisions]
1695
self._convert_revision_contents(rev, inv, present_parents)
1696
self._store_new_weave(rev, inv, present_parents)
1697
self.converted_revs.add(rev_id)
1699
def _store_new_weave(self, rev, inv, present_parents):
1700
# the XML is now updated with text versions
1702
entries = inv.iter_entries()
1704
for path, ie in entries:
1705
assert getattr(ie, 'revision', None) is not None, \
1706
'no revision on {%s} in {%s}' % \
1707
(file_id, rev.revision_id)
1708
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1709
new_inv_sha1 = sha_string(new_inv_xml)
1710
self.inv_weave.add_lines(rev.revision_id,
1712
new_inv_xml.splitlines(True))
1713
rev.inventory_sha1 = new_inv_sha1
1715
def _convert_revision_contents(self, rev, inv, present_parents):
1716
"""Convert all the files within a revision.
1718
Also upgrade the inventory to refer to the text revision ids."""
1719
rev_id = rev.revision_id
1720
mutter('converting texts of revision {%s}',
1722
parent_invs = map(self._load_updated_inventory, present_parents)
1723
entries = inv.iter_entries()
1725
for path, ie in entries:
1726
self._convert_file_version(rev, ie, parent_invs)
1728
def _convert_file_version(self, rev, ie, parent_invs):
1729
"""Convert one version of one file.
1731
The file needs to be added into the weave if it is a merge
1732
of >=2 parents or if it's changed from its parent.
1734
file_id = ie.file_id
1735
rev_id = rev.revision_id
1736
w = self.text_weaves.get(file_id)
1739
self.text_weaves[file_id] = w
1740
text_changed = False
1741
previous_entries = ie.find_previous_heads(parent_invs,
1745
for old_revision in previous_entries:
1746
# if this fails, its a ghost ?
1747
assert old_revision in self.converted_revs, \
1748
"Revision {%s} not in converted_revs" % old_revision
1749
self.snapshot_ie(previous_entries, ie, w, rev_id)
1751
assert getattr(ie, 'revision', None) is not None
1753
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1754
# TODO: convert this logic, which is ~= snapshot to
1755
# a call to:. This needs the path figured out. rather than a work_tree
1756
# a v4 revision_tree can be given, or something that looks enough like
1757
# one to give the file content to the entry if it needs it.
1758
# and we need something that looks like a weave store for snapshot to
1760
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1761
if len(previous_revisions) == 1:
1762
previous_ie = previous_revisions.values()[0]
1763
if ie._unchanged(previous_ie):
1764
ie.revision = previous_ie.revision
1767
text = self.branch.repository.text_store.get(ie.text_id)
1768
file_lines = text.readlines()
1769
assert sha_strings(file_lines) == ie.text_sha1
1770
assert sum(map(len, file_lines)) == ie.text_size
1771
w.add_lines(rev_id, previous_revisions, file_lines)
1772
self.text_count += 1
1774
w.add_lines(rev_id, previous_revisions, [])
1775
ie.revision = rev_id
1777
def _make_order(self):
1778
"""Return a suitable order for importing revisions.
1780
The order must be such that an revision is imported after all
1781
its (present) parents.
1783
todo = set(self.revisions.keys())
1784
done = self.absent_revisions.copy()
1787
# scan through looking for a revision whose parents
1789
for rev_id in sorted(list(todo)):
1790
rev = self.revisions[rev_id]
1791
parent_ids = set(rev.parent_ids)
1792
if parent_ids.issubset(done):
1793
# can take this one now
1794
order.append(rev_id)
1800
class ConvertBzrDir5To6(Converter):
1801
"""Converts format 5 bzr dirs to format 6."""
1803
def convert(self, to_convert, pb):
1804
"""See Converter.convert()."""
1805
self.bzrdir = to_convert
1807
self.pb.note('starting upgrade from format 5 to 6')
1808
self._convert_to_prefixed()
1809
return BzrDir.open(self.bzrdir.root_transport.base)
1811
def _convert_to_prefixed(self):
1812
from bzrlib.store import TransportStore
1813
self.bzrdir.transport.delete('branch-format')
1814
for store_name in ["weaves", "revision-store"]:
1815
self.pb.note("adding prefixes to %s" % store_name)
1816
store_transport = self.bzrdir.transport.clone(store_name)
1817
store = TransportStore(store_transport, prefixed=True)
1818
for urlfilename in store_transport.list_dir('.'):
1819
filename = urlutils.unescape(urlfilename)
1820
if (filename.endswith(".weave") or
1821
filename.endswith(".gz") or
1822
filename.endswith(".sig")):
1823
file_id = os.path.splitext(filename)[0]
1826
prefix_dir = store.hash_prefix(file_id)
1827
# FIXME keep track of the dirs made RBC 20060121
1829
store_transport.move(filename, prefix_dir + '/' + filename)
1830
except errors.NoSuchFile: # catches missing dirs strangely enough
1831
store_transport.mkdir(prefix_dir)
1832
store_transport.move(filename, prefix_dir + '/' + filename)
1833
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1836
class ConvertBzrDir6ToMeta(Converter):
1837
"""Converts format 6 bzr dirs to metadirs."""
1839
def convert(self, to_convert, pb):
1840
"""See Converter.convert()."""
1841
from bzrlib.branch import BzrBranchFormat5
1842
self.bzrdir = to_convert
1845
self.total = 20 # the steps we know about
1846
self.garbage_inventories = []
1848
self.pb.note('starting upgrade from format 6 to metadir')
1849
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1850
# its faster to move specific files around than to open and use the apis...
1851
# first off, nuke ancestry.weave, it was never used.
1853
self.step('Removing ancestry.weave')
1854
self.bzrdir.transport.delete('ancestry.weave')
1855
except errors.NoSuchFile:
1857
# find out whats there
1858
self.step('Finding branch files')
1859
last_revision = self.bzrdir.open_branch().last_revision()
1860
bzrcontents = self.bzrdir.transport.list_dir('.')
1861
for name in bzrcontents:
1862
if name.startswith('basis-inventory.'):
1863
self.garbage_inventories.append(name)
1864
# create new directories for repository, working tree and branch
1865
self.dir_mode = self.bzrdir._control_files._dir_mode
1866
self.file_mode = self.bzrdir._control_files._file_mode
1867
repository_names = [('inventory.weave', True),
1868
('revision-store', True),
1870
self.step('Upgrading repository ')
1871
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1872
self.make_lock('repository')
1873
# we hard code the formats here because we are converting into
1874
# the meta format. The meta format upgrader can take this to a
1875
# future format within each component.
1876
self.put_format('repository', _mod_repository.RepositoryFormat7())
1877
for entry in repository_names:
1878
self.move_entry('repository', entry)
1880
self.step('Upgrading branch ')
1881
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1882
self.make_lock('branch')
1883
self.put_format('branch', BzrBranchFormat5())
1884
branch_files = [('revision-history', True),
1885
('branch-name', True),
1887
for entry in branch_files:
1888
self.move_entry('branch', entry)
1890
checkout_files = [('pending-merges', True),
1891
('inventory', True),
1892
('stat-cache', False)]
1893
# If a mandatory checkout file is not present, the branch does not have
1894
# a functional checkout. Do not create a checkout in the converted
1896
for name, mandatory in checkout_files:
1897
if mandatory and name not in bzrcontents:
1898
has_checkout = False
1902
if not has_checkout:
1903
self.pb.note('No working tree.')
1904
# If some checkout files are there, we may as well get rid of them.
1905
for name, mandatory in checkout_files:
1906
if name in bzrcontents:
1907
self.bzrdir.transport.delete(name)
1909
from bzrlib.workingtree import WorkingTreeFormat3
1910
self.step('Upgrading working tree')
1911
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1912
self.make_lock('checkout')
1914
'checkout', WorkingTreeFormat3())
1915
self.bzrdir.transport.delete_multi(
1916
self.garbage_inventories, self.pb)
1917
for entry in checkout_files:
1918
self.move_entry('checkout', entry)
1919
if last_revision is not None:
1920
self.bzrdir._control_files.put_utf8(
1921
'checkout/last-revision', last_revision)
1922
self.bzrdir._control_files.put_utf8(
1923
'branch-format', BzrDirMetaFormat1().get_format_string())
1924
return BzrDir.open(self.bzrdir.root_transport.base)
1926
def make_lock(self, name):
1927
"""Make a lock for the new control dir name."""
1928
self.step('Make %s lock' % name)
1929
ld = lockdir.LockDir(self.bzrdir.transport,
1931
file_modebits=self.file_mode,
1932
dir_modebits=self.dir_mode)
1935
def move_entry(self, new_dir, entry):
1936
"""Move then entry name into new_dir."""
1938
mandatory = entry[1]
1939
self.step('Moving %s' % name)
1941
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1942
except errors.NoSuchFile:
1946
def put_format(self, dirname, format):
1947
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1950
class ConvertMetaToMeta(Converter):
1951
"""Converts the components of metadirs."""
1953
def __init__(self, target_format):
1954
"""Create a metadir to metadir converter.
1956
:param target_format: The final metadir format that is desired.
1958
self.target_format = target_format
1960
def convert(self, to_convert, pb):
1961
"""See Converter.convert()."""
1962
self.bzrdir = to_convert
1966
self.step('checking repository format')
1968
repo = self.bzrdir.open_repository()
1969
except errors.NoRepositoryPresent:
1972
if not isinstance(repo._format, self.target_format.repository_format.__class__):
1973
from bzrlib.repository import CopyConverter
1974
self.pb.note('starting repository conversion')
1975
converter = CopyConverter(self.target_format.repository_format)
1976
converter.convert(repo, pb)
1980
class BzrDirFormatInfo(object):
1982
def __init__(self, native, deprecated):
1983
self.deprecated = deprecated
1984
self.native = native
1987
class BzrDirFormatRegistry(registry.Registry):
1988
"""Registry of user-selectable BzrDir subformats.
1990
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
1991
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
1994
def register_metadir(self, key, repo, help, native=True, deprecated=False,
1995
tree='WorkingTreeFormat3'):
1996
"""Register a metadir subformat.
1998
repo is the repository format name as a string.
2000
# This should be expanded to support setting WorkingTree and Branch
2001
# formats, once BzrDirMetaFormat1 supports that.
2003
import bzrlib.repository
2004
import bzrlib.workingtree
2005
repo_format = getattr(bzrlib.repository, repo)
2006
tree_format = getattr(bzrlib.workingtree, tree)
2007
bd = BzrDirMetaFormat1()
2008
bd.repository_format = repo_format()
2009
bd.workingtree_format = tree_format()
2011
self.register(key, helper, help, native, deprecated)
2013
def register(self, key, factory, help, native=True, deprecated=False):
2014
"""Register a BzrDirFormat factory.
2016
The factory must be a callable that takes one parameter: the key.
2017
It must produce an instance of the BzrDirFormat when called.
2019
This function mainly exists to prevent the info object from being
2022
registry.Registry.register(self, key, factory, help,
2023
BzrDirFormatInfo(native, deprecated))
2025
def register_lazy(self, key, module_name, member_name, help, native=True,
2027
registry.Registry.register_lazy(self, key, module_name, member_name,
2028
help, BzrDirFormatInfo(native, deprecated))
2030
def set_default(self, key):
2031
"""Set the 'default' key to be a clone of the supplied key.
2033
This method must be called once and only once.
2035
registry.Registry.register(self, 'default', self.get(key),
2036
self.get_help(key), info=self.get_info(key))
2038
def set_default_repository(self, key):
2039
"""Set the FormatRegistry default and Repository default.
2041
This is a transitional method while Repository.set_default_format
2044
if 'default' in self:
2045
self.remove('default')
2046
self.set_default(key)
2047
format = self.get('default')()
2048
assert isinstance(format, BzrDirMetaFormat1)
2049
from bzrlib import repository
2050
repository.RepositoryFormat._set_default_format(
2051
format.repository_format)
2053
def make_bzrdir(self, key):
2054
return self.get(key)()
2056
def help_topic(self, topic):
2057
output = textwrap.dedent("""\
2058
Bazaar directory formats
2059
------------------------
2061
These formats can be used for creating branches, working trees, and
2065
default_help = self.get_help('default')
2067
for key in self.keys():
2068
if key == 'default':
2070
help = self.get_help(key)
2071
if help == default_help:
2072
default_realkey = key
2074
help_pairs.append((key, help))
2076
def wrapped(key, help, info):
2078
help = '(native) ' + help
2079
return ' %s:\n%s\n\n' % (key,
2080
textwrap.fill(help, initial_indent=' ',
2081
subsequent_indent=' '))
2082
output += wrapped('%s/default' % default_realkey, default_help,
2083
self.get_info('default'))
2084
deprecated_pairs = []
2085
for key, help in help_pairs:
2086
info = self.get_info(key)
2088
deprecated_pairs.append((key, help))
2090
output += wrapped(key, help, info)
2091
if len(deprecated_pairs) > 0:
2092
output += "Deprecated formats\n------------------\n\n"
2093
for key, help in deprecated_pairs:
2094
info = self.get_info(key)
2095
output += wrapped(key, help, info)
2100
format_registry = BzrDirFormatRegistry()
2101
format_registry.register('weave', BzrDirFormat6,
2102
'Pre-0.8 format. Slower than knit and does not'
2103
' support checkouts or shared repositories.', deprecated=True)
2104
format_registry.register_metadir('knit', 'RepositoryFormatKnit1',
2105
'Format using knits. Recommended.')
2106
format_registry.set_default('knit')
2107
format_registry.register_metadir('metaweave', 'RepositoryFormat7',
2108
'Transitional format in 0.8. Slower than knit.',
2110
format_registry.register_metadir('experimental-knit2', 'RepositoryFormatKnit2',
2111
'Experimental successor to knit. Use at your own risk.')
2112
format_registry.register_metadir('experimental-knit3', 'RepositoryFormatKnit2',
2113
'Experimental successor to knit2. Use at your own risk.',
2114
tree='WorkingTreeFormat4')