1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
19
At format 7 this was split out into Branch, Repository and Checkout control
23
# TODO: remove unittest dependency; put that stuff inside the test suite
25
# TODO: Can we move specific formats into separate modules to make this file
28
from cStringIO import StringIO
32
from bzrlib.lazy_import import lazy_import
33
lazy_import(globals(), """
34
from copy import deepcopy
35
from stat import S_ISDIR
45
revision as _mod_revision,
51
from bzrlib.osutils import (
56
from bzrlib.smart.client import SmartClient
57
from bzrlib.store.revision.text import TextRevisionStore
58
from bzrlib.store.text import TextStore
59
from bzrlib.store.versioned import WeaveStore
60
from bzrlib.transactions import WriteTransaction
61
from bzrlib.transport import get_transport
62
from bzrlib.weave import Weave
65
from bzrlib.trace import mutter
66
from bzrlib.transport.local import LocalTransport
70
"""A .bzr control diretory.
72
BzrDir instances let you create or open any of the things that can be
73
found within .bzr - checkouts, branches and repositories.
76
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
78
a transport connected to the directory this bzr was opened from.
82
"""Invoke break_lock on the first object in the bzrdir.
84
If there is a tree, the tree is opened and break_lock() called.
85
Otherwise, branch is tried, and finally repository.
87
# XXX: This seems more like a UI function than something that really
88
# belongs in this class.
90
thing_to_unlock = self.open_workingtree()
91
except (errors.NotLocalUrl, errors.NoWorkingTree):
93
thing_to_unlock = self.open_branch()
94
except errors.NotBranchError:
96
thing_to_unlock = self.open_repository()
97
except errors.NoRepositoryPresent:
99
thing_to_unlock.break_lock()
101
def can_convert_format(self):
102
"""Return true if this bzrdir is one whose format we can convert from."""
105
def check_conversion_target(self, target_format):
106
target_repo_format = target_format.repository_format
107
source_repo_format = self._format.repository_format
108
source_repo_format.check_conversion_target(target_repo_format)
111
def _check_supported(format, allow_unsupported):
112
"""Check whether format is a supported format.
114
If allow_unsupported is True, this is a no-op.
116
if not allow_unsupported and not format.is_supported():
117
# see open_downlevel to open legacy branches.
118
raise errors.UnsupportedFormatError(format=format)
120
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
121
"""Clone this bzrdir and its contents to url verbatim.
123
If urls last component does not exist, it will be created.
125
if revision_id is not None, then the clone operation may tune
126
itself to download less data.
127
:param force_new_repo: Do not use a shared repository for the target
128
even if one is available.
131
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
132
result = self._format.initialize(url)
134
local_repo = self.find_repository()
135
except errors.NoRepositoryPresent:
138
# may need to copy content in
140
result_repo = local_repo.clone(
142
revision_id=revision_id,
144
result_repo.set_make_working_trees(local_repo.make_working_trees())
147
result_repo = result.find_repository()
148
# fetch content this dir needs.
150
# XXX FIXME RBC 20060214 need tests for this when the basis
152
result_repo.fetch(basis_repo, revision_id=revision_id)
153
result_repo.fetch(local_repo, revision_id=revision_id)
154
except errors.NoRepositoryPresent:
155
# needed to make one anyway.
156
result_repo = local_repo.clone(
158
revision_id=revision_id,
160
result_repo.set_make_working_trees(local_repo.make_working_trees())
161
# 1 if there is a branch present
162
# make sure its content is available in the target repository
165
self.open_branch().clone(result, revision_id=revision_id)
166
except errors.NotBranchError:
169
self.open_workingtree().clone(result, basis=basis_tree)
170
except (errors.NoWorkingTree, errors.NotLocalUrl):
174
def _get_basis_components(self, basis):
175
"""Retrieve the basis components that are available at basis."""
177
return None, None, None
179
basis_tree = basis.open_workingtree()
180
basis_branch = basis_tree.branch
181
basis_repo = basis_branch.repository
182
except (errors.NoWorkingTree, errors.NotLocalUrl):
185
basis_branch = basis.open_branch()
186
basis_repo = basis_branch.repository
187
except errors.NotBranchError:
190
basis_repo = basis.open_repository()
191
except errors.NoRepositoryPresent:
193
return basis_repo, basis_branch, basis_tree
195
# TODO: This should be given a Transport, and should chdir up; otherwise
196
# this will open a new connection.
197
def _make_tail(self, url):
198
head, tail = urlutils.split(url)
199
if tail and tail != '.':
200
t = get_transport(head)
203
except errors.FileExists:
206
# TODO: Should take a Transport
208
def create(cls, base, format=None):
209
"""Create a new BzrDir at the url 'base'.
211
This will call the current default formats initialize with base
212
as the only parameter.
214
:param format: If supplied, the format of branch to create. If not
215
supplied, the default is used.
217
if cls is not BzrDir:
218
raise AssertionError("BzrDir.create always creates the default"
219
" format, not one of %r" % cls)
220
head, tail = urlutils.split(base)
221
if tail and tail != '.':
222
t = get_transport(head)
225
except errors.FileExists:
228
format = BzrDirFormat.get_default_format()
229
return format.initialize(safe_unicode(base))
231
def create_branch(self):
232
"""Create a branch in this BzrDir.
234
The bzrdirs format will control what branch format is created.
235
For more control see BranchFormatXX.create(a_bzrdir).
237
raise NotImplementedError(self.create_branch)
240
def create_branch_and_repo(base, force_new_repo=False, format=None):
241
"""Create a new BzrDir, Branch and Repository at the url 'base'.
243
This will use the current default BzrDirFormat, and use whatever
244
repository format that that uses via bzrdir.create_branch and
245
create_repository. If a shared repository is available that is used
248
The created Branch object is returned.
250
:param base: The URL to create the branch at.
251
:param force_new_repo: If True a new repository is always created.
253
bzrdir = BzrDir.create(base, format)
254
bzrdir._find_or_create_repository(force_new_repo)
255
return bzrdir.create_branch()
257
def _find_or_create_repository(self, force_new_repo):
258
"""Create a new repository if needed, returning the repository."""
260
return self.create_repository()
262
return self.find_repository()
263
except errors.NoRepositoryPresent:
264
return self.create_repository()
267
def create_branch_convenience(base, force_new_repo=False,
268
force_new_tree=None, format=None):
269
"""Create a new BzrDir, Branch and Repository at the url 'base'.
271
This is a convenience function - it will use an existing repository
272
if possible, can be told explicitly whether to create a working tree or
275
This will use the current default BzrDirFormat, and use whatever
276
repository format that that uses via bzrdir.create_branch and
277
create_repository. If a shared repository is available that is used
278
preferentially. Whatever repository is used, its tree creation policy
281
The created Branch object is returned.
282
If a working tree cannot be made due to base not being a file:// url,
283
no error is raised unless force_new_tree is True, in which case no
284
data is created on disk and NotLocalUrl is raised.
286
:param base: The URL to create the branch at.
287
:param force_new_repo: If True a new repository is always created.
288
:param force_new_tree: If True or False force creation of a tree or
289
prevent such creation respectively.
290
:param format: Override for the for the bzrdir format to create
293
# check for non local urls
294
t = get_transport(safe_unicode(base))
295
if not isinstance(t, LocalTransport):
296
raise errors.NotLocalUrl(base)
297
bzrdir = BzrDir.create(base, format)
298
repo = bzrdir._find_or_create_repository(force_new_repo)
299
result = bzrdir.create_branch()
300
if force_new_tree or (repo.make_working_trees() and
301
force_new_tree is None):
303
bzrdir.create_workingtree()
304
except errors.NotLocalUrl:
309
def create_repository(base, shared=False, format=None):
310
"""Create a new BzrDir and Repository at the url 'base'.
312
If no format is supplied, this will default to the current default
313
BzrDirFormat by default, and use whatever repository format that that
314
uses for bzrdirformat.create_repository.
316
:param shared: Create a shared repository rather than a standalone
318
The Repository object is returned.
320
This must be overridden as an instance method in child classes, where
321
it should take no parameters and construct whatever repository format
322
that child class desires.
324
bzrdir = BzrDir.create(base, format)
325
return bzrdir.create_repository(shared)
328
def create_standalone_workingtree(base, format=None):
329
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
331
'base' must be a local path or a file:// url.
333
This will use the current default BzrDirFormat, and use whatever
334
repository format that that uses for bzrdirformat.create_workingtree,
335
create_branch and create_repository.
337
:return: The WorkingTree object.
339
t = get_transport(safe_unicode(base))
340
if not isinstance(t, LocalTransport):
341
raise errors.NotLocalUrl(base)
342
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
344
format=format).bzrdir
345
return bzrdir.create_workingtree()
347
def create_workingtree(self, revision_id=None):
348
"""Create a working tree at this BzrDir.
350
revision_id: create it as of this revision id.
352
raise NotImplementedError(self.create_workingtree)
354
def destroy_workingtree(self):
355
"""Destroy the working tree at this BzrDir.
357
Formats that do not support this may raise UnsupportedOperation.
359
raise NotImplementedError(self.destroy_workingtree)
361
def destroy_workingtree_metadata(self):
362
"""Destroy the control files for the working tree at this BzrDir.
364
The contents of working tree files are not affected.
365
Formats that do not support this may raise UnsupportedOperation.
367
raise NotImplementedError(self.destroy_workingtree_metadata)
369
def find_repository(self):
370
"""Find the repository that should be used for a_bzrdir.
372
This does not require a branch as we use it to find the repo for
373
new branches as well as to hook existing branches up to their
377
return self.open_repository()
378
except errors.NoRepositoryPresent:
380
next_transport = self.root_transport.clone('..')
382
# find the next containing bzrdir
384
found_bzrdir = BzrDir.open_containing_from_transport(
386
except errors.NotBranchError:
388
raise errors.NoRepositoryPresent(self)
389
# does it have a repository ?
391
repository = found_bzrdir.open_repository()
392
except errors.NoRepositoryPresent:
393
next_transport = found_bzrdir.root_transport.clone('..')
394
if (found_bzrdir.root_transport.base == next_transport.base):
395
# top of the file system
399
if ((found_bzrdir.root_transport.base ==
400
self.root_transport.base) or repository.is_shared()):
403
raise errors.NoRepositoryPresent(self)
404
raise errors.NoRepositoryPresent(self)
406
def get_branch_reference(self):
407
"""Return the referenced URL for the branch in this bzrdir.
409
:raises NotBranchError: If there is no Branch.
410
:return: The URL the branch in this bzrdir references if it is a
411
reference branch, or None for regular branches.
415
def get_branch_transport(self, branch_format):
416
"""Get the transport for use by branch format in this BzrDir.
418
Note that bzr dirs that do not support format strings will raise
419
IncompatibleFormat if the branch format they are given has
420
a format string, and vice versa.
422
If branch_format is None, the transport is returned with no
423
checking. if it is not None, then the returned transport is
424
guaranteed to point to an existing directory ready for use.
426
raise NotImplementedError(self.get_branch_transport)
428
def get_repository_transport(self, repository_format):
429
"""Get the transport for use by repository format in this BzrDir.
431
Note that bzr dirs that do not support format strings will raise
432
IncompatibleFormat if the repository format they are given has
433
a format string, and vice versa.
435
If repository_format is None, the transport is returned with no
436
checking. if it is not None, then the returned transport is
437
guaranteed to point to an existing directory ready for use.
439
raise NotImplementedError(self.get_repository_transport)
441
def get_workingtree_transport(self, tree_format):
442
"""Get the transport for use by workingtree format in this BzrDir.
444
Note that bzr dirs that do not support format strings will raise
445
IncompatibleFormat if the workingtree format they are given has
446
a format string, and vice versa.
448
If workingtree_format is None, the transport is returned with no
449
checking. if it is not None, then the returned transport is
450
guaranteed to point to an existing directory ready for use.
452
raise NotImplementedError(self.get_workingtree_transport)
454
def __init__(self, _transport, _format):
455
"""Initialize a Bzr control dir object.
457
Only really common logic should reside here, concrete classes should be
458
made with varying behaviours.
460
:param _format: the format that is creating this BzrDir instance.
461
:param _transport: the transport this dir is based at.
463
self._format = _format
464
self.transport = _transport.clone('.bzr')
465
self.root_transport = _transport
467
def is_control_filename(self, filename):
468
"""True if filename is the name of a path which is reserved for bzrdir's.
470
:param filename: A filename within the root transport of this bzrdir.
472
This is true IF and ONLY IF the filename is part of the namespace reserved
473
for bzr control dirs. Currently this is the '.bzr' directory in the root
474
of the root_transport. it is expected that plugins will need to extend
475
this in the future - for instance to make bzr talk with svn working
478
# this might be better on the BzrDirFormat class because it refers to
479
# all the possible bzrdir disk formats.
480
# This method is tested via the workingtree is_control_filename tests-
481
# it was extracted from WorkingTree.is_control_filename. If the methods
482
# contract is extended beyond the current trivial implementation please
483
# add new tests for it to the appropriate place.
484
return filename == '.bzr' or filename.startswith('.bzr/')
486
def needs_format_conversion(self, format=None):
487
"""Return true if this bzrdir needs convert_format run on it.
489
For instance, if the repository format is out of date but the
490
branch and working tree are not, this should return True.
492
:param format: Optional parameter indicating a specific desired
493
format we plan to arrive at.
495
raise NotImplementedError(self.needs_format_conversion)
498
def open_unsupported(base):
499
"""Open a branch which is not supported."""
500
return BzrDir.open(base, _unsupported=True)
503
def open(base, _unsupported=False):
504
"""Open an existing bzrdir, rooted at 'base' (url)
506
_unsupported is a private parameter to the BzrDir class.
508
t = get_transport(base)
509
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
512
def open_from_transport(transport, _unsupported=False):
513
"""Open a bzrdir within a particular directory.
515
:param transport: Transport containing the bzrdir.
516
:param _unsupported: private.
518
format = BzrDirFormat.find_format(transport)
519
BzrDir._check_supported(format, _unsupported)
520
return format.open(transport, _found=True)
522
def open_branch(self, unsupported=False):
523
"""Open the branch object at this BzrDir if one is present.
525
If unsupported is True, then no longer supported branch formats can
528
TODO: static convenience version of this?
530
raise NotImplementedError(self.open_branch)
533
def open_containing(url):
534
"""Open an existing branch which contains url.
536
:param url: url to search from.
537
See open_containing_from_transport for more detail.
539
return BzrDir.open_containing_from_transport(get_transport(url))
542
def open_containing_from_transport(a_transport):
543
"""Open an existing branch which contains a_transport.base
545
This probes for a branch at a_transport, and searches upwards from there.
547
Basically we keep looking up until we find the control directory or
548
run into the root. If there isn't one, raises NotBranchError.
549
If there is one and it is either an unrecognised format or an unsupported
550
format, UnknownFormatError or UnsupportedFormatError are raised.
551
If there is one, it is returned, along with the unused portion of url.
553
:return: The BzrDir that contains the path, and a Unicode path
554
for the rest of the URL.
556
# this gets the normalised url back. I.e. '.' -> the full path.
557
url = a_transport.base
560
result = BzrDir.open_from_transport(a_transport)
561
return result, urlutils.unescape(a_transport.relpath(url))
562
except errors.NotBranchError, e:
565
new_t = a_transport.clone('..')
566
except errors.InvalidURLJoin:
567
# reached the root, whatever that may be
568
raise errors.NotBranchError(path=url)
569
if new_t.base == a_transport.base:
570
# reached the root, whatever that may be
571
raise errors.NotBranchError(path=url)
575
def open_containing_tree_or_branch(klass, location):
576
"""Return the branch and working tree contained by a location.
578
Returns (tree, branch, relpath).
579
If there is no tree at containing the location, tree will be None.
580
If there is no branch containing the location, an exception will be
582
relpath is the portion of the path that is contained by the branch.
584
bzrdir, relpath = klass.open_containing(location)
586
tree = bzrdir.open_workingtree()
587
except (errors.NoWorkingTree, errors.NotLocalUrl):
589
branch = bzrdir.open_branch()
592
return tree, branch, relpath
594
def open_repository(self, _unsupported=False):
595
"""Open the repository object at this BzrDir if one is present.
597
This will not follow the Branch object pointer - its strictly a direct
598
open facility. Most client code should use open_branch().repository to
601
_unsupported is a private parameter, not part of the api.
602
TODO: static convenience version of this?
604
raise NotImplementedError(self.open_repository)
606
def open_workingtree(self, _unsupported=False):
607
"""Open the workingtree object at this BzrDir if one is present.
609
TODO: static convenience version of this?
611
raise NotImplementedError(self.open_workingtree)
613
def has_branch(self):
614
"""Tell if this bzrdir contains a branch.
616
Note: if you're going to open the branch, you should just go ahead
617
and try, and not ask permission first. (This method just opens the
618
branch and discards it, and that's somewhat expensive.)
623
except errors.NotBranchError:
626
def has_workingtree(self):
627
"""Tell if this bzrdir contains a working tree.
629
This will still raise an exception if the bzrdir has a workingtree that
630
is remote & inaccessible.
632
Note: if you're going to open the working tree, you should just go ahead
633
and try, and not ask permission first. (This method just opens the
634
workingtree and discards it, and that's somewhat expensive.)
637
self.open_workingtree()
639
except errors.NoWorkingTree:
642
def cloning_metadir(self, basis=None):
643
"""Produce a metadir suitable for cloning with"""
644
def related_repository(bzrdir):
646
branch = bzrdir.open_branch()
647
return branch.repository
648
except errors.NotBranchError:
650
return bzrdir.open_repository()
651
result_format = self._format.__class__()
654
source_repository = related_repository(self)
655
except errors.NoRepositoryPresent:
658
source_repository = related_repository(self)
659
result_format.repository_format = source_repository._format
660
except errors.NoRepositoryPresent:
664
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
665
"""Create a copy of this bzrdir prepared for use as a new line of
668
If urls last component does not exist, it will be created.
670
Attributes related to the identity of the source branch like
671
branch nickname will be cleaned, a working tree is created
672
whether one existed before or not; and a local branch is always
675
if revision_id is not None, then the clone operation may tune
676
itself to download less data.
679
cloning_format = self.cloning_metadir(basis)
680
result = cloning_format.initialize(url)
681
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
683
source_branch = self.open_branch()
684
source_repository = source_branch.repository
685
except errors.NotBranchError:
688
source_repository = self.open_repository()
689
except errors.NoRepositoryPresent:
690
# copy the entire basis one if there is one
691
# but there is no repository.
692
source_repository = basis_repo
697
result_repo = result.find_repository()
698
except errors.NoRepositoryPresent:
700
if source_repository is None and result_repo is not None:
702
elif source_repository is None and result_repo is None:
703
# no repo available, make a new one
704
result.create_repository()
705
elif source_repository is not None and result_repo is None:
706
# have source, and want to make a new target repo
707
# we don't clone the repo because that preserves attributes
708
# like is_shared(), and we have not yet implemented a
709
# repository sprout().
710
result_repo = result.create_repository()
711
if result_repo is not None:
712
# fetch needed content into target.
714
# XXX FIXME RBC 20060214 need tests for this when the basis
716
result_repo.fetch(basis_repo, revision_id=revision_id)
717
if source_repository is not None:
718
result_repo.fetch(source_repository, revision_id=revision_id)
719
if source_branch is not None:
720
source_branch.sprout(result, revision_id=revision_id)
722
result.create_branch()
723
# TODO: jam 20060426 we probably need a test in here in the
724
# case that the newly sprouted branch is a remote one
725
if result_repo is None or result_repo.make_working_trees():
726
wt = result.create_workingtree()
727
if wt.inventory.root is None:
729
wt.set_root_id(self.open_workingtree.get_root_id())
730
except errors.NoWorkingTree:
735
class BzrDirPreSplitOut(BzrDir):
736
"""A common class for the all-in-one formats."""
738
def __init__(self, _transport, _format):
739
"""See BzrDir.__init__."""
740
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
741
assert self._format._lock_class == lockable_files.TransportLock
742
assert self._format._lock_file_name == 'branch-lock'
743
self._control_files = lockable_files.LockableFiles(
744
self.get_branch_transport(None),
745
self._format._lock_file_name,
746
self._format._lock_class)
748
def break_lock(self):
749
"""Pre-splitout bzrdirs do not suffer from stale locks."""
750
raise NotImplementedError(self.break_lock)
752
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
753
"""See BzrDir.clone()."""
754
from bzrlib.workingtree import WorkingTreeFormat2
756
result = self._format._initialize_for_clone(url)
757
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
758
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
759
from_branch = self.open_branch()
760
from_branch.clone(result, revision_id=revision_id)
762
self.open_workingtree().clone(result, basis=basis_tree)
763
except errors.NotLocalUrl:
764
# make a new one, this format always has to have one.
766
WorkingTreeFormat2().initialize(result)
767
except errors.NotLocalUrl:
768
# but we cannot do it for remote trees.
769
to_branch = result.open_branch()
770
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
773
def create_branch(self):
774
"""See BzrDir.create_branch."""
775
return self.open_branch()
777
def create_repository(self, shared=False):
778
"""See BzrDir.create_repository."""
780
raise errors.IncompatibleFormat('shared repository', self._format)
781
return self.open_repository()
783
def create_workingtree(self, revision_id=None):
784
"""See BzrDir.create_workingtree."""
785
# this looks buggy but is not -really-
786
# clone and sprout will have set the revision_id
787
# and that will have set it for us, its only
788
# specific uses of create_workingtree in isolation
789
# that can do wonky stuff here, and that only
790
# happens for creating checkouts, which cannot be
791
# done on this format anyway. So - acceptable wart.
792
result = self.open_workingtree()
793
if revision_id is not None:
794
if revision_id == _mod_revision.NULL_REVISION:
795
result.set_parent_ids([])
797
result.set_parent_ids([revision_id])
800
def destroy_workingtree(self):
801
"""See BzrDir.destroy_workingtree."""
802
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
804
def destroy_workingtree_metadata(self):
805
"""See BzrDir.destroy_workingtree_metadata."""
806
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
809
def get_branch_transport(self, branch_format):
810
"""See BzrDir.get_branch_transport()."""
811
if branch_format is None:
812
return self.transport
814
branch_format.get_format_string()
815
except NotImplementedError:
816
return self.transport
817
raise errors.IncompatibleFormat(branch_format, self._format)
819
def get_repository_transport(self, repository_format):
820
"""See BzrDir.get_repository_transport()."""
821
if repository_format is None:
822
return self.transport
824
repository_format.get_format_string()
825
except NotImplementedError:
826
return self.transport
827
raise errors.IncompatibleFormat(repository_format, self._format)
829
def get_workingtree_transport(self, workingtree_format):
830
"""See BzrDir.get_workingtree_transport()."""
831
if workingtree_format is None:
832
return self.transport
834
workingtree_format.get_format_string()
835
except NotImplementedError:
836
return self.transport
837
raise errors.IncompatibleFormat(workingtree_format, self._format)
839
def needs_format_conversion(self, format=None):
840
"""See BzrDir.needs_format_conversion()."""
841
# if the format is not the same as the system default,
842
# an upgrade is needed.
844
format = BzrDirFormat.get_default_format()
845
return not isinstance(self._format, format.__class__)
847
def open_branch(self, unsupported=False):
848
"""See BzrDir.open_branch."""
849
from bzrlib.branch import BzrBranchFormat4
850
format = BzrBranchFormat4()
851
self._check_supported(format, unsupported)
852
return format.open(self, _found=True)
854
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
855
"""See BzrDir.sprout()."""
856
from bzrlib.workingtree import WorkingTreeFormat2
858
result = self._format._initialize_for_clone(url)
859
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
861
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
862
except errors.NoRepositoryPresent:
865
self.open_branch().sprout(result, revision_id=revision_id)
866
except errors.NotBranchError:
868
# we always want a working tree
869
WorkingTreeFormat2().initialize(result)
873
class BzrDir4(BzrDirPreSplitOut):
874
"""A .bzr version 4 control object.
876
This is a deprecated format and may be removed after sept 2006.
879
def create_repository(self, shared=False):
880
"""See BzrDir.create_repository."""
881
return self._format.repository_format.initialize(self, shared)
883
def needs_format_conversion(self, format=None):
884
"""Format 4 dirs are always in need of conversion."""
887
def open_repository(self):
888
"""See BzrDir.open_repository."""
889
from bzrlib.repofmt.weaverepo import RepositoryFormat4
890
return RepositoryFormat4().open(self, _found=True)
893
class BzrDir5(BzrDirPreSplitOut):
894
"""A .bzr version 5 control object.
896
This is a deprecated format and may be removed after sept 2006.
899
def open_repository(self):
900
"""See BzrDir.open_repository."""
901
from bzrlib.repofmt.weaverepo import RepositoryFormat5
902
return RepositoryFormat5().open(self, _found=True)
904
def open_workingtree(self, _unsupported=False):
905
"""See BzrDir.create_workingtree."""
906
from bzrlib.workingtree import WorkingTreeFormat2
907
return WorkingTreeFormat2().open(self, _found=True)
910
class BzrDir6(BzrDirPreSplitOut):
911
"""A .bzr version 6 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 RepositoryFormat6
919
return RepositoryFormat6().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 BzrDirMeta1(BzrDir):
928
"""A .bzr meta version 1 control object.
930
This is the first control object where the
931
individual aspects are really split out: there are separate repository,
932
workingtree and branch subdirectories and any subset of the three can be
933
present within a BzrDir.
936
def can_convert_format(self):
937
"""See BzrDir.can_convert_format()."""
940
def create_branch(self):
941
"""See BzrDir.create_branch."""
942
return self._format.get_branch_format().initialize(self)
944
def create_repository(self, shared=False):
945
"""See BzrDir.create_repository."""
946
return self._format.repository_format.initialize(self, shared)
948
def create_workingtree(self, revision_id=None):
949
"""See BzrDir.create_workingtree."""
950
from bzrlib.workingtree import WorkingTreeFormat
951
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
953
def destroy_workingtree(self):
954
"""See BzrDir.destroy_workingtree."""
955
wt = self.open_workingtree()
956
repository = wt.branch.repository
957
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
958
wt.revert([], old_tree=empty)
959
self.destroy_workingtree_metadata()
961
def destroy_workingtree_metadata(self):
962
self.transport.delete_tree('checkout')
964
def _get_mkdir_mode(self):
965
"""Figure out the mode to use when creating a bzrdir subdir."""
966
temp_control = lockable_files.LockableFiles(self.transport, '',
967
lockable_files.TransportLock)
968
return temp_control._dir_mode
970
def get_branch_reference(self):
971
"""See BzrDir.get_branch_reference()."""
972
from bzrlib.branch import BranchFormat
973
format = BranchFormat.find_format(self)
974
return format.get_reference(self)
976
def get_branch_transport(self, branch_format):
977
"""See BzrDir.get_branch_transport()."""
978
if branch_format is None:
979
return self.transport.clone('branch')
981
branch_format.get_format_string()
982
except NotImplementedError:
983
raise errors.IncompatibleFormat(branch_format, self._format)
985
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
986
except errors.FileExists:
988
return self.transport.clone('branch')
990
def get_repository_transport(self, repository_format):
991
"""See BzrDir.get_repository_transport()."""
992
if repository_format is None:
993
return self.transport.clone('repository')
995
repository_format.get_format_string()
996
except NotImplementedError:
997
raise errors.IncompatibleFormat(repository_format, self._format)
999
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1000
except errors.FileExists:
1002
return self.transport.clone('repository')
1004
def get_workingtree_transport(self, workingtree_format):
1005
"""See BzrDir.get_workingtree_transport()."""
1006
if workingtree_format is None:
1007
return self.transport.clone('checkout')
1009
workingtree_format.get_format_string()
1010
except NotImplementedError:
1011
raise errors.IncompatibleFormat(workingtree_format, self._format)
1013
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1014
except errors.FileExists:
1016
return self.transport.clone('checkout')
1018
def needs_format_conversion(self, format=None):
1019
"""See BzrDir.needs_format_conversion()."""
1021
format = BzrDirFormat.get_default_format()
1022
if not isinstance(self._format, format.__class__):
1023
# it is not a meta dir format, conversion is needed.
1025
# we might want to push this down to the repository?
1027
if not isinstance(self.open_repository()._format,
1028
format.repository_format.__class__):
1029
# the repository needs an upgrade.
1031
except errors.NoRepositoryPresent:
1034
if not isinstance(self.open_branch()._format,
1035
format.get_branch_format().__class__):
1036
# the repository needs an upgrade.
1038
except errors.NotBranchError:
1040
# currently there are no other possible conversions for meta1 formats.
1043
def open_branch(self, unsupported=False):
1044
"""See BzrDir.open_branch."""
1045
from bzrlib.branch import BranchFormat
1046
format = BranchFormat.find_format(self)
1047
self._check_supported(format, unsupported)
1048
return format.open(self, _found=True)
1050
def open_repository(self, unsupported=False):
1051
"""See BzrDir.open_repository."""
1052
from bzrlib.repository import RepositoryFormat
1053
format = RepositoryFormat.find_format(self)
1054
self._check_supported(format, unsupported)
1055
return format.open(self, _found=True)
1057
def open_workingtree(self, unsupported=False):
1058
"""See BzrDir.open_workingtree."""
1059
from bzrlib.workingtree import WorkingTreeFormat
1060
format = WorkingTreeFormat.find_format(self)
1061
self._check_supported(format, unsupported)
1062
return format.open(self, _found=True)
1065
class BzrDirFormat(object):
1066
"""An encapsulation of the initialization and open routines for a format.
1068
Formats provide three things:
1069
* An initialization routine,
1073
Formats are placed in an dict by their format string for reference
1074
during bzrdir opening. These should be subclasses of BzrDirFormat
1077
Once a format is deprecated, just deprecate the initialize and open
1078
methods on the format class. Do not deprecate the object, as the
1079
object will be created every system load.
1082
_default_format = None
1083
"""The default format used for new .bzr dirs."""
1086
"""The known formats."""
1088
_control_formats = []
1089
"""The registered control formats - .bzr, ....
1091
This is a list of BzrDirFormat objects.
1094
_lock_file_name = 'branch-lock'
1096
# _lock_class must be set in subclasses to the lock type, typ.
1097
# TransportLock or LockDir
1100
def find_format(klass, transport):
1101
"""Return the format present at transport."""
1102
for format in klass._control_formats:
1104
return format.probe_transport(transport)
1105
except errors.NotBranchError:
1106
# this format does not find a control dir here.
1108
raise errors.NotBranchError(path=transport.base)
1111
def probe_transport(klass, transport):
1112
"""Return the .bzrdir style format present in a directory."""
1114
format_string = transport.get(".bzr/branch-format").read()
1115
except errors.NoSuchFile:
1116
raise errors.NotBranchError(path=transport.base)
1119
return klass._formats[format_string]
1121
raise errors.UnknownFormatError(format=format_string)
1124
def get_default_format(klass):
1125
"""Return the current default format."""
1126
return klass._default_format
1128
def get_format_string(self):
1129
"""Return the ASCII format string that identifies this format."""
1130
raise NotImplementedError(self.get_format_string)
1132
def get_format_description(self):
1133
"""Return the short description for this format."""
1134
raise NotImplementedError(self.get_format_description)
1136
def get_converter(self, format=None):
1137
"""Return the converter to use to convert bzrdirs needing converts.
1139
This returns a bzrlib.bzrdir.Converter object.
1141
This should return the best upgrader to step this format towards the
1142
current default format. In the case of plugins we can/should provide
1143
some means for them to extend the range of returnable converters.
1145
:param format: Optional format to override the default format of the
1148
raise NotImplementedError(self.get_converter)
1150
def initialize(self, url):
1151
"""Create a bzr control dir at this url and return an opened copy.
1153
Subclasses should typically override initialize_on_transport
1154
instead of this method.
1156
return self.initialize_on_transport(get_transport(url))
1158
def initialize_on_transport(self, transport):
1159
"""Initialize a new bzrdir in the base directory of a Transport."""
1160
# Since we don't have a .bzr directory, inherit the
1161
# mode from the root directory
1162
temp_control = lockable_files.LockableFiles(transport,
1163
'', lockable_files.TransportLock)
1164
temp_control._transport.mkdir('.bzr',
1165
# FIXME: RBC 20060121 don't peek under
1167
mode=temp_control._dir_mode)
1168
file_mode = temp_control._file_mode
1170
mutter('created control directory in ' + transport.base)
1171
control = transport.clone('.bzr')
1172
utf8_files = [('README',
1173
"This is a Bazaar-NG control directory.\n"
1174
"Do not change any files in this directory.\n"),
1175
('branch-format', self.get_format_string()),
1177
# NB: no need to escape relative paths that are url safe.
1178
control_files = lockable_files.LockableFiles(control,
1179
self._lock_file_name, self._lock_class)
1180
control_files.create_lock()
1181
control_files.lock_write()
1183
for file, content in utf8_files:
1184
control_files.put_utf8(file, content)
1186
control_files.unlock()
1187
return self.open(transport, _found=True)
1189
def is_supported(self):
1190
"""Is this format supported?
1192
Supported formats must be initializable and openable.
1193
Unsupported formats may not support initialization or committing or
1194
some other features depending on the reason for not being supported.
1198
def same_model(self, target_format):
1199
return (self.repository_format.rich_root_data ==
1200
target_format.rich_root_data)
1203
def known_formats(klass):
1204
"""Return all the known formats.
1206
Concrete formats should override _known_formats.
1208
# There is double indirection here to make sure that control
1209
# formats used by more than one dir format will only be probed
1210
# once. This can otherwise be quite expensive for remote connections.
1212
for format in klass._control_formats:
1213
result.update(format._known_formats())
1217
def _known_formats(klass):
1218
"""Return the known format instances for this control format."""
1219
return set(klass._formats.values())
1221
def open(self, transport, _found=False):
1222
"""Return an instance of this format for the dir transport points at.
1224
_found is a private parameter, do not use it.
1227
found_format = BzrDirFormat.find_format(transport)
1228
if not isinstance(found_format, self.__class__):
1229
raise AssertionError("%s was asked to open %s, but it seems to need "
1231
% (self, transport, found_format))
1232
return self._open(transport)
1234
def _open(self, transport):
1235
"""Template method helper for opening BzrDirectories.
1237
This performs the actual open and any additional logic or parameter
1240
raise NotImplementedError(self._open)
1243
def register_format(klass, format):
1244
klass._formats[format.get_format_string()] = format
1247
def register_control_format(klass, format):
1248
"""Register a format that does not use '.bzrdir' for its control dir.
1250
TODO: This should be pulled up into a 'ControlDirFormat' base class
1251
which BzrDirFormat can inherit from, and renamed to register_format
1252
there. It has been done without that for now for simplicity of
1255
klass._control_formats.append(format)
1258
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1259
def set_default_format(klass, format):
1260
klass._set_default_format(format)
1263
def _set_default_format(klass, format):
1264
"""Set default format (for testing behavior of defaults only)"""
1265
klass._default_format = format
1268
return self.get_format_string()[:-1]
1271
def unregister_format(klass, format):
1272
assert klass._formats[format.get_format_string()] is format
1273
del klass._formats[format.get_format_string()]
1276
def unregister_control_format(klass, format):
1277
klass._control_formats.remove(format)
1280
# register BzrDirFormat as a control format
1281
BzrDirFormat.register_control_format(BzrDirFormat)
1284
class BzrDirFormat4(BzrDirFormat):
1285
"""Bzr dir format 4.
1287
This format is a combined format for working tree, branch and repository.
1289
- Format 1 working trees [always]
1290
- Format 4 branches [always]
1291
- Format 4 repositories [always]
1293
This format is deprecated: it indexes texts using a text it which is
1294
removed in format 5; write support for this format has been removed.
1297
_lock_class = lockable_files.TransportLock
1299
def get_format_string(self):
1300
"""See BzrDirFormat.get_format_string()."""
1301
return "Bazaar-NG branch, format 0.0.4\n"
1303
def get_format_description(self):
1304
"""See BzrDirFormat.get_format_description()."""
1305
return "All-in-one format 4"
1307
def get_converter(self, format=None):
1308
"""See BzrDirFormat.get_converter()."""
1309
# there is one and only one upgrade path here.
1310
return ConvertBzrDir4To5()
1312
def initialize_on_transport(self, transport):
1313
"""Format 4 branches cannot be created."""
1314
raise errors.UninitializableFormat(self)
1316
def is_supported(self):
1317
"""Format 4 is not supported.
1319
It is not supported because the model changed from 4 to 5 and the
1320
conversion logic is expensive - so doing it on the fly was not
1325
def _open(self, transport):
1326
"""See BzrDirFormat._open."""
1327
return BzrDir4(transport, self)
1329
def __return_repository_format(self):
1330
"""Circular import protection."""
1331
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1332
return RepositoryFormat4()
1333
repository_format = property(__return_repository_format)
1336
class BzrDirFormat5(BzrDirFormat):
1337
"""Bzr control format 5.
1339
This format is a combined format for working tree, branch and repository.
1341
- Format 2 working trees [always]
1342
- Format 4 branches [always]
1343
- Format 5 repositories [always]
1344
Unhashed stores in the repository.
1347
_lock_class = lockable_files.TransportLock
1349
def get_format_string(self):
1350
"""See BzrDirFormat.get_format_string()."""
1351
return "Bazaar-NG branch, format 5\n"
1353
def get_format_description(self):
1354
"""See BzrDirFormat.get_format_description()."""
1355
return "All-in-one format 5"
1357
def get_converter(self, format=None):
1358
"""See BzrDirFormat.get_converter()."""
1359
# there is one and only one upgrade path here.
1360
return ConvertBzrDir5To6()
1362
def _initialize_for_clone(self, url):
1363
return self.initialize_on_transport(get_transport(url), _cloning=True)
1365
def initialize_on_transport(self, transport, _cloning=False):
1366
"""Format 5 dirs always have working tree, branch and repository.
1368
Except when they are being cloned.
1370
from bzrlib.branch import BzrBranchFormat4
1371
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1372
from bzrlib.workingtree import WorkingTreeFormat2
1373
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1374
RepositoryFormat5().initialize(result, _internal=True)
1376
branch = BzrBranchFormat4().initialize(result)
1378
WorkingTreeFormat2().initialize(result)
1379
except errors.NotLocalUrl:
1380
# Even though we can't access the working tree, we need to
1381
# create its control files.
1382
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1385
def _open(self, transport):
1386
"""See BzrDirFormat._open."""
1387
return BzrDir5(transport, self)
1389
def __return_repository_format(self):
1390
"""Circular import protection."""
1391
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1392
return RepositoryFormat5()
1393
repository_format = property(__return_repository_format)
1396
class BzrDirFormat6(BzrDirFormat):
1397
"""Bzr control format 6.
1399
This format is a combined format for working tree, branch and repository.
1401
- Format 2 working trees [always]
1402
- Format 4 branches [always]
1403
- Format 6 repositories [always]
1406
_lock_class = lockable_files.TransportLock
1408
def get_format_string(self):
1409
"""See BzrDirFormat.get_format_string()."""
1410
return "Bazaar-NG branch, format 6\n"
1412
def get_format_description(self):
1413
"""See BzrDirFormat.get_format_description()."""
1414
return "All-in-one format 6"
1416
def get_converter(self, format=None):
1417
"""See BzrDirFormat.get_converter()."""
1418
# there is one and only one upgrade path here.
1419
return ConvertBzrDir6ToMeta()
1421
def _initialize_for_clone(self, url):
1422
return self.initialize_on_transport(get_transport(url), _cloning=True)
1424
def initialize_on_transport(self, transport, _cloning=False):
1425
"""Format 6 dirs always have working tree, branch and repository.
1427
Except when they are being cloned.
1429
from bzrlib.branch import BzrBranchFormat4
1430
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1431
from bzrlib.workingtree import WorkingTreeFormat2
1432
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1433
RepositoryFormat6().initialize(result, _internal=True)
1435
branch = BzrBranchFormat4().initialize(result)
1437
WorkingTreeFormat2().initialize(result)
1438
except errors.NotLocalUrl:
1439
# Even though we can't access the working tree, we need to
1440
# create its control files.
1441
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1444
def _open(self, transport):
1445
"""See BzrDirFormat._open."""
1446
return BzrDir6(transport, self)
1448
def __return_repository_format(self):
1449
"""Circular import protection."""
1450
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1451
return RepositoryFormat6()
1452
repository_format = property(__return_repository_format)
1455
class BzrDirMetaFormat1(BzrDirFormat):
1456
"""Bzr meta control format 1
1458
This is the first format with split out working tree, branch and repository
1461
- Format 3 working trees [optional]
1462
- Format 5 branches [optional]
1463
- Format 7 repositories [optional]
1466
_lock_class = lockdir.LockDir
1469
self._branch_format = None
1471
def get_branch_format(self):
1472
if self._branch_format is None:
1473
from bzrlib.branch import BranchFormat
1474
self._branch_format = BranchFormat.get_default_format()
1475
return self._branch_format
1477
def set_branch_format(self, format):
1478
self._branch_format = format
1480
def get_converter(self, format=None):
1481
"""See BzrDirFormat.get_converter()."""
1483
format = BzrDirFormat.get_default_format()
1484
if not isinstance(self, format.__class__):
1485
# converting away from metadir is not implemented
1486
raise NotImplementedError(self.get_converter)
1487
return ConvertMetaToMeta(format)
1489
def get_format_string(self):
1490
"""See BzrDirFormat.get_format_string()."""
1491
return "Bazaar-NG meta directory, format 1\n"
1493
def get_format_description(self):
1494
"""See BzrDirFormat.get_format_description()."""
1495
return "Meta directory format 1"
1497
def _open(self, transport):
1498
"""See BzrDirFormat._open."""
1499
return BzrDirMeta1(transport, self)
1501
def __return_repository_format(self):
1502
"""Circular import protection."""
1503
if getattr(self, '_repository_format', None):
1504
return self._repository_format
1505
from bzrlib.repository import RepositoryFormat
1506
return RepositoryFormat.get_default_format()
1508
def __set_repository_format(self, value):
1509
"""Allow changint the repository format for metadir formats."""
1510
self._repository_format = value
1512
repository_format = property(__return_repository_format, __set_repository_format)
1515
BzrDirFormat.register_format(BzrDirFormat4())
1516
BzrDirFormat.register_format(BzrDirFormat5())
1517
BzrDirFormat.register_format(BzrDirFormat6())
1518
__default_format = BzrDirMetaFormat1()
1519
BzrDirFormat.register_format(__default_format)
1520
BzrDirFormat._default_format = __default_format
1523
class BzrDirTestProviderAdapter(object):
1524
"""A tool to generate a suite testing multiple bzrdir formats at once.
1526
This is done by copying the test once for each transport and injecting
1527
the transport_server, transport_readonly_server, and bzrdir_format
1528
classes into each copy. Each copy is also given a new id() to make it
1532
def __init__(self, vfs_factory, transport_server, transport_readonly_server,
1534
"""Create an object to adapt tests.
1536
:param vfs_server: A factory to create a Transport Server which has
1537
all the VFS methods working, and is writable.
1539
self._vfs_factory = vfs_factory
1540
self._transport_server = transport_server
1541
self._transport_readonly_server = transport_readonly_server
1542
self._formats = formats
1544
def adapt(self, test):
1545
result = unittest.TestSuite()
1546
for format in self._formats:
1547
new_test = deepcopy(test)
1548
new_test.vfs_transport_factory = self._vfs_factory
1549
new_test.transport_server = self._transport_server
1550
new_test.transport_readonly_server = self._transport_readonly_server
1551
new_test.bzrdir_format = format
1552
def make_new_test_id():
1553
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1554
return lambda: new_id
1555
new_test.id = make_new_test_id()
1556
result.addTest(new_test)
1560
class Converter(object):
1561
"""Converts a disk format object from one format to another."""
1563
def convert(self, to_convert, pb):
1564
"""Perform the conversion of to_convert, giving feedback via pb.
1566
:param to_convert: The disk object to convert.
1567
:param pb: a progress bar to use for progress information.
1570
def step(self, message):
1571
"""Update the pb by a step."""
1573
self.pb.update(message, self.count, self.total)
1576
class ConvertBzrDir4To5(Converter):
1577
"""Converts format 4 bzr dirs to format 5."""
1580
super(ConvertBzrDir4To5, self).__init__()
1581
self.converted_revs = set()
1582
self.absent_revisions = set()
1586
def convert(self, to_convert, pb):
1587
"""See Converter.convert()."""
1588
self.bzrdir = to_convert
1590
self.pb.note('starting upgrade from format 4 to 5')
1591
if isinstance(self.bzrdir.transport, LocalTransport):
1592
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1593
self._convert_to_weaves()
1594
return BzrDir.open(self.bzrdir.root_transport.base)
1596
def _convert_to_weaves(self):
1597
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1600
stat = self.bzrdir.transport.stat('weaves')
1601
if not S_ISDIR(stat.st_mode):
1602
self.bzrdir.transport.delete('weaves')
1603
self.bzrdir.transport.mkdir('weaves')
1604
except errors.NoSuchFile:
1605
self.bzrdir.transport.mkdir('weaves')
1606
# deliberately not a WeaveFile as we want to build it up slowly.
1607
self.inv_weave = Weave('inventory')
1608
# holds in-memory weaves for all files
1609
self.text_weaves = {}
1610
self.bzrdir.transport.delete('branch-format')
1611
self.branch = self.bzrdir.open_branch()
1612
self._convert_working_inv()
1613
rev_history = self.branch.revision_history()
1614
# to_read is a stack holding the revisions we still need to process;
1615
# appending to it adds new highest-priority revisions
1616
self.known_revisions = set(rev_history)
1617
self.to_read = rev_history[-1:]
1619
rev_id = self.to_read.pop()
1620
if (rev_id not in self.revisions
1621
and rev_id not in self.absent_revisions):
1622
self._load_one_rev(rev_id)
1624
to_import = self._make_order()
1625
for i, rev_id in enumerate(to_import):
1626
self.pb.update('converting revision', i, len(to_import))
1627
self._convert_one_rev(rev_id)
1629
self._write_all_weaves()
1630
self._write_all_revs()
1631
self.pb.note('upgraded to weaves:')
1632
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1633
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1634
self.pb.note(' %6d texts', self.text_count)
1635
self._cleanup_spare_files_after_format4()
1636
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1638
def _cleanup_spare_files_after_format4(self):
1639
# FIXME working tree upgrade foo.
1640
for n in 'merged-patches', 'pending-merged-patches':
1642
## assert os.path.getsize(p) == 0
1643
self.bzrdir.transport.delete(n)
1644
except errors.NoSuchFile:
1646
self.bzrdir.transport.delete_tree('inventory-store')
1647
self.bzrdir.transport.delete_tree('text-store')
1649
def _convert_working_inv(self):
1650
inv = xml4.serializer_v4.read_inventory(
1651
self.branch.control_files.get('inventory'))
1652
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1653
# FIXME inventory is a working tree change.
1654
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1656
def _write_all_weaves(self):
1657
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1658
weave_transport = self.bzrdir.transport.clone('weaves')
1659
weaves = WeaveStore(weave_transport, prefixed=False)
1660
transaction = WriteTransaction()
1664
for file_id, file_weave in self.text_weaves.items():
1665
self.pb.update('writing weave', i, len(self.text_weaves))
1666
weaves._put_weave(file_id, file_weave, transaction)
1668
self.pb.update('inventory', 0, 1)
1669
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1670
self.pb.update('inventory', 1, 1)
1674
def _write_all_revs(self):
1675
"""Write all revisions out in new form."""
1676
self.bzrdir.transport.delete_tree('revision-store')
1677
self.bzrdir.transport.mkdir('revision-store')
1678
revision_transport = self.bzrdir.transport.clone('revision-store')
1680
_revision_store = TextRevisionStore(TextStore(revision_transport,
1684
transaction = WriteTransaction()
1685
for i, rev_id in enumerate(self.converted_revs):
1686
self.pb.update('write revision', i, len(self.converted_revs))
1687
_revision_store.add_revision(self.revisions[rev_id], transaction)
1691
def _load_one_rev(self, rev_id):
1692
"""Load a revision object into memory.
1694
Any parents not either loaded or abandoned get queued to be
1696
self.pb.update('loading revision',
1697
len(self.revisions),
1698
len(self.known_revisions))
1699
if not self.branch.repository.has_revision(rev_id):
1701
self.pb.note('revision {%s} not present in branch; '
1702
'will be converted as a ghost',
1704
self.absent_revisions.add(rev_id)
1706
rev = self.branch.repository._revision_store.get_revision(rev_id,
1707
self.branch.repository.get_transaction())
1708
for parent_id in rev.parent_ids:
1709
self.known_revisions.add(parent_id)
1710
self.to_read.append(parent_id)
1711
self.revisions[rev_id] = rev
1713
def _load_old_inventory(self, rev_id):
1714
assert rev_id not in self.converted_revs
1715
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1716
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1717
inv.revision_id = rev_id
1718
rev = self.revisions[rev_id]
1719
if rev.inventory_sha1:
1720
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1721
'inventory sha mismatch for {%s}' % rev_id
1724
def _load_updated_inventory(self, rev_id):
1725
assert rev_id in self.converted_revs
1726
inv_xml = self.inv_weave.get_text(rev_id)
1727
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1730
def _convert_one_rev(self, rev_id):
1731
"""Convert revision and all referenced objects to new format."""
1732
rev = self.revisions[rev_id]
1733
inv = self._load_old_inventory(rev_id)
1734
present_parents = [p for p in rev.parent_ids
1735
if p not in self.absent_revisions]
1736
self._convert_revision_contents(rev, inv, present_parents)
1737
self._store_new_weave(rev, inv, present_parents)
1738
self.converted_revs.add(rev_id)
1740
def _store_new_weave(self, rev, inv, present_parents):
1741
# the XML is now updated with text versions
1743
entries = inv.iter_entries()
1745
for path, ie in entries:
1746
assert getattr(ie, 'revision', None) is not None, \
1747
'no revision on {%s} in {%s}' % \
1748
(file_id, rev.revision_id)
1749
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1750
new_inv_sha1 = sha_string(new_inv_xml)
1751
self.inv_weave.add_lines(rev.revision_id,
1753
new_inv_xml.splitlines(True))
1754
rev.inventory_sha1 = new_inv_sha1
1756
def _convert_revision_contents(self, rev, inv, present_parents):
1757
"""Convert all the files within a revision.
1759
Also upgrade the inventory to refer to the text revision ids."""
1760
rev_id = rev.revision_id
1761
mutter('converting texts of revision {%s}',
1763
parent_invs = map(self._load_updated_inventory, present_parents)
1764
entries = inv.iter_entries()
1766
for path, ie in entries:
1767
self._convert_file_version(rev, ie, parent_invs)
1769
def _convert_file_version(self, rev, ie, parent_invs):
1770
"""Convert one version of one file.
1772
The file needs to be added into the weave if it is a merge
1773
of >=2 parents or if it's changed from its parent.
1775
file_id = ie.file_id
1776
rev_id = rev.revision_id
1777
w = self.text_weaves.get(file_id)
1780
self.text_weaves[file_id] = w
1781
text_changed = False
1782
previous_entries = ie.find_previous_heads(parent_invs,
1786
for old_revision in previous_entries:
1787
# if this fails, its a ghost ?
1788
assert old_revision in self.converted_revs, \
1789
"Revision {%s} not in converted_revs" % old_revision
1790
self.snapshot_ie(previous_entries, ie, w, rev_id)
1792
assert getattr(ie, 'revision', None) is not None
1794
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1795
# TODO: convert this logic, which is ~= snapshot to
1796
# a call to:. This needs the path figured out. rather than a work_tree
1797
# a v4 revision_tree can be given, or something that looks enough like
1798
# one to give the file content to the entry if it needs it.
1799
# and we need something that looks like a weave store for snapshot to
1801
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1802
if len(previous_revisions) == 1:
1803
previous_ie = previous_revisions.values()[0]
1804
if ie._unchanged(previous_ie):
1805
ie.revision = previous_ie.revision
1808
text = self.branch.repository.text_store.get(ie.text_id)
1809
file_lines = text.readlines()
1810
assert sha_strings(file_lines) == ie.text_sha1
1811
assert sum(map(len, file_lines)) == ie.text_size
1812
w.add_lines(rev_id, previous_revisions, file_lines)
1813
self.text_count += 1
1815
w.add_lines(rev_id, previous_revisions, [])
1816
ie.revision = rev_id
1818
def _make_order(self):
1819
"""Return a suitable order for importing revisions.
1821
The order must be such that an revision is imported after all
1822
its (present) parents.
1824
todo = set(self.revisions.keys())
1825
done = self.absent_revisions.copy()
1828
# scan through looking for a revision whose parents
1830
for rev_id in sorted(list(todo)):
1831
rev = self.revisions[rev_id]
1832
parent_ids = set(rev.parent_ids)
1833
if parent_ids.issubset(done):
1834
# can take this one now
1835
order.append(rev_id)
1841
class ConvertBzrDir5To6(Converter):
1842
"""Converts format 5 bzr dirs to format 6."""
1844
def convert(self, to_convert, pb):
1845
"""See Converter.convert()."""
1846
self.bzrdir = to_convert
1848
self.pb.note('starting upgrade from format 5 to 6')
1849
self._convert_to_prefixed()
1850
return BzrDir.open(self.bzrdir.root_transport.base)
1852
def _convert_to_prefixed(self):
1853
from bzrlib.store import TransportStore
1854
self.bzrdir.transport.delete('branch-format')
1855
for store_name in ["weaves", "revision-store"]:
1856
self.pb.note("adding prefixes to %s" % store_name)
1857
store_transport = self.bzrdir.transport.clone(store_name)
1858
store = TransportStore(store_transport, prefixed=True)
1859
for urlfilename in store_transport.list_dir('.'):
1860
filename = urlutils.unescape(urlfilename)
1861
if (filename.endswith(".weave") or
1862
filename.endswith(".gz") or
1863
filename.endswith(".sig")):
1864
file_id = os.path.splitext(filename)[0]
1867
prefix_dir = store.hash_prefix(file_id)
1868
# FIXME keep track of the dirs made RBC 20060121
1870
store_transport.move(filename, prefix_dir + '/' + filename)
1871
except errors.NoSuchFile: # catches missing dirs strangely enough
1872
store_transport.mkdir(prefix_dir)
1873
store_transport.move(filename, prefix_dir + '/' + filename)
1874
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1877
class ConvertBzrDir6ToMeta(Converter):
1878
"""Converts format 6 bzr dirs to metadirs."""
1880
def convert(self, to_convert, pb):
1881
"""See Converter.convert()."""
1882
from bzrlib.repofmt.weaverepo import RepositoryFormat7
1883
from bzrlib.branch import BzrBranchFormat5
1884
self.bzrdir = to_convert
1887
self.total = 20 # the steps we know about
1888
self.garbage_inventories = []
1890
self.pb.note('starting upgrade from format 6 to metadir')
1891
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1892
# its faster to move specific files around than to open and use the apis...
1893
# first off, nuke ancestry.weave, it was never used.
1895
self.step('Removing ancestry.weave')
1896
self.bzrdir.transport.delete('ancestry.weave')
1897
except errors.NoSuchFile:
1899
# find out whats there
1900
self.step('Finding branch files')
1901
last_revision = self.bzrdir.open_branch().last_revision()
1902
bzrcontents = self.bzrdir.transport.list_dir('.')
1903
for name in bzrcontents:
1904
if name.startswith('basis-inventory.'):
1905
self.garbage_inventories.append(name)
1906
# create new directories for repository, working tree and branch
1907
self.dir_mode = self.bzrdir._control_files._dir_mode
1908
self.file_mode = self.bzrdir._control_files._file_mode
1909
repository_names = [('inventory.weave', True),
1910
('revision-store', True),
1912
self.step('Upgrading repository ')
1913
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1914
self.make_lock('repository')
1915
# we hard code the formats here because we are converting into
1916
# the meta format. The meta format upgrader can take this to a
1917
# future format within each component.
1918
self.put_format('repository', RepositoryFormat7())
1919
for entry in repository_names:
1920
self.move_entry('repository', entry)
1922
self.step('Upgrading branch ')
1923
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1924
self.make_lock('branch')
1925
self.put_format('branch', BzrBranchFormat5())
1926
branch_files = [('revision-history', True),
1927
('branch-name', True),
1929
for entry in branch_files:
1930
self.move_entry('branch', entry)
1932
checkout_files = [('pending-merges', True),
1933
('inventory', True),
1934
('stat-cache', False)]
1935
# If a mandatory checkout file is not present, the branch does not have
1936
# a functional checkout. Do not create a checkout in the converted
1938
for name, mandatory in checkout_files:
1939
if mandatory and name not in bzrcontents:
1940
has_checkout = False
1944
if not has_checkout:
1945
self.pb.note('No working tree.')
1946
# If some checkout files are there, we may as well get rid of them.
1947
for name, mandatory in checkout_files:
1948
if name in bzrcontents:
1949
self.bzrdir.transport.delete(name)
1951
from bzrlib.workingtree import WorkingTreeFormat3
1952
self.step('Upgrading working tree')
1953
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1954
self.make_lock('checkout')
1956
'checkout', WorkingTreeFormat3())
1957
self.bzrdir.transport.delete_multi(
1958
self.garbage_inventories, self.pb)
1959
for entry in checkout_files:
1960
self.move_entry('checkout', entry)
1961
if last_revision is not None:
1962
self.bzrdir._control_files.put_utf8(
1963
'checkout/last-revision', last_revision)
1964
self.bzrdir._control_files.put_utf8(
1965
'branch-format', BzrDirMetaFormat1().get_format_string())
1966
return BzrDir.open(self.bzrdir.root_transport.base)
1968
def make_lock(self, name):
1969
"""Make a lock for the new control dir name."""
1970
self.step('Make %s lock' % name)
1971
ld = lockdir.LockDir(self.bzrdir.transport,
1973
file_modebits=self.file_mode,
1974
dir_modebits=self.dir_mode)
1977
def move_entry(self, new_dir, entry):
1978
"""Move then entry name into new_dir."""
1980
mandatory = entry[1]
1981
self.step('Moving %s' % name)
1983
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1984
except errors.NoSuchFile:
1988
def put_format(self, dirname, format):
1989
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
1992
class ConvertMetaToMeta(Converter):
1993
"""Converts the components of metadirs."""
1995
def __init__(self, target_format):
1996
"""Create a metadir to metadir converter.
1998
:param target_format: The final metadir format that is desired.
2000
self.target_format = target_format
2002
def convert(self, to_convert, pb):
2003
"""See Converter.convert()."""
2004
self.bzrdir = to_convert
2008
self.step('checking repository format')
2010
repo = self.bzrdir.open_repository()
2011
except errors.NoRepositoryPresent:
2014
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2015
from bzrlib.repository import CopyConverter
2016
self.pb.note('starting repository conversion')
2017
converter = CopyConverter(self.target_format.repository_format)
2018
converter.convert(repo, pb)
2020
branch = self.bzrdir.open_branch()
2021
except errors.NotBranchError:
2024
# Avoid circular imports
2025
from bzrlib import branch as _mod_branch
2026
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2027
self.target_format.get_branch_format().__class__ is
2028
_mod_branch.BzrBranchFormat6):
2029
branch_converter = _mod_branch.Converter5to6()
2030
branch_converter.convert(branch)
2034
# This is not in remote.py because it's small, and needs to be registered.
2035
# Putting it in remote.py creates a circular import problem.
2036
# we can make it a lazy object if the control formats is turned into something
2038
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2039
"""Format representing bzrdirs accessed via a smart server"""
2041
def get_format_description(self):
2042
return 'bzr remote bzrdir'
2045
def probe_transport(klass, transport):
2046
"""Return a RemoteBzrDirFormat object if it looks possible."""
2048
transport.get_smart_client()
2049
except (NotImplementedError, AttributeError,
2050
errors.TransportNotPossible):
2051
# no smart server, so not a branch for this format type.
2052
raise errors.NotBranchError(path=transport.base)
2056
def initialize_on_transport(self, transport):
2057
# hand off the request to the smart server
2058
medium = transport.get_smart_medium()
2059
client = SmartClient(medium)
2060
path = client.remote_path_from_transport(transport)
2061
response = SmartClient(medium).call('BzrDirFormat.initialize', path)
2062
assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
2063
return remote.RemoteBzrDir(transport)
2065
def _open(self, transport):
2066
return remote.RemoteBzrDir(transport)
2068
def __eq__(self, other):
2069
if not isinstance(other, RemoteBzrDirFormat):
2071
return self.get_format_description() == other.get_format_description()
2074
# We can't use register_control_format because it adds it at a lower priority
2075
# than the existing branches, whereas this should take priority.
2076
BzrDirFormat._control_formats.insert(0, RemoteBzrDirFormat)
2079
class BzrDirFormatInfo(object):
2081
def __init__(self, native, deprecated):
2082
self.deprecated = deprecated
2083
self.native = native
2086
class BzrDirFormatRegistry(registry.Registry):
2087
"""Registry of user-selectable BzrDir subformats.
2089
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2090
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2093
def register_metadir(self, key, repo, help, native=True, deprecated=False,
2094
branch_format=None):
2095
"""Register a metadir subformat.
2097
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2098
by the Repository format.
2100
:param repo: The fully-qualified repository format class name as a
2103
# This should be expanded to support setting WorkingTree and Branch
2104
# formats, once BzrDirMetaFormat1 supports that.
2106
import bzrlib.branch
2107
mod_name, repo_factory_name = repo.rsplit('.', 1)
2109
mod = __import__(mod_name, globals(), locals(),
2110
[repo_factory_name])
2111
except ImportError, e:
2112
raise ImportError('failed to load repository %s: %s'
2115
repo_format_class = getattr(mod, repo_factory_name)
2116
except AttributeError:
2117
raise AttributeError('no repository format %r in module %r'
2119
bd = BzrDirMetaFormat1()
2120
bd.repository_format = repo_format_class()
2121
if branch_format is not None:
2122
bd.set_branch_format(getattr(bzrlib.branch, branch_format)())
2124
self.register(key, helper, help, native, deprecated)
2126
def register(self, key, factory, help, native=True, deprecated=False):
2127
"""Register a BzrDirFormat factory.
2129
The factory must be a callable that takes one parameter: the key.
2130
It must produce an instance of the BzrDirFormat when called.
2132
This function mainly exists to prevent the info object from being
2135
registry.Registry.register(self, key, factory, help,
2136
BzrDirFormatInfo(native, deprecated))
2138
def register_lazy(self, key, module_name, member_name, help, native=True,
2140
registry.Registry.register_lazy(self, key, module_name, member_name,
2141
help, BzrDirFormatInfo(native, deprecated))
2143
def set_default(self, key):
2144
"""Set the 'default' key to be a clone of the supplied key.
2146
This method must be called once and only once.
2148
registry.Registry.register(self, 'default', self.get(key),
2149
self.get_help(key), info=self.get_info(key))
2151
def set_default_repository(self, key):
2152
"""Set the FormatRegistry default and Repository default.
2154
This is a transitional method while Repository.set_default_format
2157
if 'default' in self:
2158
self.remove('default')
2159
self.set_default(key)
2160
format = self.get('default')()
2161
assert isinstance(format, BzrDirMetaFormat1)
2163
def make_bzrdir(self, key):
2164
return self.get(key)()
2166
def help_topic(self, topic):
2167
output = textwrap.dedent("""\
2168
Bazaar directory formats
2169
------------------------
2171
These formats can be used for creating branches, working trees, and
2175
default_help = self.get_help('default')
2177
for key in self.keys():
2178
if key == 'default':
2180
help = self.get_help(key)
2181
if help == default_help:
2182
default_realkey = key
2184
help_pairs.append((key, help))
2186
def wrapped(key, help, info):
2188
help = '(native) ' + help
2189
return ' %s:\n%s\n\n' % (key,
2190
textwrap.fill(help, initial_indent=' ',
2191
subsequent_indent=' '))
2192
output += wrapped('%s/default' % default_realkey, default_help,
2193
self.get_info('default'))
2194
deprecated_pairs = []
2195
for key, help in help_pairs:
2196
info = self.get_info(key)
2198
deprecated_pairs.append((key, help))
2200
output += wrapped(key, help, info)
2201
if len(deprecated_pairs) > 0:
2202
output += "Deprecated formats\n------------------\n\n"
2203
for key, help in deprecated_pairs:
2204
info = self.get_info(key)
2205
output += wrapped(key, help, info)
2210
format_registry = BzrDirFormatRegistry()
2211
format_registry.register('weave', BzrDirFormat6,
2212
'Pre-0.8 format. Slower than knit and does not'
2213
' support checkouts or shared repositories.',
2215
format_registry.register_metadir('knit',
2216
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2217
'Format using knits. Recommended.',
2218
branch_format='BzrBranchFormat5')
2219
format_registry.set_default('knit')
2220
format_registry.register_metadir('metaweave',
2221
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2222
'Transitional format in 0.8. Slower than knit.',
2225
format_registry.register_metadir('experimental-knit2',
2226
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
2227
'Experimental successor to knit. Use at your own risk.',
2228
branch_format='BzrBranchFormat5')
2229
format_registry.register_metadir('experimental-branch6',
2230
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2231
'Experimental successor to knit. Use at your own risk.',
2232
branch_format='BzrBranchFormat6')