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
hints = transport.create_get_hints(follow_redirections=False)
1518
format_file = transport.get(".bzr/branch-format",**hints)
1519
format_string = format_file.read()
1520
except errors.NoSuchFile:
1521
raise errors.NotBranchError(path=transport.base)
1524
return klass._formats[format_string]
1526
raise errors.UnknownFormatError(format=format_string)
1528
# REVIEWER: The following line is black magic for me. Any
1529
# pointer appreciated
1530
repository_format = property(__return_repository_format, __set_repository_format)
1533
# register BzrDirMetaFormat1 as a control format. Our only
1534
# concrete control format (BzrDirFormat is an abstract one).
1535
BzrDirFormat.register_control_format(BzrDirMetaFormat1)
1537
# Register bzr formats
1538
BzrDirFormat.register_format(BzrDirFormat4())
1539
BzrDirFormat.register_format(BzrDirFormat5())
1540
BzrDirFormat.register_format(BzrDirFormat6())
1541
__default_format = BzrDirMetaFormat1()
1542
BzrDirFormat.register_format(__default_format)
1543
BzrDirFormat._default_format = __default_format
1546
class BzrDirTestProviderAdapter(object):
1547
"""A tool to generate a suite testing multiple bzrdir formats at once.
1549
This is done by copying the test once for each transport and injecting
1550
the transport_server, transport_readonly_server, and bzrdir_format
1551
classes into each copy. Each copy is also given a new id() to make it
1555
def __init__(self, transport_server, transport_readonly_server, formats):
1556
self._transport_server = transport_server
1557
self._transport_readonly_server = transport_readonly_server
1558
self._formats = formats
1560
def adapt(self, test):
1561
result = unittest.TestSuite()
1562
for format in self._formats:
1563
new_test = deepcopy(test)
1564
new_test.transport_server = self._transport_server
1565
new_test.transport_readonly_server = self._transport_readonly_server
1566
new_test.bzrdir_format = format
1567
def make_new_test_id():
1568
new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1569
return lambda: new_id
1570
new_test.id = make_new_test_id()
1571
result.addTest(new_test)
1575
class Converter(object):
1576
"""Converts a disk format object from one format to another."""
1578
def convert(self, to_convert, pb):
1579
"""Perform the conversion of to_convert, giving feedback via pb.
1581
:param to_convert: The disk object to convert.
1582
:param pb: a progress bar to use for progress information.
1585
def step(self, message):
1586
"""Update the pb by a step."""
1588
self.pb.update(message, self.count, self.total)
1591
class ConvertBzrDir4To5(Converter):
1592
"""Converts format 4 bzr dirs to format 5."""
1595
super(ConvertBzrDir4To5, self).__init__()
1596
self.converted_revs = set()
1597
self.absent_revisions = set()
1601
def convert(self, to_convert, pb):
1602
"""See Converter.convert()."""
1603
self.bzrdir = to_convert
1605
self.pb.note('starting upgrade from format 4 to 5')
1606
if isinstance(self.bzrdir.transport, LocalTransport):
1607
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1608
self._convert_to_weaves()
1609
return BzrDir.open(self.bzrdir.root_transport.base)
1611
def _convert_to_weaves(self):
1612
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
1615
stat = self.bzrdir.transport.stat('weaves')
1616
if not S_ISDIR(stat.st_mode):
1617
self.bzrdir.transport.delete('weaves')
1618
self.bzrdir.transport.mkdir('weaves')
1619
except errors.NoSuchFile:
1620
self.bzrdir.transport.mkdir('weaves')
1621
# deliberately not a WeaveFile as we want to build it up slowly.
1622
self.inv_weave = Weave('inventory')
1623
# holds in-memory weaves for all files
1624
self.text_weaves = {}
1625
self.bzrdir.transport.delete('branch-format')
1626
self.branch = self.bzrdir.open_branch()
1627
self._convert_working_inv()
1628
rev_history = self.branch.revision_history()
1629
# to_read is a stack holding the revisions we still need to process;
1630
# appending to it adds new highest-priority revisions
1631
self.known_revisions = set(rev_history)
1632
self.to_read = rev_history[-1:]
1634
rev_id = self.to_read.pop()
1635
if (rev_id not in self.revisions
1636
and rev_id not in self.absent_revisions):
1637
self._load_one_rev(rev_id)
1639
to_import = self._make_order()
1640
for i, rev_id in enumerate(to_import):
1641
self.pb.update('converting revision', i, len(to_import))
1642
self._convert_one_rev(rev_id)
1644
self._write_all_weaves()
1645
self._write_all_revs()
1646
self.pb.note('upgraded to weaves:')
1647
self.pb.note(' %6d revisions and inventories', len(self.revisions))
1648
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
1649
self.pb.note(' %6d texts', self.text_count)
1650
self._cleanup_spare_files_after_format4()
1651
self.branch.control_files.put_utf8('branch-format', BzrDirFormat5().get_format_string())
1653
def _cleanup_spare_files_after_format4(self):
1654
# FIXME working tree upgrade foo.
1655
for n in 'merged-patches', 'pending-merged-patches':
1657
## assert os.path.getsize(p) == 0
1658
self.bzrdir.transport.delete(n)
1659
except errors.NoSuchFile:
1661
self.bzrdir.transport.delete_tree('inventory-store')
1662
self.bzrdir.transport.delete_tree('text-store')
1664
def _convert_working_inv(self):
1665
inv = xml4.serializer_v4.read_inventory(
1666
self.branch.control_files.get('inventory'))
1667
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1668
# FIXME inventory is a working tree change.
1669
self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1671
def _write_all_weaves(self):
1672
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
1673
weave_transport = self.bzrdir.transport.clone('weaves')
1674
weaves = WeaveStore(weave_transport, prefixed=False)
1675
transaction = WriteTransaction()
1679
for file_id, file_weave in self.text_weaves.items():
1680
self.pb.update('writing weave', i, len(self.text_weaves))
1681
weaves._put_weave(file_id, file_weave, transaction)
1683
self.pb.update('inventory', 0, 1)
1684
controlweaves._put_weave('inventory', self.inv_weave, transaction)
1685
self.pb.update('inventory', 1, 1)
1689
def _write_all_revs(self):
1690
"""Write all revisions out in new form."""
1691
self.bzrdir.transport.delete_tree('revision-store')
1692
self.bzrdir.transport.mkdir('revision-store')
1693
revision_transport = self.bzrdir.transport.clone('revision-store')
1695
_revision_store = TextRevisionStore(TextStore(revision_transport,
1699
transaction = WriteTransaction()
1700
for i, rev_id in enumerate(self.converted_revs):
1701
self.pb.update('write revision', i, len(self.converted_revs))
1702
_revision_store.add_revision(self.revisions[rev_id], transaction)
1706
def _load_one_rev(self, rev_id):
1707
"""Load a revision object into memory.
1709
Any parents not either loaded or abandoned get queued to be
1711
self.pb.update('loading revision',
1712
len(self.revisions),
1713
len(self.known_revisions))
1714
if not self.branch.repository.has_revision(rev_id):
1716
self.pb.note('revision {%s} not present in branch; '
1717
'will be converted as a ghost',
1719
self.absent_revisions.add(rev_id)
1721
rev = self.branch.repository._revision_store.get_revision(rev_id,
1722
self.branch.repository.get_transaction())
1723
for parent_id in rev.parent_ids:
1724
self.known_revisions.add(parent_id)
1725
self.to_read.append(parent_id)
1726
self.revisions[rev_id] = rev
1728
def _load_old_inventory(self, rev_id):
1729
assert rev_id not in self.converted_revs
1730
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1731
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1732
inv.revision_id = rev_id
1733
rev = self.revisions[rev_id]
1734
if rev.inventory_sha1:
1735
assert rev.inventory_sha1 == sha_string(old_inv_xml), \
1736
'inventory sha mismatch for {%s}' % rev_id
1739
def _load_updated_inventory(self, rev_id):
1740
assert rev_id in self.converted_revs
1741
inv_xml = self.inv_weave.get_text(rev_id)
1742
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1745
def _convert_one_rev(self, rev_id):
1746
"""Convert revision and all referenced objects to new format."""
1747
rev = self.revisions[rev_id]
1748
inv = self._load_old_inventory(rev_id)
1749
present_parents = [p for p in rev.parent_ids
1750
if p not in self.absent_revisions]
1751
self._convert_revision_contents(rev, inv, present_parents)
1752
self._store_new_weave(rev, inv, present_parents)
1753
self.converted_revs.add(rev_id)
1755
def _store_new_weave(self, rev, inv, present_parents):
1756
# the XML is now updated with text versions
1758
entries = inv.iter_entries()
1760
for path, ie in entries:
1761
assert getattr(ie, 'revision', None) is not None, \
1762
'no revision on {%s} in {%s}' % \
1763
(file_id, rev.revision_id)
1764
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1765
new_inv_sha1 = sha_string(new_inv_xml)
1766
self.inv_weave.add_lines(rev.revision_id,
1768
new_inv_xml.splitlines(True))
1769
rev.inventory_sha1 = new_inv_sha1
1771
def _convert_revision_contents(self, rev, inv, present_parents):
1772
"""Convert all the files within a revision.
1774
Also upgrade the inventory to refer to the text revision ids."""
1775
rev_id = rev.revision_id
1776
mutter('converting texts of revision {%s}',
1778
parent_invs = map(self._load_updated_inventory, present_parents)
1779
entries = inv.iter_entries()
1781
for path, ie in entries:
1782
self._convert_file_version(rev, ie, parent_invs)
1784
def _convert_file_version(self, rev, ie, parent_invs):
1785
"""Convert one version of one file.
1787
The file needs to be added into the weave if it is a merge
1788
of >=2 parents or if it's changed from its parent.
1790
file_id = ie.file_id
1791
rev_id = rev.revision_id
1792
w = self.text_weaves.get(file_id)
1795
self.text_weaves[file_id] = w
1796
text_changed = False
1797
previous_entries = ie.find_previous_heads(parent_invs,
1801
for old_revision in previous_entries:
1802
# if this fails, its a ghost ?
1803
assert old_revision in self.converted_revs, \
1804
"Revision {%s} not in converted_revs" % old_revision
1805
self.snapshot_ie(previous_entries, ie, w, rev_id)
1807
assert getattr(ie, 'revision', None) is not None
1809
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
1810
# TODO: convert this logic, which is ~= snapshot to
1811
# a call to:. This needs the path figured out. rather than a work_tree
1812
# a v4 revision_tree can be given, or something that looks enough like
1813
# one to give the file content to the entry if it needs it.
1814
# and we need something that looks like a weave store for snapshot to
1816
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
1817
if len(previous_revisions) == 1:
1818
previous_ie = previous_revisions.values()[0]
1819
if ie._unchanged(previous_ie):
1820
ie.revision = previous_ie.revision
1823
text = self.branch.repository.text_store.get(ie.text_id)
1824
file_lines = text.readlines()
1825
assert sha_strings(file_lines) == ie.text_sha1
1826
assert sum(map(len, file_lines)) == ie.text_size
1827
w.add_lines(rev_id, previous_revisions, file_lines)
1828
self.text_count += 1
1830
w.add_lines(rev_id, previous_revisions, [])
1831
ie.revision = rev_id
1833
def _make_order(self):
1834
"""Return a suitable order for importing revisions.
1836
The order must be such that an revision is imported after all
1837
its (present) parents.
1839
todo = set(self.revisions.keys())
1840
done = self.absent_revisions.copy()
1843
# scan through looking for a revision whose parents
1845
for rev_id in sorted(list(todo)):
1846
rev = self.revisions[rev_id]
1847
parent_ids = set(rev.parent_ids)
1848
if parent_ids.issubset(done):
1849
# can take this one now
1850
order.append(rev_id)
1856
class ConvertBzrDir5To6(Converter):
1857
"""Converts format 5 bzr dirs to format 6."""
1859
def convert(self, to_convert, pb):
1860
"""See Converter.convert()."""
1861
self.bzrdir = to_convert
1863
self.pb.note('starting upgrade from format 5 to 6')
1864
self._convert_to_prefixed()
1865
return BzrDir.open(self.bzrdir.root_transport.base)
1867
def _convert_to_prefixed(self):
1868
from bzrlib.store import TransportStore
1869
self.bzrdir.transport.delete('branch-format')
1870
for store_name in ["weaves", "revision-store"]:
1871
self.pb.note("adding prefixes to %s" % store_name)
1872
store_transport = self.bzrdir.transport.clone(store_name)
1873
store = TransportStore(store_transport, prefixed=True)
1874
for urlfilename in store_transport.list_dir('.'):
1875
filename = urlutils.unescape(urlfilename)
1876
if (filename.endswith(".weave") or
1877
filename.endswith(".gz") or
1878
filename.endswith(".sig")):
1879
file_id = os.path.splitext(filename)[0]
1882
prefix_dir = store.hash_prefix(file_id)
1883
# FIXME keep track of the dirs made RBC 20060121
1885
store_transport.move(filename, prefix_dir + '/' + filename)
1886
except errors.NoSuchFile: # catches missing dirs strangely enough
1887
store_transport.mkdir(prefix_dir)
1888
store_transport.move(filename, prefix_dir + '/' + filename)
1889
self.bzrdir._control_files.put_utf8('branch-format', BzrDirFormat6().get_format_string())
1892
class ConvertBzrDir6ToMeta(Converter):
1893
"""Converts format 6 bzr dirs to metadirs."""
1895
def convert(self, to_convert, pb):
1896
"""See Converter.convert()."""
1897
from bzrlib.branch import BzrBranchFormat5
1898
self.bzrdir = to_convert
1901
self.total = 20 # the steps we know about
1902
self.garbage_inventories = []
1904
self.pb.note('starting upgrade from format 6 to metadir')
1905
self.bzrdir._control_files.put_utf8('branch-format', "Converting to format 6")
1906
# its faster to move specific files around than to open and use the apis...
1907
# first off, nuke ancestry.weave, it was never used.
1909
self.step('Removing ancestry.weave')
1910
self.bzrdir.transport.delete('ancestry.weave')
1911
except errors.NoSuchFile:
1913
# find out whats there
1914
self.step('Finding branch files')
1915
last_revision = self.bzrdir.open_branch().last_revision()
1916
bzrcontents = self.bzrdir.transport.list_dir('.')
1917
for name in bzrcontents:
1918
if name.startswith('basis-inventory.'):
1919
self.garbage_inventories.append(name)
1920
# create new directories for repository, working tree and branch
1921
self.dir_mode = self.bzrdir._control_files._dir_mode
1922
self.file_mode = self.bzrdir._control_files._file_mode
1923
repository_names = [('inventory.weave', True),
1924
('revision-store', True),
1926
self.step('Upgrading repository ')
1927
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
1928
self.make_lock('repository')
1929
# we hard code the formats here because we are converting into
1930
# the meta format. The meta format upgrader can take this to a
1931
# future format within each component.
1932
self.put_format('repository', _mod_repository.RepositoryFormat7())
1933
for entry in repository_names:
1934
self.move_entry('repository', entry)
1936
self.step('Upgrading branch ')
1937
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1938
self.make_lock('branch')
1939
self.put_format('branch', BzrBranchFormat5())
1940
branch_files = [('revision-history', True),
1941
('branch-name', True),
1943
for entry in branch_files:
1944
self.move_entry('branch', entry)
1946
checkout_files = [('pending-merges', True),
1947
('inventory', True),
1948
('stat-cache', False)]
1949
# If a mandatory checkout file is not present, the branch does not have
1950
# a functional checkout. Do not create a checkout in the converted
1952
for name, mandatory in checkout_files:
1953
if mandatory and name not in bzrcontents:
1954
has_checkout = False
1958
if not has_checkout:
1959
self.pb.note('No working tree.')
1960
# If some checkout files are there, we may as well get rid of them.
1961
for name, mandatory in checkout_files:
1962
if name in bzrcontents:
1963
self.bzrdir.transport.delete(name)
1965
from bzrlib.workingtree import WorkingTreeFormat3
1966
self.step('Upgrading working tree')
1967
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1968
self.make_lock('checkout')
1970
'checkout', WorkingTreeFormat3())
1971
self.bzrdir.transport.delete_multi(
1972
self.garbage_inventories, self.pb)
1973
for entry in checkout_files:
1974
self.move_entry('checkout', entry)
1975
if last_revision is not None:
1976
self.bzrdir._control_files.put_utf8(
1977
'checkout/last-revision', last_revision)
1978
self.bzrdir._control_files.put_utf8(
1979
'branch-format', BzrDirMetaFormat1().get_format_string())
1980
return BzrDir.open(self.bzrdir.root_transport.base)
1982
def make_lock(self, name):
1983
"""Make a lock for the new control dir name."""
1984
self.step('Make %s lock' % name)
1985
ld = lockdir.LockDir(self.bzrdir.transport,
1987
file_modebits=self.file_mode,
1988
dir_modebits=self.dir_mode)
1991
def move_entry(self, new_dir, entry):
1992
"""Move then entry name into new_dir."""
1994
mandatory = entry[1]
1995
self.step('Moving %s' % name)
1997
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
1998
except errors.NoSuchFile:
2002
def put_format(self, dirname, format):
2003
self.bzrdir._control_files.put_utf8('%s/format' % dirname, format.get_format_string())
2006
class ConvertMetaToMeta(Converter):
2007
"""Converts the components of metadirs."""
2009
def __init__(self, target_format):
2010
"""Create a metadir to metadir converter.
2012
:param target_format: The final metadir format that is desired.
2014
self.target_format = target_format
2016
def convert(self, to_convert, pb):
2017
"""See Converter.convert()."""
2018
self.bzrdir = to_convert
2022
self.step('checking repository format')
2024
repo = self.bzrdir.open_repository()
2025
except errors.NoRepositoryPresent:
2028
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2029
from bzrlib.repository import CopyConverter
2030
self.pb.note('starting repository conversion')
2031
converter = CopyConverter(self.target_format.repository_format)
2032
converter.convert(repo, pb)
2036
class BzrDirFormatInfo(object):
2038
def __init__(self, native, deprecated):
2039
self.deprecated = deprecated
2040
self.native = native
2043
class BzrDirFormatRegistry(registry.Registry):
2044
"""Registry of user-selectable BzrDir subformats.
2046
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2047
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2050
def register_metadir(self, key, repo, help, native=True, deprecated=False):
2051
"""Register a metadir subformat.
2053
repo is the repository format name as a string.
2055
# This should be expanded to support setting WorkingTree and Branch
2056
# formats, once BzrDirMetaFormat1 supports that.
2058
import bzrlib.repository
2059
repo_format = getattr(bzrlib.repository, repo)
2060
bd = BzrDirMetaFormat1()
2061
bd.repository_format = repo_format()
2063
self.register(key, helper, help, native, deprecated)
2065
def register(self, key, factory, help, native=True, deprecated=False):
2066
"""Register a BzrDirFormat factory.
2068
The factory must be a callable that takes one parameter: the key.
2069
It must produce an instance of the BzrDirFormat when called.
2071
This function mainly exists to prevent the info object from being
2074
registry.Registry.register(self, key, factory, help,
2075
BzrDirFormatInfo(native, deprecated))
2077
def register_lazy(self, key, module_name, member_name, help, native=True,
2079
registry.Registry.register_lazy(self, key, module_name, member_name,
2080
help, BzrDirFormatInfo(native, deprecated))
2082
def set_default(self, key):
2083
"""Set the 'default' key to be a clone of the supplied key.
2085
This method must be called once and only once.
2087
registry.Registry.register(self, 'default', self.get(key),
2088
self.get_help(key), info=self.get_info(key))
2090
def set_default_repository(self, key):
2091
"""Set the FormatRegistry default and Repository default.
2093
This is a transitional method while Repository.set_default_format
2096
if 'default' in self:
2097
self.remove('default')
2098
self.set_default(key)
2099
format = self.get('default')()
2100
assert isinstance(format, BzrDirMetaFormat1)
2101
from bzrlib import repository
2102
repository.RepositoryFormat._set_default_format(
2103
format.repository_format)
2105
def make_bzrdir(self, key):
2106
return self.get(key)()
2108
def help_topic(self, topic):
2109
output = textwrap.dedent("""\
2110
Bazaar directory formats
2111
------------------------
2113
These formats can be used for creating branches, working trees, and
2117
default_help = self.get_help('default')
2119
for key in self.keys():
2120
if key == 'default':
2122
help = self.get_help(key)
2123
if help == default_help:
2124
default_realkey = key
2126
help_pairs.append((key, help))
2128
def wrapped(key, help, info):
2130
help = '(native) ' + help
2131
return ' %s:\n%s\n\n' % (key,
2132
textwrap.fill(help, initial_indent=' ',
2133
subsequent_indent=' '))
2134
output += wrapped('%s/default' % default_realkey, default_help,
2135
self.get_info('default'))
2136
deprecated_pairs = []
2137
for key, help in help_pairs:
2138
info = self.get_info(key)
2140
deprecated_pairs.append((key, help))
2142
output += wrapped(key, help, info)
2143
if len(deprecated_pairs) > 0:
2144
output += "Deprecated formats\n------------------\n\n"
2145
for key, help in deprecated_pairs:
2146
info = self.get_info(key)
2147
output += wrapped(key, help, info)
2152
format_registry = BzrDirFormatRegistry()
2153
format_registry.register('weave', BzrDirFormat6,
2154
'Pre-0.8 format. Slower than knit and does not'
2155
' support checkouts or shared repositories.', deprecated=True)
2156
format_registry.register_metadir('knit', 'RepositoryFormatKnit1',
2157
'Format using knits. Recommended.')
2158
format_registry.set_default('knit')
2159
format_registry.register_metadir('metaweave', 'RepositoryFormat7',
2160
'Transitional format in 0.8. Slower than knit.',
2162
format_registry.register_metadir('experimental-knit2', 'RepositoryFormatKnit2',
2163
'Experimental successor to knit. Use at your own risk.')