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 (
71
from bzrlib.transport.local import LocalTransport
75
"""A .bzr control diretory.
77
BzrDir instances let you create or open any of the things that can be
78
found within .bzr - checkouts, branches and repositories.
81
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
83
a transport connected to the directory this bzr was opened from.
87
"""Invoke break_lock on the first object in the bzrdir.
89
If there is a tree, the tree is opened and break_lock() called.
90
Otherwise, branch is tried, and finally repository.
93
thing_to_unlock = self.open_workingtree()
94
except (errors.NotLocalUrl, errors.NoWorkingTree):
96
thing_to_unlock = self.open_branch()
97
except errors.NotBranchError:
99
thing_to_unlock = self.open_repository()
100
except errors.NoRepositoryPresent:
102
thing_to_unlock.break_lock()
104
def can_convert_format(self):
105
"""Return true if this bzrdir is one whose format we can convert from."""
108
def check_conversion_target(self, target_format):
109
target_repo_format = target_format.repository_format
110
source_repo_format = self._format.repository_format
111
source_repo_format.check_conversion_target(target_repo_format)
114
def _check_supported(format, allow_unsupported):
115
"""Check whether format is a supported format.
117
If allow_unsupported is True, this is a no-op.
119
if not allow_unsupported and not format.is_supported():
120
# see open_downlevel to open legacy branches.
121
raise errors.UnsupportedFormatError(format=format)
123
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
124
"""Clone this bzrdir and its contents to url verbatim.
126
If urls last component does not exist, it will be created.
128
if revision_id is not None, then the clone operation may tune
129
itself to download less data.
130
:param force_new_repo: Do not use a shared repository for the target
131
even if one is available.
134
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
135
result = self._format.initialize(url)
137
local_repo = self.find_repository()
138
except errors.NoRepositoryPresent:
141
# may need to copy content in
143
result_repo = local_repo.clone(
145
revision_id=revision_id,
147
result_repo.set_make_working_trees(local_repo.make_working_trees())
150
result_repo = result.find_repository()
151
# fetch content this dir needs.
153
# XXX FIXME RBC 20060214 need tests for this when the basis
155
result_repo.fetch(basis_repo, revision_id=revision_id)
156
result_repo.fetch(local_repo, revision_id=revision_id)
157
except errors.NoRepositoryPresent:
158
# needed to make one anyway.
159
result_repo = local_repo.clone(
161
revision_id=revision_id,
163
result_repo.set_make_working_trees(local_repo.make_working_trees())
164
# 1 if there is a branch present
165
# make sure its content is available in the target repository
168
self.open_branch().clone(result, revision_id=revision_id)
169
except errors.NotBranchError:
172
self.open_workingtree().clone(result, basis=basis_tree)
173
except (errors.NoWorkingTree, errors.NotLocalUrl):
177
def _get_basis_components(self, basis):
178
"""Retrieve the basis components that are available at basis."""
180
return None, None, None
182
basis_tree = basis.open_workingtree()
183
basis_branch = basis_tree.branch
184
basis_repo = basis_branch.repository
185
except (errors.NoWorkingTree, errors.NotLocalUrl):
188
basis_branch = basis.open_branch()
189
basis_repo = basis_branch.repository
190
except errors.NotBranchError:
193
basis_repo = basis.open_repository()
194
except errors.NoRepositoryPresent:
196
return basis_repo, basis_branch, basis_tree
198
# TODO: This should be given a Transport, and should chdir up; otherwise
199
# this will open a new connection.
200
def _make_tail(self, url):
201
head, tail = urlutils.split(url)
202
if tail and tail != '.':
203
t = get_transport(head)
206
except errors.FileExists:
209
# TODO: Should take a Transport
211
def create(cls, base, format=None):
212
"""Create a new BzrDir at the url 'base'.
214
This will call the current default formats initialize with base
215
as the only parameter.
217
:param format: If supplied, the format of branch to create. If not
218
supplied, the default is used.
220
if cls is not BzrDir:
221
raise AssertionError("BzrDir.create always creates the default"
222
" format, not one of %r" % cls)
223
head, tail = urlutils.split(base)
224
if tail and tail != '.':
225
t = get_transport(head)
228
except errors.FileExists:
231
format = BzrDirFormat.get_default_format()
232
return format.initialize(safe_unicode(base))
234
def create_branch(self):
235
"""Create a branch in this BzrDir.
237
The bzrdirs format will control what branch format is created.
238
For more control see BranchFormatXX.create(a_bzrdir).
240
raise NotImplementedError(self.create_branch)
243
def create_branch_and_repo(base, force_new_repo=False, format=None):
244
"""Create a new BzrDir, Branch and Repository at the url 'base'.
246
This will use the current default BzrDirFormat, and use whatever
247
repository format that that uses via bzrdir.create_branch and
248
create_repository. If a shared repository is available that is used
251
The created Branch object is returned.
253
:param base: The URL to create the branch at.
254
:param force_new_repo: If True a new repository is always created.
256
bzrdir = BzrDir.create(base, format)
257
bzrdir._find_or_create_repository(force_new_repo)
258
return bzrdir.create_branch()
260
def _find_or_create_repository(self, force_new_repo):
261
"""Create a new repository if needed, returning the repository."""
263
return self.create_repository()
265
return self.find_repository()
266
except errors.NoRepositoryPresent:
267
return self.create_repository()
270
def create_branch_convenience(base, force_new_repo=False,
271
force_new_tree=None, format=None):
272
"""Create a new BzrDir, Branch and Repository at the url 'base'.
274
This is a convenience function - it will use an existing repository
275
if possible, can be told explicitly whether to create a working tree or
278
This will use the current default BzrDirFormat, and use whatever
279
repository format that that uses via bzrdir.create_branch and
280
create_repository. If a shared repository is available that is used
281
preferentially. Whatever repository is used, its tree creation policy
284
The created Branch object is returned.
285
If a working tree cannot be made due to base not being a file:// url,
286
no error is raised unless force_new_tree is True, in which case no
287
data is created on disk and NotLocalUrl is raised.
289
:param base: The URL to create the branch at.
290
:param force_new_repo: If True a new repository is always created.
291
:param force_new_tree: If True or False force creation of a tree or
292
prevent such creation respectively.
293
:param format: Override for the for the bzrdir format to create
296
# check for non local urls
297
t = get_transport(safe_unicode(base))
298
if not isinstance(t, LocalTransport):
299
raise errors.NotLocalUrl(base)
300
bzrdir = BzrDir.create(base, format)
301
repo = bzrdir._find_or_create_repository(force_new_repo)
302
result = bzrdir.create_branch()
303
if force_new_tree or (repo.make_working_trees() and
304
force_new_tree is None):
306
bzrdir.create_workingtree()
307
except errors.NotLocalUrl:
312
def create_repository(base, shared=False, format=None):
313
"""Create a new BzrDir and Repository at the url 'base'.
315
If no format is supplied, this will default to the current default
316
BzrDirFormat by default, and use whatever repository format that that
317
uses for bzrdirformat.create_repository.
319
:param shared: Create a shared repository rather than a standalone
321
The Repository object is returned.
323
This must be overridden as an instance method in child classes, where
324
it should take no parameters and construct whatever repository format
325
that child class desires.
327
bzrdir = BzrDir.create(base, format)
328
return bzrdir.create_repository(shared)
331
def create_standalone_workingtree(base, format=None):
332
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
334
'base' must be a local path or a file:// url.
336
This will use the current default BzrDirFormat, and use whatever
337
repository format that that uses for bzrdirformat.create_workingtree,
338
create_branch and create_repository.
340
:return: The WorkingTree object.
342
t = get_transport(safe_unicode(base))
343
if not isinstance(t, LocalTransport):
344
raise errors.NotLocalUrl(base)
345
bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
347
format=format).bzrdir
348
return bzrdir.create_workingtree()
350
def create_workingtree(self, revision_id=None):
351
"""Create a working tree at this BzrDir.
353
revision_id: create it as of this revision id.
355
raise NotImplementedError(self.create_workingtree)
357
def destroy_workingtree(self):
358
"""Destroy the working tree at this BzrDir.
360
Formats that do not support this may raise UnsupportedOperation.
362
raise NotImplementedError(self.destroy_workingtree)
364
def destroy_workingtree_metadata(self):
365
"""Destroy the control files for the working tree at this BzrDir.
367
The contents of working tree files are not affected.
368
Formats that do not support this may raise UnsupportedOperation.
370
raise NotImplementedError(self.destroy_workingtree_metadata)
372
def find_repository(self):
373
"""Find the repository that should be used for a_bzrdir.
375
This does not require a branch as we use it to find the repo for
376
new branches as well as to hook existing branches up to their
380
return self.open_repository()
381
except errors.NoRepositoryPresent:
383
next_transport = self.root_transport.clone('..')
385
# find the next containing bzrdir
387
found_bzrdir = BzrDir.open_containing_from_transport(
389
except errors.NotBranchError:
391
raise errors.NoRepositoryPresent(self)
392
# does it have a repository ?
394
repository = found_bzrdir.open_repository()
395
except errors.NoRepositoryPresent:
396
next_transport = found_bzrdir.root_transport.clone('..')
397
if (found_bzrdir.root_transport.base == next_transport.base):
398
# top of the file system
402
if ((found_bzrdir.root_transport.base ==
403
self.root_transport.base) or repository.is_shared()):
406
raise errors.NoRepositoryPresent(self)
407
raise errors.NoRepositoryPresent(self)
409
def get_branch_transport(self, branch_format):
410
"""Get the transport for use by branch format in this BzrDir.
412
Note that bzr dirs that do not support format strings will raise
413
IncompatibleFormat if the branch format they are given has
414
a format string, and vice versa.
416
If branch_format is None, the transport is returned with no
417
checking. if it is not None, then the returned transport is
418
guaranteed to point to an existing directory ready for use.
420
raise NotImplementedError(self.get_branch_transport)
422
def get_repository_transport(self, repository_format):
423
"""Get the transport for use by repository format in this BzrDir.
425
Note that bzr dirs that do not support format strings will raise
426
IncompatibleFormat if the repository format they are given has
427
a format string, and vice versa.
429
If repository_format is None, the transport is returned with no
430
checking. if it is not None, then the returned transport is
431
guaranteed to point to an existing directory ready for use.
433
raise NotImplementedError(self.get_repository_transport)
435
def get_workingtree_transport(self, tree_format):
436
"""Get the transport for use by workingtree format in this BzrDir.
438
Note that bzr dirs that do not support format strings will raise
439
IncompatibleFormat if the workingtree format they are given has
440
a format string, and vice versa.
442
If workingtree_format is None, the transport is returned with no
443
checking. if it is not None, then the returned transport is
444
guaranteed to point to an existing directory ready for use.
446
raise NotImplementedError(self.get_workingtree_transport)
448
def __init__(self, _transport, _format):
449
"""Initialize a Bzr control dir object.
451
Only really common logic should reside here, concrete classes should be
452
made with varying behaviours.
454
:param _format: the format that is creating this BzrDir instance.
455
:param _transport: the transport this dir is based at.
457
self._format = _format
458
self.transport = _transport.clone('.bzr')
459
self.root_transport = _transport
461
def is_control_filename(self, filename):
462
"""True if filename is the name of a path which is reserved for bzrdir's.
464
:param filename: A filename within the root transport of this bzrdir.
466
This is true IF and ONLY IF the filename is part of the namespace reserved
467
for bzr control dirs. Currently this is the '.bzr' directory in the root
468
of the root_transport. it is expected that plugins will need to extend
469
this in the future - for instance to make bzr talk with svn working
472
# this might be better on the BzrDirFormat class because it refers to
473
# all the possible bzrdir disk formats.
474
# This method is tested via the workingtree is_control_filename tests-
475
# it was extracted from WorkingTree.is_control_filename. If the methods
476
# contract is extended beyond the current trivial implementation please
477
# add new tests for it to the appropriate place.
478
return filename == '.bzr' or filename.startswith('.bzr/')
480
def needs_format_conversion(self, format=None):
481
"""Return true if this bzrdir needs convert_format run on it.
483
For instance, if the repository format is out of date but the
484
branch and working tree are not, this should return True.
486
:param format: Optional parameter indicating a specific desired
487
format we plan to arrive at.
489
raise NotImplementedError(self.needs_format_conversion)
492
def open_unsupported(base):
493
"""Open a branch which is not supported."""
494
return BzrDir.open(base, _unsupported=True)
497
def open(base, _unsupported=False):
498
"""Open an existing bzrdir, rooted at 'base' (url)
500
_unsupported is a private parameter to the BzrDir class.
502
t = get_transport(base)
503
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
508
def open_from_transport(transport, _unsupported=False):
509
"""Open a bzrdir within a particular directory.
511
:param transport: Transport containing the bzrdir.
512
:param _unsupported: private.
514
initial_base = transport.base
515
redirected = True # to enter the loop
517
# If a loop occurs, there is little we can do. So we
518
# don't try to detect them, just getting out if too much
519
# redirections occurs. The solution is outside: where the
521
while redirected and redirections < BzrDir.MAX_REDIRECTIONS:
523
format = BzrDirFormat.find_format(transport)
525
except errors.RedirectRequested, e:
527
qualified_source = e.get_source_url()
528
relpath = transport.relpath(qualified_source)
529
if not e.target.endswith(relpath):
530
# Not redirected to a branch-format, not a branch
531
raise errors.NotBranchError(path=e.target)
533
target = e.target[:-len(relpath)]
534
note('%s is%s redirected to %s',
538
# Let's try with a new transport
539
qualified_target = e.get_target_url()[:-len(relpath)]
540
transport = get_transport(target)
543
# Out of the loop and still redirected ? Either the
544
# user have kept a very very very old reference to a
545
# branch or a loop occured in the redirections.
546
# Nothing we can cure here: tell the user.
547
raise errors.NotBranchError(path=initial_base)
548
BzrDir._check_supported(format, _unsupported)
549
return format.open(transport, _found=True)
551
def open_branch(self, unsupported=False):
552
"""Open the branch object at this BzrDir if one is present.
554
If unsupported is True, then no longer supported branch formats can
557
TODO: static convenience version of this?
559
raise NotImplementedError(self.open_branch)
562
def open_containing(url):
563
"""Open an existing branch which contains url.
565
:param url: url to search from.
566
See open_containing_from_transport for more detail.
568
return BzrDir.open_containing_from_transport(get_transport(url))
571
def open_containing_from_transport(a_transport):
572
"""Open an existing branch which contains a_transport.base
574
This probes for a branch at a_transport, and searches upwards from there.
576
Basically we keep looking up until we find the control directory or
577
run into the root. If there isn't one, raises NotBranchError.
578
If there is one and it is either an unrecognised format or an unsupported
579
format, UnknownFormatError or UnsupportedFormatError are raised.
580
If there is one, it is returned, along with the unused portion of url.
582
:return: The BzrDir that contains the path, and a Unicode path
583
for the rest of the URL.
585
# this gets the normalised url back. I.e. '.' -> the full path.
586
url = a_transport.base
589
result = BzrDir.open_from_transport(a_transport)
590
return result, urlutils.unescape(a_transport.relpath(url))
591
except errors.NotBranchError, e:
593
new_t = a_transport.clone('..')
594
if new_t.base == a_transport.base:
595
# reached the root, whatever that may be
596
raise errors.NotBranchError(path=url)
600
def open_containing_tree_or_branch(klass, location):
601
"""Return the branch and working tree contained by a location.
603
Returns (tree, branch, relpath).
604
If there is no tree at containing the location, tree will be None.
605
If there is no branch containing the location, an exception will be
607
relpath is the portion of the path that is contained by the branch.
609
bzrdir, relpath = klass.open_containing(location)
611
tree = bzrdir.open_workingtree()
612
except (errors.NoWorkingTree, errors.NotLocalUrl):
614
branch = bzrdir.open_branch()
617
return tree, branch, relpath
619
def open_repository(self, _unsupported=False):
620
"""Open the repository object at this BzrDir if one is present.
622
This will not follow the Branch object pointer - its strictly a direct
623
open facility. Most client code should use open_branch().repository to
626
_unsupported is a private parameter, not part of the api.
627
TODO: static convenience version of this?
629
raise NotImplementedError(self.open_repository)
631
def open_workingtree(self, _unsupported=False):
632
"""Open the workingtree object at this BzrDir if one is present.
634
TODO: static convenience version of this?
636
raise NotImplementedError(self.open_workingtree)
638
def has_branch(self):
639
"""Tell if this bzrdir contains a branch.
641
Note: if you're going to open the branch, you should just go ahead
642
and try, and not ask permission first. (This method just opens the
643
branch and discards it, and that's somewhat expensive.)
648
except errors.NotBranchError:
651
def has_workingtree(self):
652
"""Tell if this bzrdir contains a working tree.
654
This will still raise an exception if the bzrdir has a workingtree that
655
is remote & inaccessible.
657
Note: if you're going to open the working tree, you should just go ahead
658
and try, and not ask permission first. (This method just opens the
659
workingtree and discards it, and that's somewhat expensive.)
662
self.open_workingtree()
664
except errors.NoWorkingTree:
667
def cloning_metadir(self, basis=None):
668
"""Produce a metadir suitable for cloning with"""
669
def related_repository(bzrdir):
671
branch = bzrdir.open_branch()
672
return branch.repository
673
except errors.NotBranchError:
675
return bzrdir.open_repository()
676
result_format = self._format.__class__()
679
source_repository = related_repository(self)
680
except errors.NoRepositoryPresent:
683
source_repository = related_repository(self)
684
result_format.repository_format = source_repository._format
685
except errors.NoRepositoryPresent:
689
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
690
"""Create a copy of this bzrdir prepared for use as a new line of
693
If urls last component does not exist, it will be created.
695
Attributes related to the identity of the source branch like
696
branch nickname will be cleaned, a working tree is created
697
whether one existed before or not; and a local branch is always
700
if revision_id is not None, then the clone operation may tune
701
itself to download less data.
704
cloning_format = self.cloning_metadir(basis)
705
result = cloning_format.initialize(url)
706
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
708
source_branch = self.open_branch()
709
source_repository = source_branch.repository
710
except errors.NotBranchError:
713
source_repository = self.open_repository()
714
except errors.NoRepositoryPresent:
715
# copy the entire basis one if there is one
716
# but there is no repository.
717
source_repository = basis_repo
722
result_repo = result.find_repository()
723
except errors.NoRepositoryPresent:
725
if source_repository is None and result_repo is not None:
727
elif source_repository is None and result_repo is None:
728
# no repo available, make a new one
729
result.create_repository()
730
elif source_repository is not None and result_repo is None:
731
# have source, and want to make a new target repo
732
# we don't clone the repo because that preserves attributes
733
# like is_shared(), and we have not yet implemented a
734
# repository sprout().
735
result_repo = result.create_repository()
736
if result_repo is not None:
737
# fetch needed content into target.
739
# XXX FIXME RBC 20060214 need tests for this when the basis
741
result_repo.fetch(basis_repo, revision_id=revision_id)
742
if source_repository is not None:
743
result_repo.fetch(source_repository, revision_id=revision_id)
744
if source_branch is not None:
745
source_branch.sprout(result, revision_id=revision_id)
747
result.create_branch()
748
# TODO: jam 20060426 we probably need a test in here in the
749
# case that the newly sprouted branch is a remote one
750
if result_repo is None or result_repo.make_working_trees():
751
wt = result.create_workingtree()
752
if wt.inventory.root is None:
754
wt.set_root_id(self.open_workingtree.get_root_id())
755
except errors.NoWorkingTree:
760
class BzrDirPreSplitOut(BzrDir):
761
"""A common class for the all-in-one formats."""
763
def __init__(self, _transport, _format):
764
"""See BzrDir.__init__."""
765
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
766
assert self._format._lock_class == lockable_files.TransportLock
767
assert self._format._lock_file_name == 'branch-lock'
768
self._control_files = lockable_files.LockableFiles(
769
self.get_branch_transport(None),
770
self._format._lock_file_name,
771
self._format._lock_class)
773
def break_lock(self):
774
"""Pre-splitout bzrdirs do not suffer from stale locks."""
775
raise NotImplementedError(self.break_lock)
777
def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
778
"""See BzrDir.clone()."""
779
from bzrlib.workingtree import WorkingTreeFormat2
781
result = self._format._initialize_for_clone(url)
782
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
783
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
784
from_branch = self.open_branch()
785
from_branch.clone(result, revision_id=revision_id)
787
self.open_workingtree().clone(result, basis=basis_tree)
788
except errors.NotLocalUrl:
789
# make a new one, this format always has to have one.
791
WorkingTreeFormat2().initialize(result)
792
except errors.NotLocalUrl:
793
# but we cannot do it for remote trees.
794
to_branch = result.open_branch()
795
WorkingTreeFormat2().stub_initialize_remote(to_branch.control_files)
798
def create_branch(self):
799
"""See BzrDir.create_branch."""
800
return self.open_branch()
802
def create_repository(self, shared=False):
803
"""See BzrDir.create_repository."""
805
raise errors.IncompatibleFormat('shared repository', self._format)
806
return self.open_repository()
808
def create_workingtree(self, revision_id=None):
809
"""See BzrDir.create_workingtree."""
810
# this looks buggy but is not -really-
811
# clone and sprout will have set the revision_id
812
# and that will have set it for us, its only
813
# specific uses of create_workingtree in isolation
814
# that can do wonky stuff here, and that only
815
# happens for creating checkouts, which cannot be
816
# done on this format anyway. So - acceptable wart.
817
result = self.open_workingtree()
818
if revision_id is not None:
819
if revision_id == _mod_revision.NULL_REVISION:
820
result.set_parent_ids([])
822
result.set_parent_ids([revision_id])
825
def destroy_workingtree(self):
826
"""See BzrDir.destroy_workingtree."""
827
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
829
def destroy_workingtree_metadata(self):
830
"""See BzrDir.destroy_workingtree_metadata."""
831
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
834
def get_branch_transport(self, branch_format):
835
"""See BzrDir.get_branch_transport()."""
836
if branch_format is None:
837
return self.transport
839
branch_format.get_format_string()
840
except NotImplementedError:
841
return self.transport
842
raise errors.IncompatibleFormat(branch_format, self._format)
844
def get_repository_transport(self, repository_format):
845
"""See BzrDir.get_repository_transport()."""
846
if repository_format is None:
847
return self.transport
849
repository_format.get_format_string()
850
except NotImplementedError:
851
return self.transport
852
raise errors.IncompatibleFormat(repository_format, self._format)
854
def get_workingtree_transport(self, workingtree_format):
855
"""See BzrDir.get_workingtree_transport()."""
856
if workingtree_format is None:
857
return self.transport
859
workingtree_format.get_format_string()
860
except NotImplementedError:
861
return self.transport
862
raise errors.IncompatibleFormat(workingtree_format, self._format)
864
def needs_format_conversion(self, format=None):
865
"""See BzrDir.needs_format_conversion()."""
866
# if the format is not the same as the system default,
867
# an upgrade is needed.
869
format = BzrDirFormat.get_default_format()
870
return not isinstance(self._format, format.__class__)
872
def open_branch(self, unsupported=False):
873
"""See BzrDir.open_branch."""
874
from bzrlib.branch import BzrBranchFormat4
875
format = BzrBranchFormat4()
876
self._check_supported(format, unsupported)
877
return format.open(self, _found=True)
879
def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
880
"""See BzrDir.sprout()."""
881
from bzrlib.workingtree import WorkingTreeFormat2
883
result = self._format._initialize_for_clone(url)
884
basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
886
self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
887
except errors.NoRepositoryPresent:
890
self.open_branch().sprout(result, revision_id=revision_id)
891
except errors.NotBranchError:
893
# we always want a working tree
894
WorkingTreeFormat2().initialize(result)
898
class BzrDir4(BzrDirPreSplitOut):
899
"""A .bzr version 4 control object.
901
This is a deprecated format and may be removed after sept 2006.
904
def create_repository(self, shared=False):
905
"""See BzrDir.create_repository."""
906
return self._format.repository_format.initialize(self, shared)
908
def needs_format_conversion(self, format=None):
909
"""Format 4 dirs are always in need of conversion."""
912
def open_repository(self):
913
"""See BzrDir.open_repository."""
914
from bzrlib.repository import RepositoryFormat4
915
return RepositoryFormat4().open(self, _found=True)
918
class BzrDir5(BzrDirPreSplitOut):
919
"""A .bzr version 5 control object.
921
This is a deprecated format and may be removed after sept 2006.
924
def open_repository(self):
925
"""See BzrDir.open_repository."""
926
from bzrlib.repository import RepositoryFormat5
927
return RepositoryFormat5().open(self, _found=True)
929
def open_workingtree(self, _unsupported=False):
930
"""See BzrDir.create_workingtree."""
931
from bzrlib.workingtree import WorkingTreeFormat2
932
return WorkingTreeFormat2().open(self, _found=True)
935
class BzrDir6(BzrDirPreSplitOut):
936
"""A .bzr version 6 control object.
938
This is a deprecated format and may be removed after sept 2006.
941
def open_repository(self):
942
"""See BzrDir.open_repository."""
943
from bzrlib.repository import RepositoryFormat6
944
return RepositoryFormat6().open(self, _found=True)
946
def open_workingtree(self, _unsupported=False):
947
"""See BzrDir.create_workingtree."""
948
from bzrlib.workingtree import WorkingTreeFormat2
949
return WorkingTreeFormat2().open(self, _found=True)
952
class BzrDirMeta1(BzrDir):
953
"""A .bzr meta version 1 control object.
955
This is the first control object where the
956
individual aspects are really split out: there are separate repository,
957
workingtree and branch subdirectories and any subset of the three can be
958
present within a BzrDir.
961
def can_convert_format(self):
962
"""See BzrDir.can_convert_format()."""
965
def create_branch(self):
966
"""See BzrDir.create_branch."""
967
from bzrlib.branch import BranchFormat
968
return BranchFormat.get_default_format().initialize(self)
970
def create_repository(self, shared=False):
971
"""See BzrDir.create_repository."""
972
return self._format.repository_format.initialize(self, shared)
974
def create_workingtree(self, revision_id=None):
975
"""See BzrDir.create_workingtree."""
976
from bzrlib.workingtree import WorkingTreeFormat
977
return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
979
def destroy_workingtree(self):
980
"""See BzrDir.destroy_workingtree."""
981
wt = self.open_workingtree()
982
repository = wt.branch.repository
983
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
984
wt.revert([], old_tree=empty)
985
self.destroy_workingtree_metadata()
987
def destroy_workingtree_metadata(self):
988
self.transport.delete_tree('checkout')
990
def _get_mkdir_mode(self):
991
"""Figure out the mode to use when creating a bzrdir subdir."""
992
temp_control = lockable_files.LockableFiles(self.transport, '',
993
lockable_files.TransportLock)
994
return temp_control._dir_mode
996
def get_branch_transport(self, branch_format):
997
"""See BzrDir.get_branch_transport()."""
998
if branch_format is None:
999
return self.transport.clone('branch')
1001
branch_format.get_format_string()
1002
except NotImplementedError:
1003
raise errors.IncompatibleFormat(branch_format, self._format)
1005
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1006
except errors.FileExists:
1008
return self.transport.clone('branch')
1010
def get_repository_transport(self, repository_format):
1011
"""See BzrDir.get_repository_transport()."""
1012
if repository_format is None:
1013
return self.transport.clone('repository')
1015
repository_format.get_format_string()
1016
except NotImplementedError:
1017
raise errors.IncompatibleFormat(repository_format, self._format)
1019
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1020
except errors.FileExists:
1022
return self.transport.clone('repository')
1024
def get_workingtree_transport(self, workingtree_format):
1025
"""See BzrDir.get_workingtree_transport()."""
1026
if workingtree_format is None:
1027
return self.transport.clone('checkout')
1029
workingtree_format.get_format_string()
1030
except NotImplementedError:
1031
raise errors.IncompatibleFormat(workingtree_format, self._format)
1033
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1034
except errors.FileExists:
1036
return self.transport.clone('checkout')
1038
def needs_format_conversion(self, format=None):
1039
"""See BzrDir.needs_format_conversion()."""
1041
format = BzrDirFormat.get_default_format()
1042
if not isinstance(self._format, format.__class__):
1043
# it is not a meta dir format, conversion is needed.
1045
# we might want to push this down to the repository?
1047
if not isinstance(self.open_repository()._format,
1048
format.repository_format.__class__):
1049
# the repository needs an upgrade.
1051
except errors.NoRepositoryPresent:
1053
# currently there are no other possible conversions for meta1 formats.
1056
def open_branch(self, unsupported=False):
1057
"""See BzrDir.open_branch."""
1058
from bzrlib.branch import BranchFormat
1059
format = BranchFormat.find_format(self)
1060
self._check_supported(format, unsupported)
1061
return format.open(self, _found=True)
1063
def open_repository(self, unsupported=False):
1064
"""See BzrDir.open_repository."""
1065
from bzrlib.repository import RepositoryFormat
1066
format = RepositoryFormat.find_format(self)
1067
self._check_supported(format, unsupported)
1068
return format.open(self, _found=True)
1070
def open_workingtree(self, unsupported=False):
1071
"""See BzrDir.open_workingtree."""
1072
from bzrlib.workingtree import WorkingTreeFormat
1073
format = WorkingTreeFormat.find_format(self)
1074
self._check_supported(format, unsupported)
1075
return format.open(self, _found=True)
1078
class BzrDirFormat(object):
1079
"""An encapsulation of the initialization and open routines for a format.
1081
Formats provide three things:
1082
* An initialization routine,
1086
Formats are placed in an dict by their format string for reference
1087
during bzrdir opening. These should be subclasses of BzrDirFormat
1090
Once a format is deprecated, just deprecate the initialize and open
1091
methods on the format class. Do not deprecate the object, as the
1092
object will be created every system load.
1095
_default_format = None
1096
"""The default format used for new .bzr dirs."""
1099
"""The known formats."""
1101
_control_formats = []
1102
"""The registered control formats - .bzr, ....
1104
This is a list of BzrDirFormat objects.
1107
_lock_file_name = 'branch-lock'
1109
# _lock_class must be set in subclasses to the lock type, typ.
1110
# TransportLock or LockDir
1113
def find_format(klass, transport):
1114
"""Return the format present at transport."""
1115
for format in klass._control_formats:
1117
return format.probe_transport(transport)
1118
except errors.NotBranchError:
1119
# this format does not find a control dir here.
1121
raise errors.NotBranchError(path=transport.base)
1124
def probe_transport(klass, transport):
1125
"""Return the .bzrdir style transport present at URL."""
1127
format_file = transport.get(".bzr/branch-format")
1128
format_string = format_file.read()
1129
except errors.NoSuchFile:
1130
raise errors.NotBranchError(path=transport.base)
1133
return klass._formats[format_string]
1135
raise errors.UnknownFormatError(format=format_string)
1138
def get_default_format(klass):
1139
"""Return the current default format."""
1140
return klass._default_format
1142
def get_format_string(self):
1143
"""Return the ASCII format string that identifies this format."""
1144
raise NotImplementedError(self.get_format_string)
1146
def get_format_description(self):
1147
"""Return the short description for this format."""
1148
raise NotImplementedError(self.get_format_description)
1150
def get_converter(self, format=None):
1151
"""Return the converter to use to convert bzrdirs needing converts.
1153
This returns a bzrlib.bzrdir.Converter object.
1155
This should return the best upgrader to step this format towards the
1156
current default format. In the case of plugins we can/should provide
1157
some means for them to extend the range of returnable converters.
1159
:param format: Optional format to override the default format of the
1162
raise NotImplementedError(self.get_converter)
1164
def initialize(self, url):
1165
"""Create a bzr control dir at this url and return an opened copy.
1167
Subclasses should typically override initialize_on_transport
1168
instead of this method.
1170
return self.initialize_on_transport(get_transport(url))
1172
def initialize_on_transport(self, transport):
1173
"""Initialize a new bzrdir in the base directory of a Transport."""
1174
# Since we don't have a .bzr directory, inherit the
1175
# mode from the root directory
1176
temp_control = lockable_files.LockableFiles(transport,
1177
'', lockable_files.TransportLock)
1178
temp_control._transport.mkdir('.bzr',
1179
# FIXME: RBC 20060121 don't peek under
1181
mode=temp_control._dir_mode)
1182
file_mode = temp_control._file_mode
1184
mutter('created control directory in ' + transport.base)
1185
control = transport.clone('.bzr')
1186
utf8_files = [('README',
1187
"This is a Bazaar-NG control directory.\n"
1188
"Do not change any files in this directory.\n"),
1189
('branch-format', self.get_format_string()),
1191
# NB: no need to escape relative paths that are url safe.
1192
control_files = lockable_files.LockableFiles(control,
1193
self._lock_file_name, self._lock_class)
1194
control_files.create_lock()
1195
control_files.lock_write()
1197
for file, content in utf8_files:
1198
control_files.put_utf8(file, content)
1200
control_files.unlock()
1201
return self.open(transport, _found=True)
1203
def is_supported(self):
1204
"""Is this format supported?
1206
Supported formats must be initializable and openable.
1207
Unsupported formats may not support initialization or committing or
1208
some other features depending on the reason for not being supported.
1212
def same_model(self, target_format):
1213
return (self.repository_format.rich_root_data ==
1214
target_format.rich_root_data)
1217
def known_formats(klass):
1218
"""Return all the known formats.
1220
Concrete formats should override _known_formats.
1222
# There is double indirection here to make sure that control
1223
# formats used by more than one dir format will only be probed
1224
# once. This can otherwise be quite expensive for remote connections.
1226
for format in klass._control_formats:
1227
result.update(format._known_formats())
1231
def _known_formats(klass):
1232
"""Return the known format instances for this control format."""
1233
return set(klass._formats.values())
1235
def open(self, transport, _found=False):
1236
"""Return an instance of this format for the dir transport points at.
1238
_found is a private parameter, do not use it.
1241
found_format = BzrDirFormat.find_format(transport)
1242
if not isinstance(found_format, self.__class__):
1243
raise AssertionError("%s was asked to open %s, but it seems to need "
1245
% (self, transport, found_format))
1246
return self._open(transport)
1248
def _open(self, transport):
1249
"""Template method helper for opening BzrDirectories.
1251
This performs the actual open and any additional logic or parameter
1254
raise NotImplementedError(self._open)
1257
def register_format(klass, format):
1258
klass._formats[format.get_format_string()] = format
1261
def register_control_format(klass, format):
1262
"""Register a format that does not use '.bzr' for its control dir.
1264
TODO: This should be pulled up into a 'ControlDirFormat' base class
1265
which BzrDirFormat can inherit from, and renamed to register_format
1266
there. It has been done without that for now for simplicity of
1269
klass._control_formats.append(format)
1272
@symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1273
def set_default_format(klass, format):
1274
klass._set_default_format(format)
1277
def _set_default_format(klass, format):
1278
"""Set default format (for testing behavior of defaults only)"""
1279
klass._default_format = format
1282
return self.get_format_string()[:-1]
1285
def unregister_format(klass, format):
1286
assert klass._formats[format.get_format_string()] is format
1287
del klass._formats[format.get_format_string()]
1290
def unregister_control_format(klass, format):
1291
klass._control_formats.remove(format)
1294
class BzrDirFormat4(BzrDirFormat):
1295
"""Bzr dir format 4.
1297
This format is a combined format for working tree, branch and repository.
1299
- Format 1 working trees [always]
1300
- Format 4 branches [always]
1301
- Format 4 repositories [always]
1303
This format is deprecated: it indexes texts using a text it which is
1304
removed in format 5; write support for this format has been removed.
1307
_lock_class = lockable_files.TransportLock
1309
def get_format_string(self):
1310
"""See BzrDirFormat.get_format_string()."""
1311
return "Bazaar-NG branch, format 0.0.4\n"
1313
def get_format_description(self):
1314
"""See BzrDirFormat.get_format_description()."""
1315
return "All-in-one format 4"
1317
def get_converter(self, format=None):
1318
"""See BzrDirFormat.get_converter()."""
1319
# there is one and only one upgrade path here.
1320
return ConvertBzrDir4To5()
1322
def initialize_on_transport(self, transport):
1323
"""Format 4 branches cannot be created."""
1324
raise errors.UninitializableFormat(self)
1326
def is_supported(self):
1327
"""Format 4 is not supported.
1329
It is not supported because the model changed from 4 to 5 and the
1330
conversion logic is expensive - so doing it on the fly was not
1335
def _open(self, transport):
1336
"""See BzrDirFormat._open."""
1337
return BzrDir4(transport, self)
1339
def __return_repository_format(self):
1340
"""Circular import protection."""
1341
from bzrlib.repository import RepositoryFormat4
1342
return RepositoryFormat4()
1343
repository_format = property(__return_repository_format)
1346
class BzrDirFormat5(BzrDirFormat):
1347
"""Bzr control format 5.
1349
This format is a combined format for working tree, branch and repository.
1351
- Format 2 working trees [always]
1352
- Format 4 branches [always]
1353
- Format 5 repositories [always]
1354
Unhashed stores in the repository.
1357
_lock_class = lockable_files.TransportLock
1359
def get_format_string(self):
1360
"""See BzrDirFormat.get_format_string()."""
1361
return "Bazaar-NG branch, format 5\n"
1363
def get_format_description(self):
1364
"""See BzrDirFormat.get_format_description()."""
1365
return "All-in-one format 5"
1367
def get_converter(self, format=None):
1368
"""See BzrDirFormat.get_converter()."""
1369
# there is one and only one upgrade path here.
1370
return ConvertBzrDir5To6()
1372
def _initialize_for_clone(self, url):
1373
return self.initialize_on_transport(get_transport(url), _cloning=True)
1375
def initialize_on_transport(self, transport, _cloning=False):
1376
"""Format 5 dirs always have working tree, branch and repository.
1378
Except when they are being cloned.
1380
from bzrlib.branch import BzrBranchFormat4
1381
from bzrlib.repository import RepositoryFormat5
1382
from bzrlib.workingtree import WorkingTreeFormat2
1383
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1384
RepositoryFormat5().initialize(result, _internal=True)
1386
branch = BzrBranchFormat4().initialize(result)
1388
WorkingTreeFormat2().initialize(result)
1389
except errors.NotLocalUrl:
1390
# Even though we can't access the working tree, we need to
1391
# create its control files.
1392
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1395
def _open(self, transport):
1396
"""See BzrDirFormat._open."""
1397
return BzrDir5(transport, self)
1399
def __return_repository_format(self):
1400
"""Circular import protection."""
1401
from bzrlib.repository import RepositoryFormat5
1402
return RepositoryFormat5()
1403
repository_format = property(__return_repository_format)
1406
class BzrDirFormat6(BzrDirFormat):
1407
"""Bzr control format 6.
1409
This format is a combined format for working tree, branch and repository.
1411
- Format 2 working trees [always]
1412
- Format 4 branches [always]
1413
- Format 6 repositories [always]
1416
_lock_class = lockable_files.TransportLock
1418
def get_format_string(self):
1419
"""See BzrDirFormat.get_format_string()."""
1420
return "Bazaar-NG branch, format 6\n"
1422
def get_format_description(self):
1423
"""See BzrDirFormat.get_format_description()."""
1424
return "All-in-one format 6"
1426
def get_converter(self, format=None):
1427
"""See BzrDirFormat.get_converter()."""
1428
# there is one and only one upgrade path here.
1429
return ConvertBzrDir6ToMeta()
1431
def _initialize_for_clone(self, url):
1432
return self.initialize_on_transport(get_transport(url), _cloning=True)
1434
def initialize_on_transport(self, transport, _cloning=False):
1435
"""Format 6 dirs always have working tree, branch and repository.
1437
Except when they are being cloned.
1439
from bzrlib.branch import BzrBranchFormat4
1440
from bzrlib.repository import RepositoryFormat6
1441
from bzrlib.workingtree import WorkingTreeFormat2
1442
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1443
RepositoryFormat6().initialize(result, _internal=True)
1445
branch = BzrBranchFormat4().initialize(result)
1447
WorkingTreeFormat2().initialize(result)
1448
except errors.NotLocalUrl:
1449
# Even though we can't access the working tree, we need to
1450
# create its control files.
1451
WorkingTreeFormat2().stub_initialize_remote(branch.control_files)
1454
def _open(self, transport):
1455
"""See BzrDirFormat._open."""
1456
return BzrDir6(transport, self)
1458
def __return_repository_format(self):
1459
"""Circular import protection."""
1460
from bzrlib.repository import RepositoryFormat6
1461
return RepositoryFormat6()
1462
repository_format = property(__return_repository_format)
1465
class BzrDirMetaFormat1(BzrDirFormat):
1466
"""Bzr meta control format 1
1468
This is the first format with split out working tree, branch and repository
1471
- Format 3 working trees [optional]
1472
- Format 5 branches [optional]
1473
- Format 7 repositories [optional]
1476
_lock_class = lockdir.LockDir
1478
def get_converter(self, format=None):
1479
"""See BzrDirFormat.get_converter()."""
1481
format = BzrDirFormat.get_default_format()
1482
if not isinstance(self, format.__class__):
1483
# converting away from metadir is not implemented
1484
raise NotImplementedError(self.get_converter)
1485
return ConvertMetaToMeta(format)
1487
def get_format_string(self):
1488
"""See BzrDirFormat.get_format_string()."""
1489
return "Bazaar-NG meta directory, format 1\n"
1491
def get_format_description(self):
1492
"""See BzrDirFormat.get_format_description()."""
1493
return "Meta directory format 1"
1495
def _open(self, transport):
1496
"""See BzrDirFormat._open."""
1497
return BzrDirMeta1(transport, self)
1499
def __return_repository_format(self):
1500
"""Circular import protection."""
1501
if getattr(self, '_repository_format', None):
1502
return self._repository_format
1503
from bzrlib.repository import RepositoryFormat
1504
return RepositoryFormat.get_default_format()
1506
def __set_repository_format(self, value):
1507
"""Allow changint the repository format for metadir formats."""
1508
self._repository_format = value
1511
def probe_transport(klass, transport):
1512
"""Return the .bzrdir style transport present at URL.
1514
Redirections are not followed.
1517
format_file = transport.get(".bzr/branch-format")
1518
format_string = format_file.read()
1519
except errors.NoSuchFile:
1520
raise errors.NotBranchError(path=transport.base)
1523
return klass._formats[format_string]
1525
raise errors.UnknownFormatError(format=format_string)
1527
repository_format = property(__return_repository_format, __set_repository_format)
1530
# register BzrDirMetaFormat1 as a control format. Our only
1531
# concrete control format (BzrDirFormat is an abstract one).
1532
BzrDirFormat.register_control_format(BzrDirMetaFormat1)
1534
# Register bzr formats
1535
BzrDirFormat.register_format(BzrDirFormat4())
1536
BzrDirFormat.register_format(BzrDirFormat5())
1537
BzrDirFormat.register_format(BzrDirFormat6())
1538
__default_format = BzrDirMetaFormat1()
1539
BzrDirFormat.register_format(__default_format)
1540
BzrDirFormat._default_format = __default_format
1543
class BzrDirTestProviderAdapter(object):
1544
"""A tool to generate a suite testing multiple bzrdir formats at once.
1546
This is done by copying the test once for each transport and injecting
1547
the transport_server, transport_readonly_server, and bzrdir_format
1548
classes into each copy. Each copy is also given a new id() to make it
1552
def __init__(self, transport_server, transport_readonly_server, formats):
1553
self._transport_server = transport_server
1554
self._transport_readonly_server = transport_readonly_server
1555
self._formats = formats
1557
def adapt(self, test):
1558
result = unittest.TestSuite()
1559
for format in self._formats:
1560
new_test = deepcopy(test)
1561
new_test.transport_server = self._transport_server
1562
new_test.transport_readonly_server = self._transport_readonly_server
1563
new_test.bzrdir_format = format
1564
def make_new_test_id():
1565
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1566
return lambda: new_id
1567
new_test.id = make_new_test_id()
1568
result.addTest(new_test)
1572
class Converter(object):
1573
"""Converts a disk format object from one format to another."""
1575
def convert(self, to_convert, pb):
1576
"""Perform the conversion of to_convert, giving feedback via pb.
1578
:param to_convert: The disk object to convert.
1579
:param pb: a progress bar to use for progress information.
1582
def step(self, message):
1583
"""Update the pb by a step."""
1585
self.pb.update(message, self.count, self.total)
1588
class ConvertBzrDir4To5(Converter):
1589
"""Converts format 4 bzr dirs to format 5."""
1592
super(ConvertBzrDir4To5, self).__init__()
1593
self.converted_revs = set()
1594
self.absent_revisions = set()
1598
def convert(self, to_convert, pb):
1599
"""See Converter.convert()."""
1600
self.bzrdir = to_convert
1602
self.pb.note('starting upgrade from format 4 to 5')
1603
if isinstance(self.bzrdir.transport, LocalTransport):
1604
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1605
self._convert_to_weaves()
1606
return BzrDir.open(self.bzrdir.root_transport.base)
1608
def _convert_to_weaves(self):
1609
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1612
stat = self.bzrdir.transport.stat('weaves')
1613
if not S_ISDIR(stat.st_mode):
1614
self.bzrdir.transport.delete('weaves')
1615
self.bzrdir.transport.mkdir('weaves')
1616
except errors.NoSuchFile:
1617
self.bzrdir.transport.mkdir('weaves')
1618
# deliberately not a WeaveFile as we want to build it up slowly.
1619
self.inv_weave = Weave('inventory')
1620
# holds in-memory weaves for all files
1621
self.text_weaves = {}
1622
self.bzrdir.transport.delete('branch-format')
1623
self.branch = self.bzrdir.open_branch()
1624
self._convert_working_inv()
1625
rev_history = self.branch.revision_history()
1626
# to_read is a stack holding the revisions we still need to process;
1627
# appending to it adds new highest-priority revisions
1628
self.known_revisions = set(rev_history)
1629
self.to_read = rev_history[-1:]
1631
rev_id = self.to_read.pop()
1632
if (rev_id not in self.revisions
1633
and rev_id not in self.absent_revisions):
1634
self._load_one_rev(rev_id)
1636
to_import = self._make_order()
1637
for i, rev_id in enumerate(to_import):
1638
self.pb.update('converting revision', i, len(to_import))
1639
self._convert_one_rev(rev_id)
1641
self._write_all_weaves()
1642
self._write_all_revs()
1643
self.pb.note('upgraded to weaves:')
1644
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1645
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1646
self.pb.note(' %6d texts', self.text_count)
1647
self._cleanup_spare_files_after_format4()
1648
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1650
def _cleanup_spare_files_after_format4(self):
1651
# FIXME working tree upgrade foo.
1652
for n in 'merged-patches', 'pending-merged-patches':
1654
## assert os.path.getsize(p) == 0
1655
self.bzrdir.transport.delete(n)
1656
except errors.NoSuchFile:
1658
self.bzrdir.transport.delete_tree('inventory-store')
1659
self.bzrdir.transport.delete_tree('text-store')
1661
def _convert_working_inv(self):
1662
inv = xml4.serializer_v4.read_inventory(
1663
self.branch.control_files.get('inventory'))
1664
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1665
# FIXME inventory is a working tree change.
1666
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1668
def _write_all_weaves(self):
1669
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1670
weave_transport = self.bzrdir.transport.clone('weaves')
1671
weaves = WeaveStore(weave_transport, prefixed=False)
1672
transaction = WriteTransaction()
1676
for file_id, file_weave in self.text_weaves.items():
1677
self.pb.update('writing weave', i, len(self.text_weaves))
1678
weaves._put_weave(file_id, file_weave, transaction)
1680
self.pb.update('inventory', 0, 1)
1681
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1682
self.pb.update('inventory', 1, 1)
1686
def _write_all_revs(self):
1687
"""Write all revisions out in new form."""
1688
self.bzrdir.transport.delete_tree('revision-store')
1689
self.bzrdir.transport.mkdir('revision-store')
1690
revision_transport = self.bzrdir.transport.clone('revision-store')
1692
_revision_store = TextRevisionStore(TextStore(revision_transport,
1696
transaction = WriteTransaction()
1697
for i, rev_id in enumerate(self.converted_revs):
1698
self.pb.update('write revision', i, len(self.converted_revs))
1699
_revision_store.add_revision(self.revisions[rev_id], transaction)
1703
def _load_one_rev(self, rev_id):
1704
"""Load a revision object into memory.
1706
Any parents not either loaded or abandoned get queued to be
1708
self.pb.update('loading revision',
1709
len(self.revisions),
1710
len(self.known_revisions))
1711
if not self.branch.repository.has_revision(rev_id):
1713
self.pb.note('revision {%s} not present in branch; '
1714
'will be converted as a ghost',
1716
self.absent_revisions.add(rev_id)
1718
rev = self.branch.repository._revision_store.get_revision(rev_id,
1719
self.branch.repository.get_transaction())
1720
for parent_id in rev.parent_ids:
1721
self.known_revisions.add(parent_id)
1722
self.to_read.append(parent_id)
1723
self.revisions[rev_id] = rev
1725
def _load_old_inventory(self, rev_id):
1726
assert rev_id not in self.converted_revs
1727
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1728
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1729
inv.revision_id = rev_id
1730
rev = self.revisions[rev_id]
1731
if rev.inventory_sha1:
1732
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1733
'inventory sha mismatch for {%s}' % rev_id
1736
def _load_updated_inventory(self, rev_id):
1737
assert rev_id in self.converted_revs
1738
inv_xml = self.inv_weave.get_text(rev_id)
1739
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1742
def _convert_one_rev(self, rev_id):
1743
"""Convert revision and all referenced objects to new format."""
1744
rev = self.revisions[rev_id]
1745
inv = self._load_old_inventory(rev_id)
1746
present_parents = [p for p in rev.parent_ids
1747
if p not in self.absent_revisions]
1748
self._convert_revision_contents(rev, inv, present_parents)
1749
self._store_new_weave(rev, inv, present_parents)
1750
self.converted_revs.add(rev_id)
1752
def _store_new_weave(self, rev, inv, present_parents):
1753
# the XML is now updated with text versions
1755
entries = inv.iter_entries()
1757
for path, ie in entries:
1758
assert getattr(ie, 'revision', None) is not None, \
1759
'no revision on {%s} in {%s}' % \
1760
(file_id, rev.revision_id)
1761
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1762
new_inv_sha1 = sha_string(new_inv_xml)
1763
self.inv_weave.add_lines(rev.revision_id,
1765
new_inv_xml.splitlines(True))
1766
rev.inventory_sha1 = new_inv_sha1
1768
def _convert_revision_contents(self, rev, inv, present_parents):
1769
"""Convert all the files within a revision.
1771
Also upgrade the inventory to refer to the text revision ids."""
1772
rev_id = rev.revision_id
1773
mutter('converting texts of revision {%s}',
1775
parent_invs = map(self._load_updated_inventory, present_parents)
1776
entries = inv.iter_entries()
1778
for path, ie in entries:
1779
self._convert_file_version(rev, ie, parent_invs)
1781
def _convert_file_version(self, rev, ie, parent_invs):
1782
"""Convert one version of one file.
1784
The file needs to be added into the weave if it is a merge
1785
of >=2 parents or if it's changed from its parent.
1787
file_id = ie.file_id
1788
rev_id = rev.revision_id
1789
w = self.text_weaves.get(file_id)
1792
self.text_weaves[file_id] = w
1793
text_changed = False
1794
previous_entries = ie.find_previous_heads(parent_invs,
1798
for old_revision in previous_entries:
1799
# if this fails, its a ghost ?
1800
assert old_revision in self.converted_revs, \
1801
"Revision {%s} not in converted_revs" % old_revision
1802
self.snapshot_ie(previous_entries, ie, w, rev_id)
1804
assert getattr(ie, 'revision', None) is not None
1806
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1807
# TODO: convert this logic, which is ~= snapshot to
1808
# a call to:. This needs the path figured out. rather than a work_tree
1809
# a v4 revision_tree can be given, or something that looks enough like
1810
# one to give the file content to the entry if it needs it.
1811
# and we need something that looks like a weave store for snapshot to
1813
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1814
if len(previous_revisions) == 1:
1815
previous_ie = previous_revisions.values()[0]
1816
if ie._unchanged(previous_ie):
1817
ie.revision = previous_ie.revision
1820
text = self.branch.repository.text_store.get(ie.text_id)
1821
file_lines = text.readlines()
1822
assert sha_strings(file_lines) == ie.text_sha1
1823
assert sum(map(len, file_lines)) == ie.text_size
1824
w.add_lines(rev_id, previous_revisions, file_lines)
1825
self.text_count += 1
1827
w.add_lines(rev_id, previous_revisions, [])
1828
ie.revision = rev_id
1830
def _make_order(self):
1831
"""Return a suitable order for importing revisions.
1833
The order must be such that an revision is imported after all
1834
its (present) parents.
1836
todo = set(self.revisions.keys())
1837
done = self.absent_revisions.copy()
1840
# scan through looking for a revision whose parents
1842
for rev_id in sorted(list(todo)):
1843
rev = self.revisions[rev_id]
1844
parent_ids = set(rev.parent_ids)
1845
if parent_ids.issubset(done):
1846
# can take this one now
1847
order.append(rev_id)
1853
class ConvertBzrDir5To6(Converter):
1854
"""Converts format 5 bzr dirs to format 6."""
1856
def convert(self, to_convert, pb):
1857
"""See Converter.convert()."""
1858
self.bzrdir = to_convert
1860
self.pb.note('starting upgrade from format 5 to 6')
1861
self._convert_to_prefixed()
1862
return BzrDir.open(self.bzrdir.root_transport.base)
1864
def _convert_to_prefixed(self):
1865
from bzrlib.store import TransportStore
1866
self.bzrdir.transport.delete('branch-format')
1867
for store_name in ["weaves", "revision-store"]:
1868
self.pb.note("adding prefixes to %s" % store_name)
1869
store_transport = self.bzrdir.transport.clone(store_name)
1870
store = TransportStore(store_transport, prefixed=True)
1871
for urlfilename in store_transport.list_dir('.'):
1872
filename = urlutils.unescape(urlfilename)
1873
if (filename.endswith(".weave") or
1874
filename.endswith(".gz") or
1875
filename.endswith(".sig")):
1876
file_id = os.path.splitext(filename)[0]
1879
prefix_dir = store.hash_prefix(file_id)
1880
# FIXME keep track of the dirs made RBC 20060121
1882
store_transport.move(filename, prefix_dir + '/' + filename)
1883
except errors.NoSuchFile: # catches missing dirs strangely enough
1884
store_transport.mkdir(prefix_dir)
1885
store_transport.move(filename, prefix_dir + '/' + filename)
1886
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1889
class ConvertBzrDir6ToMeta(Converter):
1890
"""Converts format 6 bzr dirs to metadirs."""
1892
def convert(self, to_convert, pb):
1893
"""See Converter.convert()."""
1894
from bzrlib.branch import BzrBranchFormat5
1895
self.bzrdir = to_convert
1898
self.total = 20 # the steps we know about
1899
self.garbage_inventories = []
1901
self.pb.note('starting upgrade from format 6 to metadir')
1902
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1903
# its faster to move specific files around than to open and use the apis...
1904
# first off, nuke ancestry.weave, it was never used.
1906
self.step('Removing ancestry.weave')
1907
self.bzrdir.transport.delete('ancestry.weave')
1908
except errors.NoSuchFile:
1910
# find out whats there
1911
self.step('Finding branch files')
1912
last_revision = self.bzrdir.open_branch().last_revision()
1913
bzrcontents = self.bzrdir.transport.list_dir('.')
1914
for name in bzrcontents:
1915
if name.startswith('basis-inventory.'):
1916
self.garbage_inventories.append(name)
1917
# create new directories for repository, working tree and branch
1918
self.dir_mode = self.bzrdir._control_files._dir_mode
1919
self.file_mode = self.bzrdir._control_files._file_mode
1920
repository_names = [('inventory.weave', True),
1921
('revision-store', True),
1923
self.step('Upgrading repository ')
1924
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1925
self.make_lock('repository')
1926
# we hard code the formats here because we are converting into
1927
# the meta format. The meta format upgrader can take this to a
1928
# future format within each component.
1929
self.put_format('repository', _mod_repository.RepositoryFormat7())
1930
for entry in repository_names:
1931
self.move_entry('repository', entry)
1933
self.step('Upgrading branch ')
1934
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1935
self.make_lock('branch')
1936
self.put_format('branch', BzrBranchFormat5())
1937
branch_files = [('revision-history', True),
1938
('branch-name', True),
1940
for entry in branch_files:
1941
self.move_entry('branch', entry)
1943
checkout_files = [('pending-merges', True),
1944
('inventory', True),
1945
('stat-cache', False)]
1946
# If a mandatory checkout file is not present, the branch does not have
1947
# a functional checkout. Do not create a checkout in the converted
1949
for name, mandatory in checkout_files:
1950
if mandatory and name not in bzrcontents:
1951
has_checkout = False
1955
if not has_checkout:
1956
self.pb.note('No working tree.')
1957
# If some checkout files are there, we may as well get rid of them.
1958
for name, mandatory in checkout_files:
1959
if name in bzrcontents:
1960
self.bzrdir.transport.delete(name)
1962
from bzrlib.workingtree import WorkingTreeFormat3
1963
self.step('Upgrading working tree')
1964
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1965
self.make_lock('checkout')
1967
'checkout', WorkingTreeFormat3())
1968
self.bzrdir.transport.delete_multi(
1969
self.garbage_inventories, self.pb)
1970
for entry in checkout_files:
1971
self.move_entry('checkout', entry)
1972
if last_revision is not None:
1973
self.bzrdir._control_files.put_utf8(
1974
'checkout/last-revision', last_revision)
1975
self.bzrdir._control_files.put_utf8(
1976
'branch-format', BzrDirMetaFormat1().get_format_string())
1977
return BzrDir.open(self.bzrdir.root_transport.base)
1979
def make_lock(self, name):
1980
"""Make a lock for the new control dir name."""
1981
self.step('Make %s lock' % name)
1982
ld = lockdir.LockDir(self.bzrdir.transport,
1984
file_modebits=self.file_mode,
1985
dir_modebits=self.dir_mode)
1988
def move_entry(self, new_dir, entry):
1989
"""Move then entry name into new_dir."""
1991
mandatory = entry[1]
1992
self.step('Moving %s' % name)
1994
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1995
except errors.NoSuchFile:
1999
def put_format(self, dirname, format):
2000
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2003
class ConvertMetaToMeta(Converter):
2004
"""Converts the components of metadirs."""
2006
def __init__(self, target_format):
2007
"""Create a metadir to metadir converter.
2009
:param target_format: The final metadir format that is desired.
2011
self.target_format = target_format
2013
def convert(self, to_convert, pb):
2014
"""See Converter.convert()."""
2015
self.bzrdir = to_convert
2019
self.step('checking repository format')
2021
repo = self.bzrdir.open_repository()
2022
except errors.NoRepositoryPresent:
2025
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2026
from bzrlib.repository import CopyConverter
2027
self.pb.note('starting repository conversion')
2028
converter = CopyConverter(self.target_format.repository_format)
2029
converter.convert(repo, pb)
2033
class BzrDirFormatInfo(object):
2035
def __init__(self, native, deprecated):
2036
self.deprecated = deprecated
2037
self.native = native
2040
class BzrDirFormatRegistry(registry.Registry):
2041
"""Registry of user-selectable BzrDir subformats.
2043
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2044
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2047
def register_metadir(self, key, repo, help, native=True, deprecated=False):
2048
"""Register a metadir subformat.
2050
repo is the repository format name as a string.
2052
# This should be expanded to support setting WorkingTree and Branch
2053
# formats, once BzrDirMetaFormat1 supports that.
2055
import bzrlib.repository
2056
repo_format = getattr(bzrlib.repository, repo)
2057
bd = BzrDirMetaFormat1()
2058
bd.repository_format = repo_format()
2060
self.register(key, helper, help, native, deprecated)
2062
def register(self, key, factory, help, native=True, deprecated=False):
2063
"""Register a BzrDirFormat factory.
2065
The factory must be a callable that takes one parameter: the key.
2066
It must produce an instance of the BzrDirFormat when called.
2068
This function mainly exists to prevent the info object from being
2071
registry.Registry.register(self, key, factory, help,
2072
BzrDirFormatInfo(native, deprecated))
2074
def register_lazy(self, key, module_name, member_name, help, native=True,
2076
registry.Registry.register_lazy(self, key, module_name, member_name,
2077
help, BzrDirFormatInfo(native, deprecated))
2079
def set_default(self, key):
2080
"""Set the 'default' key to be a clone of the supplied key.
2082
This method must be called once and only once.
2084
registry.Registry.register(self, 'default', self.get(key),
2085
self.get_help(key), info=self.get_info(key))
2087
def set_default_repository(self, key):
2088
"""Set the FormatRegistry default and Repository default.
2090
This is a transitional method while Repository.set_default_format
2093
if 'default' in self:
2094
self.remove('default')
2095
self.set_default(key)
2096
format = self.get('default')()
2097
assert isinstance(format, BzrDirMetaFormat1)
2098
from bzrlib import repository
2099
repository.RepositoryFormat._set_default_format(
2100
format.repository_format)
2102
def make_bzrdir(self, key):
2103
return self.get(key)()
2105
def help_topic(self, topic):
2106
output = textwrap.dedent("""\
2107
Bazaar directory formats
2108
------------------------
2110
These formats can be used for creating branches, working trees, and
2114
default_help = self.get_help('default')
2116
for key in self.keys():
2117
if key == 'default':
2119
help = self.get_help(key)
2120
if help == default_help:
2121
default_realkey = key
2123
help_pairs.append((key, help))
2125
def wrapped(key, help, info):
2127
help = '(native) ' + help
2128
return ' %s:\n%s\n\n' % (key,
2129
textwrap.fill(help, initial_indent=' ',
2130
subsequent_indent=' '))
2131
output += wrapped('%s/default' % default_realkey, default_help,
2132
self.get_info('default'))
2133
deprecated_pairs = []
2134
for key, help in help_pairs:
2135
info = self.get_info(key)
2137
deprecated_pairs.append((key, help))
2139
output += wrapped(key, help, info)
2140
if len(deprecated_pairs) > 0:
2141
output += "Deprecated formats\n------------------\n\n"
2142
for key, help in deprecated_pairs:
2143
info = self.get_info(key)
2144
output += wrapped(key, help, info)
2149
format_registry = BzrDirFormatRegistry()
2150
format_registry.register('weave', BzrDirFormat6,
2151
'Pre-0.8 format. Slower than knit and does not'
2152
' support checkouts or shared repositories.', deprecated=True)
2153
format_registry.register_metadir('knit', 'RepositoryFormatKnit1',
2154
'Format using knits. Recommended.')
2155
format_registry.set_default('knit')
2156
format_registry.register_metadir('metaweave', 'RepositoryFormat7',
2157
'Transitional format in 0.8. Slower than knit.',
2159
format_registry.register_metadir('experimental-knit2', 'RepositoryFormatKnit2',
2160
'Experimental successor to knit. Use at your own risk.')