1
# Copyright (C) 2005, 2006, 2007, 2008 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
22
Note: This module has a lot of ``open`` functions/methods that return
23
references to in-memory objects. As a rule, there are no matching ``close``
24
methods. To free any associated resources, simply stop referencing the
28
# TODO: Move old formats into a plugin to make this file smaller.
30
from cStringIO import StringIO
34
from bzrlib.lazy_import import lazy_import
35
lazy_import(globals(), """
36
from stat import S_ISDIR
38
from warnings import warn
49
revision as _mod_revision,
60
from bzrlib.osutils import (
64
from bzrlib.repository import Repository
65
from bzrlib.smart.client import _SmartClient
66
from bzrlib.smart import protocol
67
from bzrlib.store.versioned import WeaveStore
68
from bzrlib.transactions import WriteTransaction
69
from bzrlib.transport import (
70
do_catching_redirections,
73
from bzrlib.weave import Weave
76
from bzrlib.trace import (
80
from bzrlib.transport.local import LocalTransport
81
from bzrlib.symbol_versioning import (
88
"""A .bzr control diretory.
90
BzrDir instances let you create or open any of the things that can be
91
found within .bzr - checkouts, branches and repositories.
94
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
96
a transport connected to the directory this bzr was opened from
97
(i.e. the parent directory holding the .bzr directory).
99
Everything in the bzrdir should have the same file permissions.
102
def break_lock(self):
103
"""Invoke break_lock on the first object in the bzrdir.
105
If there is a tree, the tree is opened and break_lock() called.
106
Otherwise, branch is tried, and finally repository.
108
# XXX: This seems more like a UI function than something that really
109
# belongs in this class.
111
thing_to_unlock = self.open_workingtree()
112
except (errors.NotLocalUrl, errors.NoWorkingTree):
114
thing_to_unlock = self.open_branch()
115
except errors.NotBranchError:
117
thing_to_unlock = self.open_repository()
118
except errors.NoRepositoryPresent:
120
thing_to_unlock.break_lock()
122
def can_convert_format(self):
123
"""Return true if this bzrdir is one whose format we can convert from."""
126
def check_conversion_target(self, target_format):
127
target_repo_format = target_format.repository_format
128
source_repo_format = self._format.repository_format
129
source_repo_format.check_conversion_target(target_repo_format)
132
def _check_supported(format, allow_unsupported,
133
recommend_upgrade=True,
135
"""Give an error or warning on old formats.
137
:param format: may be any kind of format - workingtree, branch,
140
:param allow_unsupported: If true, allow opening
141
formats that are strongly deprecated, and which may
142
have limited functionality.
144
:param recommend_upgrade: If true (default), warn
145
the user through the ui object that they may wish
146
to upgrade the object.
148
# TODO: perhaps move this into a base Format class; it's not BzrDir
149
# specific. mbp 20070323
150
if not allow_unsupported and not format.is_supported():
151
# see open_downlevel to open legacy branches.
152
raise errors.UnsupportedFormatError(format=format)
153
if recommend_upgrade \
154
and getattr(format, 'upgrade_recommended', False):
155
ui.ui_factory.recommend_upgrade(
156
format.get_format_description(),
159
def clone(self, url, revision_id=None, force_new_repo=False):
160
"""Clone this bzrdir and its contents to url verbatim.
162
If url's last component does not exist, it will be created.
164
if revision_id is not None, then the clone operation may tune
165
itself to download less data.
166
:param force_new_repo: Do not use a shared repository for the target
167
even if one is available.
169
return self.clone_on_transport(get_transport(url),
170
revision_id=revision_id,
171
force_new_repo=force_new_repo)
173
def clone_on_transport(self, transport, revision_id=None,
174
force_new_repo=False):
175
"""Clone this bzrdir and its contents to transport verbatim.
177
If the target directory does not exist, it will be created.
179
if revision_id is not None, then the clone operation may tune
180
itself to download less data.
181
:param force_new_repo: Do not use a shared repository for the target
182
even if one is available.
184
transport.ensure_base()
185
result = self.cloning_metadir().initialize_on_transport(transport)
186
repository_policy = None
188
local_repo = self.find_repository()
189
except errors.NoRepositoryPresent:
192
# may need to copy content in
193
repository_policy = result.determine_repository_policy(
195
make_working_trees = local_repo.make_working_trees()
196
result_repo = repository_policy.acquire_repository(
197
make_working_trees, local_repo.is_shared())
198
result_repo.fetch(local_repo, revision_id=revision_id)
199
# 1 if there is a branch present
200
# make sure its content is available in the target repository
203
local_branch = self.open_branch()
204
except errors.NotBranchError:
207
result_branch = local_branch.clone(result, revision_id=revision_id)
208
if repository_policy is not None:
209
repository_policy.configure_branch(result_branch)
211
result_repo = result.find_repository()
212
except errors.NoRepositoryPresent:
214
if result_repo is None or result_repo.make_working_trees():
216
self.open_workingtree().clone(result)
217
except (errors.NoWorkingTree, errors.NotLocalUrl):
221
# TODO: This should be given a Transport, and should chdir up; otherwise
222
# this will open a new connection.
223
def _make_tail(self, url):
224
t = get_transport(url)
228
def create(cls, base, format=None, possible_transports=None):
229
"""Create a new BzrDir at the url 'base'.
231
:param format: If supplied, the format of branch to create. If not
232
supplied, the default is used.
233
:param possible_transports: If supplied, a list of transports that
234
can be reused to share a remote connection.
236
if cls is not BzrDir:
237
raise AssertionError("BzrDir.create always creates the default"
238
" format, not one of %r" % cls)
239
t = get_transport(base, possible_transports)
242
format = BzrDirFormat.get_default_format()
243
return format.initialize_on_transport(t)
246
def find_bzrdirs(transport, evaluate=None, list_current=None):
247
"""Find bzrdirs recursively from current location.
249
This is intended primarily as a building block for more sophisticated
250
functionality, like finding trees under a directory, or finding
251
branches that use a given repository.
252
:param evaluate: An optional callable that yields recurse, value,
253
where recurse controls whether this bzrdir is recursed into
254
and value is the value to yield. By default, all bzrdirs
255
are recursed into, and the return value is the bzrdir.
256
:param list_current: if supplied, use this function to list the current
257
directory, instead of Transport.list_dir
258
:return: a generator of found bzrdirs, or whatever evaluate returns.
260
if list_current is None:
261
def list_current(transport):
262
return transport.list_dir('')
264
def evaluate(bzrdir):
267
pending = [transport]
268
while len(pending) > 0:
269
current_transport = pending.pop()
272
bzrdir = BzrDir.open_from_transport(current_transport)
273
except errors.NotBranchError:
276
recurse, value = evaluate(bzrdir)
279
subdirs = list_current(current_transport)
280
except errors.NoSuchFile:
283
for subdir in sorted(subdirs, reverse=True):
284
pending.append(current_transport.clone(subdir))
287
def find_branches(transport):
288
"""Find all branches under a transport.
290
This will find all branches below the transport, including branches
291
inside other branches. Where possible, it will use
292
Repository.find_branches.
294
To list all the branches that use a particular Repository, see
295
Repository.find_branches
297
def evaluate(bzrdir):
299
repository = bzrdir.open_repository()
300
except errors.NoRepositoryPresent:
303
return False, (None, repository)
305
branch = bzrdir.open_branch()
306
except errors.NotBranchError:
307
return True, (None, None)
309
return True, (branch, None)
311
for branch, repo in BzrDir.find_bzrdirs(transport, evaluate=evaluate):
313
branches.extend(repo.find_branches())
314
if branch is not None:
315
branches.append(branch)
318
def destroy_repository(self):
319
"""Destroy the repository in this BzrDir"""
320
raise NotImplementedError(self.destroy_repository)
322
def create_branch(self):
323
"""Create a branch in this BzrDir.
325
The bzrdir's format will control what branch format is created.
326
For more control see BranchFormatXX.create(a_bzrdir).
328
raise NotImplementedError(self.create_branch)
330
def destroy_branch(self):
331
"""Destroy the branch in this BzrDir"""
332
raise NotImplementedError(self.destroy_branch)
335
def create_branch_and_repo(base, force_new_repo=False, format=None):
336
"""Create a new BzrDir, Branch and Repository at the url 'base'.
338
This will use the current default BzrDirFormat unless one is
339
specified, and use whatever
340
repository format that that uses via bzrdir.create_branch and
341
create_repository. If a shared repository is available that is used
344
The created Branch object is returned.
346
:param base: The URL to create the branch at.
347
:param force_new_repo: If True a new repository is always created.
348
:param format: If supplied, the format of branch to create. If not
349
supplied, the default is used.
351
bzrdir = BzrDir.create(base, format)
352
bzrdir._find_or_create_repository(force_new_repo)
353
return bzrdir.create_branch()
355
def determine_repository_policy(self, force_new_repo=False):
356
"""Return an object representing a policy to use.
358
This controls whether a new repository is created, or a shared
359
repository used instead.
361
def repository_policy(found_bzrdir):
363
# does it have a repository ?
365
repository = found_bzrdir.open_repository()
366
except errors.NoRepositoryPresent:
369
if ((found_bzrdir.root_transport.base !=
370
self.root_transport.base) and not repository.is_shared()):
377
return UseExistingRepository(repository), True
379
return CreateRepository(self), True
381
if not force_new_repo:
382
policy = self._find_containing(repository_policy)
383
if policy is not None:
385
return CreateRepository(self)
387
def _find_or_create_repository(self, force_new_repo):
388
"""Create a new repository if needed, returning the repository."""
389
policy = self.determine_repository_policy(force_new_repo)
390
return policy.acquire_repository()
393
def create_branch_convenience(base, force_new_repo=False,
394
force_new_tree=None, format=None,
395
possible_transports=None):
396
"""Create a new BzrDir, Branch and Repository at the url 'base'.
398
This is a convenience function - it will use an existing repository
399
if possible, can be told explicitly whether to create a working tree or
402
This will use the current default BzrDirFormat unless one is
403
specified, and use whatever
404
repository format that that uses via bzrdir.create_branch and
405
create_repository. If a shared repository is available that is used
406
preferentially. Whatever repository is used, its tree creation policy
409
The created Branch object is returned.
410
If a working tree cannot be made due to base not being a file:// url,
411
no error is raised unless force_new_tree is True, in which case no
412
data is created on disk and NotLocalUrl is raised.
414
:param base: The URL to create the branch at.
415
:param force_new_repo: If True a new repository is always created.
416
:param force_new_tree: If True or False force creation of a tree or
417
prevent such creation respectively.
418
:param format: Override for the bzrdir format to create.
419
:param possible_transports: An optional reusable transports list.
422
# check for non local urls
423
t = get_transport(base, possible_transports)
424
if not isinstance(t, LocalTransport):
425
raise errors.NotLocalUrl(base)
426
bzrdir = BzrDir.create(base, format, possible_transports)
427
repo = bzrdir._find_or_create_repository(force_new_repo)
428
result = bzrdir.create_branch()
429
if force_new_tree or (repo.make_working_trees() and
430
force_new_tree is None):
432
bzrdir.create_workingtree()
433
except errors.NotLocalUrl:
438
def create_standalone_workingtree(base, format=None):
439
"""Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
441
'base' must be a local path or a file:// url.
443
This will use the current default BzrDirFormat unless one is
444
specified, and use whatever
445
repository format that that uses for bzrdirformat.create_workingtree,
446
create_branch and create_repository.
448
:param format: Override for the bzrdir format to create.
449
:return: The WorkingTree object.
451
t = get_transport(base)
452
if not isinstance(t, LocalTransport):
453
raise errors.NotLocalUrl(base)
454
bzrdir = BzrDir.create_branch_and_repo(base,
456
format=format).bzrdir
457
return bzrdir.create_workingtree()
459
def create_workingtree(self, revision_id=None, from_branch=None,
460
accelerator_tree=None, hardlink=False):
461
"""Create a working tree at this BzrDir.
463
:param revision_id: create it as of this revision id.
464
:param from_branch: override bzrdir branch (for lightweight checkouts)
465
:param accelerator_tree: A tree which can be used for retrieving file
466
contents more quickly than the revision tree, i.e. a workingtree.
467
The revision tree will be used for cases where accelerator_tree's
468
content is different.
470
raise NotImplementedError(self.create_workingtree)
472
def retire_bzrdir(self, limit=10000):
473
"""Permanently disable the bzrdir.
475
This is done by renaming it to give the user some ability to recover
476
if there was a problem.
478
This will have horrible consequences if anyone has anything locked or
480
:param limit: number of times to retry
485
to_path = '.bzr.retired.%d' % i
486
self.root_transport.rename('.bzr', to_path)
487
note("renamed %s to %s"
488
% (self.root_transport.abspath('.bzr'), to_path))
490
except (errors.TransportError, IOError, errors.PathError):
497
def destroy_workingtree(self):
498
"""Destroy the working tree at this BzrDir.
500
Formats that do not support this may raise UnsupportedOperation.
502
raise NotImplementedError(self.destroy_workingtree)
504
def destroy_workingtree_metadata(self):
505
"""Destroy the control files for the working tree at this BzrDir.
507
The contents of working tree files are not affected.
508
Formats that do not support this may raise UnsupportedOperation.
510
raise NotImplementedError(self.destroy_workingtree_metadata)
512
def _find_containing(self, evaluate):
513
"""Find something in a containing control directory.
515
This method will scan containing control dirs, until it finds what
516
it is looking for, decides that it will never find it, or runs out
517
of containing control directories to check.
519
It is used to implement find_repository and
520
determine_repository_policy.
522
:param evaluate: A function returning (value, stop). If stop is True,
523
the value will be returned.
527
result, stop = evaluate(found_bzrdir)
530
next_transport = found_bzrdir.root_transport.clone('..')
531
if (found_bzrdir.root_transport.base == next_transport.base):
532
# top of the file system
534
# find the next containing bzrdir
536
found_bzrdir = BzrDir.open_containing_from_transport(
538
except errors.NotBranchError:
541
def find_repository(self):
542
"""Find the repository that should be used.
544
This does not require a branch as we use it to find the repo for
545
new branches as well as to hook existing branches up to their
548
def usable_repository(found_bzrdir):
549
# does it have a repository ?
551
repository = found_bzrdir.open_repository()
552
except errors.NoRepositoryPresent:
554
if found_bzrdir.root_transport.base == self.root_transport.base:
555
return repository, True
556
elif repository.is_shared():
557
return repository, True
561
found_repo = self._find_containing(usable_repository)
562
if found_repo is None:
563
raise errors.NoRepositoryPresent(self)
566
def get_branch_reference(self):
567
"""Return the referenced URL for the branch in this bzrdir.
569
:raises NotBranchError: If there is no Branch.
570
:return: The URL the branch in this bzrdir references if it is a
571
reference branch, or None for regular branches.
575
def get_branch_transport(self, branch_format):
576
"""Get the transport for use by branch format in this BzrDir.
578
Note that bzr dirs that do not support format strings will raise
579
IncompatibleFormat if the branch format they are given has
580
a format string, and vice versa.
582
If branch_format is None, the transport is returned with no
583
checking. If it is not None, then the returned transport is
584
guaranteed to point to an existing directory ready for use.
586
raise NotImplementedError(self.get_branch_transport)
588
def _find_creation_modes(self):
589
"""Determine the appropriate modes for files and directories.
591
They're always set to be consistent with the base directory,
592
assuming that this transport allows setting modes.
594
# TODO: Do we need or want an option (maybe a config setting) to turn
595
# this off or override it for particular locations? -- mbp 20080512
596
if self._mode_check_done:
598
self._mode_check_done = True
600
st = self.transport.stat('.')
601
except errors.TransportNotPossible:
602
self._dir_mode = None
603
self._file_mode = None
605
# Check the directory mode, but also make sure the created
606
# directories and files are read-write for this user. This is
607
# mostly a workaround for filesystems which lie about being able to
608
# write to a directory (cygwin & win32)
609
self._dir_mode = (st.st_mode & 07777) | 00700
610
# Remove the sticky and execute bits for files
611
self._file_mode = self._dir_mode & ~07111
613
def _get_file_mode(self):
614
"""Return Unix mode for newly created files, or None.
616
if not self._mode_check_done:
617
self._find_creation_modes()
618
return self._file_mode
620
def _get_dir_mode(self):
621
"""Return Unix mode for newly created directories, or None.
623
if not self._mode_check_done:
624
self._find_creation_modes()
625
return self._dir_mode
627
def get_repository_transport(self, repository_format):
628
"""Get the transport for use by repository format in this BzrDir.
630
Note that bzr dirs that do not support format strings will raise
631
IncompatibleFormat if the repository format they are given has
632
a format string, and vice versa.
634
If repository_format is None, the transport is returned with no
635
checking. If it is not None, then the returned transport is
636
guaranteed to point to an existing directory ready for use.
638
raise NotImplementedError(self.get_repository_transport)
640
def get_workingtree_transport(self, tree_format):
641
"""Get the transport for use by workingtree format in this BzrDir.
643
Note that bzr dirs that do not support format strings will raise
644
IncompatibleFormat if the workingtree format they are given has a
645
format string, and vice versa.
647
If workingtree_format is None, the transport is returned with no
648
checking. If it is not None, then the returned transport is
649
guaranteed to point to an existing directory ready for use.
651
raise NotImplementedError(self.get_workingtree_transport)
653
def __init__(self, _transport, _format):
654
"""Initialize a Bzr control dir object.
656
Only really common logic should reside here, concrete classes should be
657
made with varying behaviours.
659
:param _format: the format that is creating this BzrDir instance.
660
:param _transport: the transport this dir is based at.
662
self._format = _format
663
self.transport = _transport.clone('.bzr')
664
self.root_transport = _transport
665
self._mode_check_done = False
667
def is_control_filename(self, filename):
668
"""True if filename is the name of a path which is reserved for bzrdir's.
670
:param filename: A filename within the root transport of this bzrdir.
672
This is true IF and ONLY IF the filename is part of the namespace reserved
673
for bzr control dirs. Currently this is the '.bzr' directory in the root
674
of the root_transport. it is expected that plugins will need to extend
675
this in the future - for instance to make bzr talk with svn working
678
# this might be better on the BzrDirFormat class because it refers to
679
# all the possible bzrdir disk formats.
680
# This method is tested via the workingtree is_control_filename tests-
681
# it was extracted from WorkingTree.is_control_filename. If the method's
682
# contract is extended beyond the current trivial implementation, please
683
# add new tests for it to the appropriate place.
684
return filename == '.bzr' or filename.startswith('.bzr/')
686
def needs_format_conversion(self, format=None):
687
"""Return true if this bzrdir needs convert_format run on it.
689
For instance, if the repository format is out of date but the
690
branch and working tree are not, this should return True.
692
:param format: Optional parameter indicating a specific desired
693
format we plan to arrive at.
695
raise NotImplementedError(self.needs_format_conversion)
698
def open_unsupported(base):
699
"""Open a branch which is not supported."""
700
return BzrDir.open(base, _unsupported=True)
703
def open(base, _unsupported=False, possible_transports=None):
704
"""Open an existing bzrdir, rooted at 'base' (url).
706
:param _unsupported: a private parameter to the BzrDir class.
708
t = get_transport(base, possible_transports=possible_transports)
709
return BzrDir.open_from_transport(t, _unsupported=_unsupported)
712
def open_from_transport(transport, _unsupported=False,
713
_server_formats=True):
714
"""Open a bzrdir within a particular directory.
716
:param transport: Transport containing the bzrdir.
717
:param _unsupported: private.
719
base = transport.base
721
def find_format(transport):
722
return transport, BzrDirFormat.find_format(
723
transport, _server_formats=_server_formats)
725
def redirected(transport, e, redirection_notice):
726
qualified_source = e.get_source_url()
727
relpath = transport.relpath(qualified_source)
728
if not e.target.endswith(relpath):
729
# Not redirected to a branch-format, not a branch
730
raise errors.NotBranchError(path=e.target)
731
target = e.target[:-len(relpath)]
732
note('%s is%s redirected to %s',
733
transport.base, e.permanently, target)
734
# Let's try with a new transport
735
# FIXME: If 'transport' has a qualifier, this should
736
# be applied again to the new transport *iff* the
737
# schemes used are the same. Uncomment this code
738
# once the function (and tests) exist.
740
#target = urlutils.copy_url_qualifiers(original, target)
741
return get_transport(target)
744
transport, format = do_catching_redirections(find_format,
747
except errors.TooManyRedirections:
748
raise errors.NotBranchError(base)
750
BzrDir._check_supported(format, _unsupported)
751
return format.open(transport, _found=True)
753
def open_branch(self, unsupported=False):
754
"""Open the branch object at this BzrDir if one is present.
756
If unsupported is True, then no longer supported branch formats can
759
TODO: static convenience version of this?
761
raise NotImplementedError(self.open_branch)
764
def open_containing(url, possible_transports=None):
765
"""Open an existing branch which contains url.
767
:param url: url to search from.
768
See open_containing_from_transport for more detail.
770
transport = get_transport(url, possible_transports)
771
return BzrDir.open_containing_from_transport(transport)
774
def open_containing_from_transport(a_transport):
775
"""Open an existing branch which contains a_transport.base.
777
This probes for a branch at a_transport, and searches upwards from there.
779
Basically we keep looking up until we find the control directory or
780
run into the root. If there isn't one, raises NotBranchError.
781
If there is one and it is either an unrecognised format or an unsupported
782
format, UnknownFormatError or UnsupportedFormatError are raised.
783
If there is one, it is returned, along with the unused portion of url.
785
:return: The BzrDir that contains the path, and a Unicode path
786
for the rest of the URL.
788
# this gets the normalised url back. I.e. '.' -> the full path.
789
url = a_transport.base
792
result = BzrDir.open_from_transport(a_transport)
793
return result, urlutils.unescape(a_transport.relpath(url))
794
except errors.NotBranchError, e:
797
new_t = a_transport.clone('..')
798
except errors.InvalidURLJoin:
799
# reached the root, whatever that may be
800
raise errors.NotBranchError(path=url)
801
if new_t.base == a_transport.base:
802
# reached the root, whatever that may be
803
raise errors.NotBranchError(path=url)
806
def _get_tree_branch(self):
807
"""Return the branch and tree, if any, for this bzrdir.
809
Return None for tree if not present or inaccessible.
810
Raise NotBranchError if no branch is present.
811
:return: (tree, branch)
814
tree = self.open_workingtree()
815
except (errors.NoWorkingTree, errors.NotLocalUrl):
817
branch = self.open_branch()
823
def open_tree_or_branch(klass, location):
824
"""Return the branch and working tree at a location.
826
If there is no tree at the location, tree will be None.
827
If there is no branch at the location, an exception will be
829
:return: (tree, branch)
831
bzrdir = klass.open(location)
832
return bzrdir._get_tree_branch()
835
def open_containing_tree_or_branch(klass, location):
836
"""Return the branch and working tree contained by a location.
838
Returns (tree, branch, relpath).
839
If there is no tree at containing the location, tree will be None.
840
If there is no branch containing the location, an exception will be
842
relpath is the portion of the path that is contained by the branch.
844
bzrdir, relpath = klass.open_containing(location)
845
tree, branch = bzrdir._get_tree_branch()
846
return tree, branch, relpath
849
def open_containing_tree_branch_or_repository(klass, location):
850
"""Return the working tree, branch and repo contained by a location.
852
Returns (tree, branch, repository, relpath).
853
If there is no tree containing the location, tree will be None.
854
If there is no branch containing the location, branch will be None.
855
If there is no repository containing the location, repository will be
857
relpath is the portion of the path that is contained by the innermost
860
If no tree, branch or repository is found, a NotBranchError is raised.
862
bzrdir, relpath = klass.open_containing(location)
864
tree, branch = bzrdir._get_tree_branch()
865
except errors.NotBranchError:
867
repo = bzrdir.find_repository()
868
return None, None, repo, relpath
869
except (errors.NoRepositoryPresent):
870
raise errors.NotBranchError(location)
871
return tree, branch, branch.repository, relpath
873
def open_repository(self, _unsupported=False):
874
"""Open the repository object at this BzrDir if one is present.
876
This will not follow the Branch object pointer - it's strictly a direct
877
open facility. Most client code should use open_branch().repository to
880
:param _unsupported: a private parameter, not part of the api.
881
TODO: static convenience version of this?
883
raise NotImplementedError(self.open_repository)
885
def open_workingtree(self, _unsupported=False,
886
recommend_upgrade=True, from_branch=None):
887
"""Open the workingtree object at this BzrDir if one is present.
889
:param recommend_upgrade: Optional keyword parameter, when True (the
890
default), emit through the ui module a recommendation that the user
891
upgrade the working tree when the workingtree being opened is old
892
(but still fully supported).
893
:param from_branch: override bzrdir branch (for lightweight checkouts)
895
raise NotImplementedError(self.open_workingtree)
897
def has_branch(self):
898
"""Tell if this bzrdir contains a branch.
900
Note: if you're going to open the branch, you should just go ahead
901
and try, and not ask permission first. (This method just opens the
902
branch and discards it, and that's somewhat expensive.)
907
except errors.NotBranchError:
910
def has_workingtree(self):
911
"""Tell if this bzrdir contains a working tree.
913
This will still raise an exception if the bzrdir has a workingtree that
914
is remote & inaccessible.
916
Note: if you're going to open the working tree, you should just go ahead
917
and try, and not ask permission first. (This method just opens the
918
workingtree and discards it, and that's somewhat expensive.)
921
self.open_workingtree(recommend_upgrade=False)
923
except errors.NoWorkingTree:
926
def _cloning_metadir(self):
927
"""Produce a metadir suitable for cloning with."""
928
result_format = self._format.__class__()
931
branch = self.open_branch()
932
source_repository = branch.repository
933
except errors.NotBranchError:
935
source_repository = self.open_repository()
936
except errors.NoRepositoryPresent:
937
source_repository = None
939
# XXX TODO: This isinstance is here because we have not implemented
940
# the fix recommended in bug # 103195 - to delegate this choice the
942
repo_format = source_repository._format
943
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
944
result_format.repository_format = repo_format
946
# TODO: Couldn't we just probe for the format in these cases,
947
# rather than opening the whole tree? It would be a little
948
# faster. mbp 20070401
949
tree = self.open_workingtree(recommend_upgrade=False)
950
except (errors.NoWorkingTree, errors.NotLocalUrl):
951
result_format.workingtree_format = None
953
result_format.workingtree_format = tree._format.__class__()
954
return result_format, source_repository
956
def cloning_metadir(self):
957
"""Produce a metadir suitable for cloning or sprouting with.
959
These operations may produce workingtrees (yes, even though they're
960
"cloning" something that doesn't have a tree), so a viable workingtree
961
format must be selected.
963
format, repository = self._cloning_metadir()
964
if format._workingtree_format is None:
965
if repository is None:
967
tree_format = repository._format._matchingbzrdir.workingtree_format
968
format.workingtree_format = tree_format.__class__()
971
def checkout_metadir(self):
972
return self.cloning_metadir()
974
def sprout(self, url, revision_id=None, force_new_repo=False,
975
recurse='down', possible_transports=None,
976
accelerator_tree=None, hardlink=False, stacked=False):
977
"""Create a copy of this bzrdir prepared for use as a new line of
980
If url's last component does not exist, it will be created.
982
Attributes related to the identity of the source branch like
983
branch nickname will be cleaned, a working tree is created
984
whether one existed before or not; and a local branch is always
987
if revision_id is not None, then the clone operation may tune
988
itself to download less data.
989
:param accelerator_tree: A tree which can be used for retrieving file
990
contents more quickly than the revision tree, i.e. a workingtree.
991
The revision tree will be used for cases where accelerator_tree's
992
content is different.
993
:param hardlink: If true, hard-link files from accelerator_tree,
995
:param stacked: If true, create a stacked branch referring to the
996
location of this control directory.
998
target_transport = get_transport(url, possible_transports)
999
target_transport.ensure_base()
1000
cloning_format = self.cloning_metadir()
1001
result = cloning_format.initialize_on_transport(target_transport)
1003
source_branch = self.open_branch()
1004
source_repository = source_branch.repository
1006
stacked_branch_url = self.root_transport.base
1009
stacked_branch_url = source_branch.get_stacked_on_url()
1010
except (errors.NotStacked, errors.UnstackableBranchFormat,
1011
errors.UnstackableRepositoryFormat):
1012
stacked_branch_url = None
1013
except errors.NotBranchError:
1014
source_branch = None
1016
source_repository = self.open_repository()
1017
except errors.NoRepositoryPresent:
1018
source_repository = None
1019
stacked_branch_url = None
1024
result_repo = result.find_repository()
1025
except errors.NoRepositoryPresent:
1028
# Create/update the result repository as required
1029
if source_repository is None:
1030
if result_repo is None:
1031
# no repo available, make a new one
1032
result.create_repository()
1033
elif stacked_branch_url is not None:
1034
if result_repo is None:
1035
result_repo = source_repository._format.initialize(result)
1036
stacked_dir = BzrDir.open(stacked_branch_url)
1038
stacked_repo = stacked_dir.open_branch().repository
1039
except errors.NotBranchError:
1040
stacked_repo = stacked_dir.open_repository()
1041
result_repo.add_fallback_repository(stacked_repo)
1042
result_repo.fetch(source_repository, revision_id=revision_id)
1043
elif result_repo is None:
1044
# have source, and want to make a new target repo
1045
result_repo = source_repository.sprout(result,
1046
revision_id=revision_id)
1048
# Fetch needed content into target.
1049
# Would rather do it this way ...
1050
# source_repository.copy_content_into(result_repo,
1051
# revision_id=revision_id)
1052
# so we can override the copy method
1053
result_repo.fetch(source_repository, revision_id=revision_id)
1055
# Create/update the result branch
1056
if source_branch is not None:
1057
result_branch = source_branch.sprout(result,
1058
revision_id=revision_id)
1060
result_branch = result.create_branch()
1061
if stacked_branch_url is not None:
1062
result_branch.set_stacked_on_url(stacked_branch_url)
1064
# Create/update the result working tree
1065
if isinstance(target_transport, LocalTransport) and (
1066
result_repo is None or result_repo.make_working_trees()):
1067
wt = result.create_workingtree(accelerator_tree=accelerator_tree,
1071
if wt.path2id('') is None:
1073
wt.set_root_id(self.open_workingtree.get_root_id())
1074
except errors.NoWorkingTree:
1080
if recurse == 'down':
1082
basis = wt.basis_tree()
1084
subtrees = basis.iter_references()
1085
elif source_branch is not None:
1086
basis = source_branch.basis_tree()
1088
subtrees = basis.iter_references()
1093
for path, file_id in subtrees:
1094
target = urlutils.join(url, urlutils.escape(path))
1095
sublocation = source_branch.reference_parent(file_id, path)
1096
sublocation.bzrdir.sprout(target,
1097
basis.get_reference_revision(file_id, path),
1098
force_new_repo=force_new_repo, recurse=recurse,
1101
if basis is not None:
1106
class BzrDirPreSplitOut(BzrDir):
1107
"""A common class for the all-in-one formats."""
1109
def __init__(self, _transport, _format):
1110
"""See BzrDir.__init__."""
1111
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
1112
self._control_files = lockable_files.LockableFiles(
1113
self.get_branch_transport(None),
1114
self._format._lock_file_name,
1115
self._format._lock_class)
1117
def break_lock(self):
1118
"""Pre-splitout bzrdirs do not suffer from stale locks."""
1119
raise NotImplementedError(self.break_lock)
1121
def cloning_metadir(self):
1122
"""Produce a metadir suitable for cloning with."""
1123
return self._format.__class__()
1125
def clone(self, url, revision_id=None, force_new_repo=False):
1126
"""See BzrDir.clone()."""
1127
from bzrlib.workingtree import WorkingTreeFormat2
1128
self._make_tail(url)
1129
result = self._format._initialize_for_clone(url)
1130
self.open_repository().clone(result, revision_id=revision_id)
1131
from_branch = self.open_branch()
1132
from_branch.clone(result, revision_id=revision_id)
1134
self.open_workingtree().clone(result)
1135
except errors.NotLocalUrl:
1136
# make a new one, this format always has to have one.
1138
WorkingTreeFormat2().initialize(result)
1139
except errors.NotLocalUrl:
1140
# but we cannot do it for remote trees.
1141
to_branch = result.open_branch()
1142
WorkingTreeFormat2()._stub_initialize_remote(to_branch)
1145
def create_branch(self):
1146
"""See BzrDir.create_branch."""
1147
return self.open_branch()
1149
def destroy_branch(self):
1150
"""See BzrDir.destroy_branch."""
1151
raise errors.UnsupportedOperation(self.destroy_branch, self)
1153
def create_repository(self, shared=False):
1154
"""See BzrDir.create_repository."""
1156
raise errors.IncompatibleFormat('shared repository', self._format)
1157
return self.open_repository()
1159
def destroy_repository(self):
1160
"""See BzrDir.destroy_repository."""
1161
raise errors.UnsupportedOperation(self.destroy_repository, self)
1163
def create_workingtree(self, revision_id=None, from_branch=None,
1164
accelerator_tree=None, hardlink=False):
1165
"""See BzrDir.create_workingtree."""
1166
# this looks buggy but is not -really-
1167
# because this format creates the workingtree when the bzrdir is
1169
# clone and sprout will have set the revision_id
1170
# and that will have set it for us, its only
1171
# specific uses of create_workingtree in isolation
1172
# that can do wonky stuff here, and that only
1173
# happens for creating checkouts, which cannot be
1174
# done on this format anyway. So - acceptable wart.
1175
result = self.open_workingtree(recommend_upgrade=False)
1176
if revision_id is not None:
1177
if revision_id == _mod_revision.NULL_REVISION:
1178
result.set_parent_ids([])
1180
result.set_parent_ids([revision_id])
1183
def destroy_workingtree(self):
1184
"""See BzrDir.destroy_workingtree."""
1185
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
1187
def destroy_workingtree_metadata(self):
1188
"""See BzrDir.destroy_workingtree_metadata."""
1189
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
1192
def get_branch_transport(self, branch_format):
1193
"""See BzrDir.get_branch_transport()."""
1194
if branch_format is None:
1195
return self.transport
1197
branch_format.get_format_string()
1198
except NotImplementedError:
1199
return self.transport
1200
raise errors.IncompatibleFormat(branch_format, self._format)
1202
def get_repository_transport(self, repository_format):
1203
"""See BzrDir.get_repository_transport()."""
1204
if repository_format is None:
1205
return self.transport
1207
repository_format.get_format_string()
1208
except NotImplementedError:
1209
return self.transport
1210
raise errors.IncompatibleFormat(repository_format, self._format)
1212
def get_workingtree_transport(self, workingtree_format):
1213
"""See BzrDir.get_workingtree_transport()."""
1214
if workingtree_format is None:
1215
return self.transport
1217
workingtree_format.get_format_string()
1218
except NotImplementedError:
1219
return self.transport
1220
raise errors.IncompatibleFormat(workingtree_format, self._format)
1222
def needs_format_conversion(self, format=None):
1223
"""See BzrDir.needs_format_conversion()."""
1224
# if the format is not the same as the system default,
1225
# an upgrade is needed.
1227
format = BzrDirFormat.get_default_format()
1228
return not isinstance(self._format, format.__class__)
1230
def open_branch(self, unsupported=False):
1231
"""See BzrDir.open_branch."""
1232
from bzrlib.branch import BzrBranchFormat4
1233
format = BzrBranchFormat4()
1234
self._check_supported(format, unsupported)
1235
return format.open(self, _found=True)
1237
def sprout(self, url, revision_id=None, force_new_repo=False,
1238
possible_transports=None, accelerator_tree=None,
1239
hardlink=False, stacked=False):
1240
"""See BzrDir.sprout()."""
1242
raise errors.UnstackableBranchFormat(
1243
self._format, self.root_transport.base)
1244
from bzrlib.workingtree import WorkingTreeFormat2
1245
self._make_tail(url)
1246
result = self._format._initialize_for_clone(url)
1248
self.open_repository().clone(result, revision_id=revision_id)
1249
except errors.NoRepositoryPresent:
1252
self.open_branch().sprout(result, revision_id=revision_id)
1253
except errors.NotBranchError:
1255
# we always want a working tree
1256
WorkingTreeFormat2().initialize(result,
1257
accelerator_tree=accelerator_tree,
1262
class BzrDir4(BzrDirPreSplitOut):
1263
"""A .bzr version 4 control object.
1265
This is a deprecated format and may be removed after sept 2006.
1268
def create_repository(self, shared=False):
1269
"""See BzrDir.create_repository."""
1270
return self._format.repository_format.initialize(self, shared)
1272
def needs_format_conversion(self, format=None):
1273
"""Format 4 dirs are always in need of conversion."""
1276
def open_repository(self):
1277
"""See BzrDir.open_repository."""
1278
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1279
return RepositoryFormat4().open(self, _found=True)
1282
class BzrDir5(BzrDirPreSplitOut):
1283
"""A .bzr version 5 control object.
1285
This is a deprecated format and may be removed after sept 2006.
1288
def open_repository(self):
1289
"""See BzrDir.open_repository."""
1290
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1291
return RepositoryFormat5().open(self, _found=True)
1293
def open_workingtree(self, _unsupported=False,
1294
recommend_upgrade=True):
1295
"""See BzrDir.create_workingtree."""
1296
from bzrlib.workingtree import WorkingTreeFormat2
1297
wt_format = WorkingTreeFormat2()
1298
# we don't warn here about upgrades; that ought to be handled for the
1300
return wt_format.open(self, _found=True)
1303
class BzrDir6(BzrDirPreSplitOut):
1304
"""A .bzr version 6 control object.
1306
This is a deprecated format and may be removed after sept 2006.
1309
def open_repository(self):
1310
"""See BzrDir.open_repository."""
1311
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1312
return RepositoryFormat6().open(self, _found=True)
1314
def open_workingtree(self, _unsupported=False,
1315
recommend_upgrade=True):
1316
"""See BzrDir.create_workingtree."""
1317
# we don't warn here about upgrades; that ought to be handled for the
1319
from bzrlib.workingtree import WorkingTreeFormat2
1320
return WorkingTreeFormat2().open(self, _found=True)
1323
class BzrDirMeta1(BzrDir):
1324
"""A .bzr meta version 1 control object.
1326
This is the first control object where the
1327
individual aspects are really split out: there are separate repository,
1328
workingtree and branch subdirectories and any subset of the three can be
1329
present within a BzrDir.
1332
def can_convert_format(self):
1333
"""See BzrDir.can_convert_format()."""
1336
def create_branch(self):
1337
"""See BzrDir.create_branch."""
1338
return self._format.get_branch_format().initialize(self)
1340
def destroy_branch(self):
1341
"""See BzrDir.create_branch."""
1342
self.transport.delete_tree('branch')
1344
def create_repository(self, shared=False):
1345
"""See BzrDir.create_repository."""
1346
return self._format.repository_format.initialize(self, shared)
1348
def destroy_repository(self):
1349
"""See BzrDir.destroy_repository."""
1350
self.transport.delete_tree('repository')
1352
def create_workingtree(self, revision_id=None, from_branch=None,
1353
accelerator_tree=None, hardlink=False):
1354
"""See BzrDir.create_workingtree."""
1355
return self._format.workingtree_format.initialize(
1356
self, revision_id, from_branch=from_branch,
1357
accelerator_tree=accelerator_tree, hardlink=hardlink)
1359
def destroy_workingtree(self):
1360
"""See BzrDir.destroy_workingtree."""
1361
wt = self.open_workingtree(recommend_upgrade=False)
1362
repository = wt.branch.repository
1363
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1364
wt.revert(old_tree=empty)
1365
self.destroy_workingtree_metadata()
1367
def destroy_workingtree_metadata(self):
1368
self.transport.delete_tree('checkout')
1370
def find_branch_format(self):
1371
"""Find the branch 'format' for this bzrdir.
1373
This might be a synthetic object for e.g. RemoteBranch and SVN.
1375
from bzrlib.branch import BranchFormat
1376
return BranchFormat.find_format(self)
1378
def _get_mkdir_mode(self):
1379
"""Figure out the mode to use when creating a bzrdir subdir."""
1380
temp_control = lockable_files.LockableFiles(self.transport, '',
1381
lockable_files.TransportLock)
1382
return temp_control._dir_mode
1384
def get_branch_reference(self):
1385
"""See BzrDir.get_branch_reference()."""
1386
from bzrlib.branch import BranchFormat
1387
format = BranchFormat.find_format(self)
1388
return format.get_reference(self)
1390
def get_branch_transport(self, branch_format):
1391
"""See BzrDir.get_branch_transport()."""
1392
if branch_format is None:
1393
return self.transport.clone('branch')
1395
branch_format.get_format_string()
1396
except NotImplementedError:
1397
raise errors.IncompatibleFormat(branch_format, self._format)
1399
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1400
except errors.FileExists:
1402
return self.transport.clone('branch')
1404
def get_repository_transport(self, repository_format):
1405
"""See BzrDir.get_repository_transport()."""
1406
if repository_format is None:
1407
return self.transport.clone('repository')
1409
repository_format.get_format_string()
1410
except NotImplementedError:
1411
raise errors.IncompatibleFormat(repository_format, self._format)
1413
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1414
except errors.FileExists:
1416
return self.transport.clone('repository')
1418
def get_workingtree_transport(self, workingtree_format):
1419
"""See BzrDir.get_workingtree_transport()."""
1420
if workingtree_format is None:
1421
return self.transport.clone('checkout')
1423
workingtree_format.get_format_string()
1424
except NotImplementedError:
1425
raise errors.IncompatibleFormat(workingtree_format, self._format)
1427
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1428
except errors.FileExists:
1430
return self.transport.clone('checkout')
1432
def needs_format_conversion(self, format=None):
1433
"""See BzrDir.needs_format_conversion()."""
1435
format = BzrDirFormat.get_default_format()
1436
if not isinstance(self._format, format.__class__):
1437
# it is not a meta dir format, conversion is needed.
1439
# we might want to push this down to the repository?
1441
if not isinstance(self.open_repository()._format,
1442
format.repository_format.__class__):
1443
# the repository needs an upgrade.
1445
except errors.NoRepositoryPresent:
1448
if not isinstance(self.open_branch()._format,
1449
format.get_branch_format().__class__):
1450
# the branch needs an upgrade.
1452
except errors.NotBranchError:
1455
my_wt = self.open_workingtree(recommend_upgrade=False)
1456
if not isinstance(my_wt._format,
1457
format.workingtree_format.__class__):
1458
# the workingtree needs an upgrade.
1460
except (errors.NoWorkingTree, errors.NotLocalUrl):
1464
def open_branch(self, unsupported=False):
1465
"""See BzrDir.open_branch."""
1466
format = self.find_branch_format()
1467
self._check_supported(format, unsupported)
1468
return format.open(self, _found=True)
1470
def open_repository(self, unsupported=False):
1471
"""See BzrDir.open_repository."""
1472
from bzrlib.repository import RepositoryFormat
1473
format = RepositoryFormat.find_format(self)
1474
self._check_supported(format, unsupported)
1475
return format.open(self, _found=True)
1477
def open_workingtree(self, unsupported=False,
1478
recommend_upgrade=True):
1479
"""See BzrDir.open_workingtree."""
1480
from bzrlib.workingtree import WorkingTreeFormat
1481
format = WorkingTreeFormat.find_format(self)
1482
self._check_supported(format, unsupported,
1484
basedir=self.root_transport.base)
1485
return format.open(self, _found=True)
1488
class BzrDirFormat(object):
1489
"""An encapsulation of the initialization and open routines for a format.
1491
Formats provide three things:
1492
* An initialization routine,
1496
Formats are placed in a dict by their format string for reference
1497
during bzrdir opening. These should be subclasses of BzrDirFormat
1500
Once a format is deprecated, just deprecate the initialize and open
1501
methods on the format class. Do not deprecate the object, as the
1502
object will be created every system load.
1505
_default_format = None
1506
"""The default format used for new .bzr dirs."""
1509
"""The known formats."""
1511
_control_formats = []
1512
"""The registered control formats - .bzr, ....
1514
This is a list of BzrDirFormat objects.
1517
_control_server_formats = []
1518
"""The registered control server formats, e.g. RemoteBzrDirs.
1520
This is a list of BzrDirFormat objects.
1523
_lock_file_name = 'branch-lock'
1525
# _lock_class must be set in subclasses to the lock type, typ.
1526
# TransportLock or LockDir
1529
def find_format(klass, transport, _server_formats=True):
1530
"""Return the format present at transport."""
1532
formats = klass._control_server_formats + klass._control_formats
1534
formats = klass._control_formats
1535
for format in formats:
1537
return format.probe_transport(transport)
1538
except errors.NotBranchError:
1539
# this format does not find a control dir here.
1541
raise errors.NotBranchError(path=transport.base)
1544
def probe_transport(klass, transport):
1545
"""Return the .bzrdir style format present in a directory."""
1547
format_string = transport.get(".bzr/branch-format").read()
1548
except errors.NoSuchFile:
1549
raise errors.NotBranchError(path=transport.base)
1552
return klass._formats[format_string]
1554
raise errors.UnknownFormatError(format=format_string, kind='bzrdir')
1557
def get_default_format(klass):
1558
"""Return the current default format."""
1559
return klass._default_format
1561
def get_format_string(self):
1562
"""Return the ASCII format string that identifies this format."""
1563
raise NotImplementedError(self.get_format_string)
1565
def get_format_description(self):
1566
"""Return the short description for this format."""
1567
raise NotImplementedError(self.get_format_description)
1569
def get_converter(self, format=None):
1570
"""Return the converter to use to convert bzrdirs needing converts.
1572
This returns a bzrlib.bzrdir.Converter object.
1574
This should return the best upgrader to step this format towards the
1575
current default format. In the case of plugins we can/should provide
1576
some means for them to extend the range of returnable converters.
1578
:param format: Optional format to override the default format of the
1581
raise NotImplementedError(self.get_converter)
1583
def initialize(self, url, possible_transports=None):
1584
"""Create a bzr control dir at this url and return an opened copy.
1586
Subclasses should typically override initialize_on_transport
1587
instead of this method.
1589
return self.initialize_on_transport(get_transport(url,
1590
possible_transports))
1592
def initialize_on_transport(self, transport):
1593
"""Initialize a new bzrdir in the base directory of a Transport."""
1594
# Since we don't have a .bzr directory, inherit the
1595
# mode from the root directory
1596
temp_control = lockable_files.LockableFiles(transport,
1597
'', lockable_files.TransportLock)
1598
temp_control._transport.mkdir('.bzr',
1599
# FIXME: RBC 20060121 don't peek under
1601
mode=temp_control._dir_mode)
1602
if sys.platform == 'win32' and isinstance(transport, LocalTransport):
1603
win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1604
file_mode = temp_control._file_mode
1606
bzrdir_transport = transport.clone('.bzr')
1607
utf8_files = [('README',
1608
"This is a Bazaar control directory.\n"
1609
"Do not change any files in this directory.\n"
1610
"See http://bazaar-vcs.org/ for more information about Bazaar.\n"),
1611
('branch-format', self.get_format_string()),
1613
# NB: no need to escape relative paths that are url safe.
1614
control_files = lockable_files.LockableFiles(bzrdir_transport,
1615
self._lock_file_name, self._lock_class)
1616
control_files.create_lock()
1617
control_files.lock_write()
1619
for (filename, content) in utf8_files:
1620
bzrdir_transport.put_bytes(filename, content,
1623
control_files.unlock()
1624
return self.open(transport, _found=True)
1626
def is_supported(self):
1627
"""Is this format supported?
1629
Supported formats must be initializable and openable.
1630
Unsupported formats may not support initialization or committing or
1631
some other features depending on the reason for not being supported.
1635
def same_model(self, target_format):
1636
return (self.repository_format.rich_root_data ==
1637
target_format.rich_root_data)
1640
def known_formats(klass):
1641
"""Return all the known formats.
1643
Concrete formats should override _known_formats.
1645
# There is double indirection here to make sure that control
1646
# formats used by more than one dir format will only be probed
1647
# once. This can otherwise be quite expensive for remote connections.
1649
for format in klass._control_formats:
1650
result.update(format._known_formats())
1654
def _known_formats(klass):
1655
"""Return the known format instances for this control format."""
1656
return set(klass._formats.values())
1658
def open(self, transport, _found=False):
1659
"""Return an instance of this format for the dir transport points at.
1661
_found is a private parameter, do not use it.
1664
found_format = BzrDirFormat.find_format(transport)
1665
if not isinstance(found_format, self.__class__):
1666
raise AssertionError("%s was asked to open %s, but it seems to need "
1668
% (self, transport, found_format))
1669
return self._open(transport)
1671
def _open(self, transport):
1672
"""Template method helper for opening BzrDirectories.
1674
This performs the actual open and any additional logic or parameter
1677
raise NotImplementedError(self._open)
1680
def register_format(klass, format):
1681
klass._formats[format.get_format_string()] = format
1684
def register_control_format(klass, format):
1685
"""Register a format that does not use '.bzr' for its control dir.
1687
TODO: This should be pulled up into a 'ControlDirFormat' base class
1688
which BzrDirFormat can inherit from, and renamed to register_format
1689
there. It has been done without that for now for simplicity of
1692
klass._control_formats.append(format)
1695
def register_control_server_format(klass, format):
1696
"""Register a control format for client-server environments.
1698
These formats will be tried before ones registered with
1699
register_control_format. This gives implementations that decide to the
1700
chance to grab it before anything looks at the contents of the format
1703
klass._control_server_formats.append(format)
1706
def _set_default_format(klass, format):
1707
"""Set default format (for testing behavior of defaults only)"""
1708
klass._default_format = format
1712
return self.get_format_string().rstrip()
1715
def unregister_format(klass, format):
1716
del klass._formats[format.get_format_string()]
1719
def unregister_control_format(klass, format):
1720
klass._control_formats.remove(format)
1723
class BzrDirFormat4(BzrDirFormat):
1724
"""Bzr dir format 4.
1726
This format is a combined format for working tree, branch and repository.
1728
- Format 1 working trees [always]
1729
- Format 4 branches [always]
1730
- Format 4 repositories [always]
1732
This format is deprecated: it indexes texts using a text it which is
1733
removed in format 5; write support for this format has been removed.
1736
_lock_class = lockable_files.TransportLock
1738
def get_format_string(self):
1739
"""See BzrDirFormat.get_format_string()."""
1740
return "Bazaar-NG branch, format 0.0.4\n"
1742
def get_format_description(self):
1743
"""See BzrDirFormat.get_format_description()."""
1744
return "All-in-one format 4"
1746
def get_converter(self, format=None):
1747
"""See BzrDirFormat.get_converter()."""
1748
# there is one and only one upgrade path here.
1749
return ConvertBzrDir4To5()
1751
def initialize_on_transport(self, transport):
1752
"""Format 4 branches cannot be created."""
1753
raise errors.UninitializableFormat(self)
1755
def is_supported(self):
1756
"""Format 4 is not supported.
1758
It is not supported because the model changed from 4 to 5 and the
1759
conversion logic is expensive - so doing it on the fly was not
1764
def _open(self, transport):
1765
"""See BzrDirFormat._open."""
1766
return BzrDir4(transport, self)
1768
def __return_repository_format(self):
1769
"""Circular import protection."""
1770
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1771
return RepositoryFormat4()
1772
repository_format = property(__return_repository_format)
1775
class BzrDirFormat5(BzrDirFormat):
1776
"""Bzr control format 5.
1778
This format is a combined format for working tree, branch and repository.
1780
- Format 2 working trees [always]
1781
- Format 4 branches [always]
1782
- Format 5 repositories [always]
1783
Unhashed stores in the repository.
1786
_lock_class = lockable_files.TransportLock
1788
def get_format_string(self):
1789
"""See BzrDirFormat.get_format_string()."""
1790
return "Bazaar-NG branch, format 5\n"
1792
def get_format_description(self):
1793
"""See BzrDirFormat.get_format_description()."""
1794
return "All-in-one format 5"
1796
def get_converter(self, format=None):
1797
"""See BzrDirFormat.get_converter()."""
1798
# there is one and only one upgrade path here.
1799
return ConvertBzrDir5To6()
1801
def _initialize_for_clone(self, url):
1802
return self.initialize_on_transport(get_transport(url), _cloning=True)
1804
def initialize_on_transport(self, transport, _cloning=False):
1805
"""Format 5 dirs always have working tree, branch and repository.
1807
Except when they are being cloned.
1809
from bzrlib.branch import BzrBranchFormat4
1810
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1811
from bzrlib.workingtree import WorkingTreeFormat2
1812
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1813
RepositoryFormat5().initialize(result, _internal=True)
1815
branch = BzrBranchFormat4().initialize(result)
1817
WorkingTreeFormat2().initialize(result)
1818
except errors.NotLocalUrl:
1819
# Even though we can't access the working tree, we need to
1820
# create its control files.
1821
WorkingTreeFormat2()._stub_initialize_remote(branch)
1824
def _open(self, transport):
1825
"""See BzrDirFormat._open."""
1826
return BzrDir5(transport, self)
1828
def __return_repository_format(self):
1829
"""Circular import protection."""
1830
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1831
return RepositoryFormat5()
1832
repository_format = property(__return_repository_format)
1835
class BzrDirFormat6(BzrDirFormat):
1836
"""Bzr control format 6.
1838
This format is a combined format for working tree, branch and repository.
1840
- Format 2 working trees [always]
1841
- Format 4 branches [always]
1842
- Format 6 repositories [always]
1845
_lock_class = lockable_files.TransportLock
1847
def get_format_string(self):
1848
"""See BzrDirFormat.get_format_string()."""
1849
return "Bazaar-NG branch, format 6\n"
1851
def get_format_description(self):
1852
"""See BzrDirFormat.get_format_description()."""
1853
return "All-in-one format 6"
1855
def get_converter(self, format=None):
1856
"""See BzrDirFormat.get_converter()."""
1857
# there is one and only one upgrade path here.
1858
return ConvertBzrDir6ToMeta()
1860
def _initialize_for_clone(self, url):
1861
return self.initialize_on_transport(get_transport(url), _cloning=True)
1863
def initialize_on_transport(self, transport, _cloning=False):
1864
"""Format 6 dirs always have working tree, branch and repository.
1866
Except when they are being cloned.
1868
from bzrlib.branch import BzrBranchFormat4
1869
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1870
from bzrlib.workingtree import WorkingTreeFormat2
1871
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1872
RepositoryFormat6().initialize(result, _internal=True)
1874
branch = BzrBranchFormat4().initialize(result)
1876
WorkingTreeFormat2().initialize(result)
1877
except errors.NotLocalUrl:
1878
# Even though we can't access the working tree, we need to
1879
# create its control files.
1880
WorkingTreeFormat2()._stub_initialize_remote(branch)
1883
def _open(self, transport):
1884
"""See BzrDirFormat._open."""
1885
return BzrDir6(transport, self)
1887
def __return_repository_format(self):
1888
"""Circular import protection."""
1889
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1890
return RepositoryFormat6()
1891
repository_format = property(__return_repository_format)
1894
class BzrDirMetaFormat1(BzrDirFormat):
1895
"""Bzr meta control format 1
1897
This is the first format with split out working tree, branch and repository
1900
- Format 3 working trees [optional]
1901
- Format 5 branches [optional]
1902
- Format 7 repositories [optional]
1905
_lock_class = lockdir.LockDir
1908
self._workingtree_format = None
1909
self._branch_format = None
1911
def __eq__(self, other):
1912
if other.__class__ is not self.__class__:
1914
if other.repository_format != self.repository_format:
1916
if other.workingtree_format != self.workingtree_format:
1920
def __ne__(self, other):
1921
return not self == other
1923
def get_branch_format(self):
1924
if self._branch_format is None:
1925
from bzrlib.branch import BranchFormat
1926
self._branch_format = BranchFormat.get_default_format()
1927
return self._branch_format
1929
def set_branch_format(self, format):
1930
self._branch_format = format
1932
def get_converter(self, format=None):
1933
"""See BzrDirFormat.get_converter()."""
1935
format = BzrDirFormat.get_default_format()
1936
if not isinstance(self, format.__class__):
1937
# converting away from metadir is not implemented
1938
raise NotImplementedError(self.get_converter)
1939
return ConvertMetaToMeta(format)
1941
def get_format_string(self):
1942
"""See BzrDirFormat.get_format_string()."""
1943
return "Bazaar-NG meta directory, format 1\n"
1945
def get_format_description(self):
1946
"""See BzrDirFormat.get_format_description()."""
1947
return "Meta directory format 1"
1949
def _open(self, transport):
1950
"""See BzrDirFormat._open."""
1951
return BzrDirMeta1(transport, self)
1953
def __return_repository_format(self):
1954
"""Circular import protection."""
1955
if getattr(self, '_repository_format', None):
1956
return self._repository_format
1957
from bzrlib.repository import RepositoryFormat
1958
return RepositoryFormat.get_default_format()
1960
def __set_repository_format(self, value):
1961
"""Allow changing the repository format for metadir formats."""
1962
self._repository_format = value
1964
repository_format = property(__return_repository_format, __set_repository_format)
1966
def __get_workingtree_format(self):
1967
if self._workingtree_format is None:
1968
from bzrlib.workingtree import WorkingTreeFormat
1969
self._workingtree_format = WorkingTreeFormat.get_default_format()
1970
return self._workingtree_format
1972
def __set_workingtree_format(self, wt_format):
1973
self._workingtree_format = wt_format
1975
workingtree_format = property(__get_workingtree_format,
1976
__set_workingtree_format)
1979
# Register bzr control format
1980
BzrDirFormat.register_control_format(BzrDirFormat)
1982
# Register bzr formats
1983
BzrDirFormat.register_format(BzrDirFormat4())
1984
BzrDirFormat.register_format(BzrDirFormat5())
1985
BzrDirFormat.register_format(BzrDirFormat6())
1986
__default_format = BzrDirMetaFormat1()
1987
BzrDirFormat.register_format(__default_format)
1988
BzrDirFormat._default_format = __default_format
1991
class Converter(object):
1992
"""Converts a disk format object from one format to another."""
1994
def convert(self, to_convert, pb):
1995
"""Perform the conversion of to_convert, giving feedback via pb.
1997
:param to_convert: The disk object to convert.
1998
:param pb: a progress bar to use for progress information.
2001
def step(self, message):
2002
"""Update the pb by a step."""
2004
self.pb.update(message, self.count, self.total)
2007
class ConvertBzrDir4To5(Converter):
2008
"""Converts format 4 bzr dirs to format 5."""
2011
super(ConvertBzrDir4To5, self).__init__()
2012
self.converted_revs = set()
2013
self.absent_revisions = set()
2017
def convert(self, to_convert, pb):
2018
"""See Converter.convert()."""
2019
self.bzrdir = to_convert
2021
self.pb.note('starting upgrade from format 4 to 5')
2022
if isinstance(self.bzrdir.transport, LocalTransport):
2023
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
2024
self._convert_to_weaves()
2025
return BzrDir.open(self.bzrdir.root_transport.base)
2027
def _convert_to_weaves(self):
2028
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
2031
stat = self.bzrdir.transport.stat('weaves')
2032
if not S_ISDIR(stat.st_mode):
2033
self.bzrdir.transport.delete('weaves')
2034
self.bzrdir.transport.mkdir('weaves')
2035
except errors.NoSuchFile:
2036
self.bzrdir.transport.mkdir('weaves')
2037
# deliberately not a WeaveFile as we want to build it up slowly.
2038
self.inv_weave = Weave('inventory')
2039
# holds in-memory weaves for all files
2040
self.text_weaves = {}
2041
self.bzrdir.transport.delete('branch-format')
2042
self.branch = self.bzrdir.open_branch()
2043
self._convert_working_inv()
2044
rev_history = self.branch.revision_history()
2045
# to_read is a stack holding the revisions we still need to process;
2046
# appending to it adds new highest-priority revisions
2047
self.known_revisions = set(rev_history)
2048
self.to_read = rev_history[-1:]
2050
rev_id = self.to_read.pop()
2051
if (rev_id not in self.revisions
2052
and rev_id not in self.absent_revisions):
2053
self._load_one_rev(rev_id)
2055
to_import = self._make_order()
2056
for i, rev_id in enumerate(to_import):
2057
self.pb.update('converting revision', i, len(to_import))
2058
self._convert_one_rev(rev_id)
2060
self._write_all_weaves()
2061
self._write_all_revs()
2062
self.pb.note('upgraded to weaves:')
2063
self.pb.note(' %6d revisions and inventories', len(self.revisions))
2064
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
2065
self.pb.note(' %6d texts', self.text_count)
2066
self._cleanup_spare_files_after_format4()
2067
self.branch._transport.put_bytes(
2069
BzrDirFormat5().get_format_string(),
2070
mode=self.bzrdir._get_file_mode())
2072
def _cleanup_spare_files_after_format4(self):
2073
# FIXME working tree upgrade foo.
2074
for n in 'merged-patches', 'pending-merged-patches':
2076
## assert os.path.getsize(p) == 0
2077
self.bzrdir.transport.delete(n)
2078
except errors.NoSuchFile:
2080
self.bzrdir.transport.delete_tree('inventory-store')
2081
self.bzrdir.transport.delete_tree('text-store')
2083
def _convert_working_inv(self):
2084
inv = xml4.serializer_v4.read_inventory(
2085
self.branch._transport.get('inventory'))
2086
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
2087
self.branch._transport.put_bytes('inventory', new_inv_xml,
2088
mode=self.bzrdir._get_file_mode())
2090
def _write_all_weaves(self):
2091
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
2092
weave_transport = self.bzrdir.transport.clone('weaves')
2093
weaves = WeaveStore(weave_transport, prefixed=False)
2094
transaction = WriteTransaction()
2098
for file_id, file_weave in self.text_weaves.items():
2099
self.pb.update('writing weave', i, len(self.text_weaves))
2100
weaves._put_weave(file_id, file_weave, transaction)
2102
self.pb.update('inventory', 0, 1)
2103
controlweaves._put_weave('inventory', self.inv_weave, transaction)
2104
self.pb.update('inventory', 1, 1)
2108
def _write_all_revs(self):
2109
"""Write all revisions out in new form."""
2110
self.bzrdir.transport.delete_tree('revision-store')
2111
self.bzrdir.transport.mkdir('revision-store')
2112
revision_transport = self.bzrdir.transport.clone('revision-store')
2114
from bzrlib.xml5 import serializer_v5
2115
from bzrlib.repofmt.weaverepo import RevisionTextStore
2116
revision_store = RevisionTextStore(revision_transport,
2117
serializer_v5, False, versionedfile.PrefixMapper(),
2118
lambda:True, lambda:True)
2120
for i, rev_id in enumerate(self.converted_revs):
2121
self.pb.update('write revision', i, len(self.converted_revs))
2122
text = serializer_v5.write_revision_to_string(
2123
self.revisions[rev_id])
2125
revision_store.add_lines(key, None, osutils.split_lines(text))
2129
def _load_one_rev(self, rev_id):
2130
"""Load a revision object into memory.
2132
Any parents not either loaded or abandoned get queued to be
2134
self.pb.update('loading revision',
2135
len(self.revisions),
2136
len(self.known_revisions))
2137
if not self.branch.repository.has_revision(rev_id):
2139
self.pb.note('revision {%s} not present in branch; '
2140
'will be converted as a ghost',
2142
self.absent_revisions.add(rev_id)
2144
rev = self.branch.repository.get_revision(rev_id)
2145
for parent_id in rev.parent_ids:
2146
self.known_revisions.add(parent_id)
2147
self.to_read.append(parent_id)
2148
self.revisions[rev_id] = rev
2150
def _load_old_inventory(self, rev_id):
2151
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
2152
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
2153
inv.revision_id = rev_id
2154
rev = self.revisions[rev_id]
2157
def _load_updated_inventory(self, rev_id):
2158
inv_xml = self.inv_weave.get_text(rev_id)
2159
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml, rev_id)
2162
def _convert_one_rev(self, rev_id):
2163
"""Convert revision and all referenced objects to new format."""
2164
rev = self.revisions[rev_id]
2165
inv = self._load_old_inventory(rev_id)
2166
present_parents = [p for p in rev.parent_ids
2167
if p not in self.absent_revisions]
2168
self._convert_revision_contents(rev, inv, present_parents)
2169
self._store_new_inv(rev, inv, present_parents)
2170
self.converted_revs.add(rev_id)
2172
def _store_new_inv(self, rev, inv, present_parents):
2173
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
2174
new_inv_sha1 = sha_string(new_inv_xml)
2175
self.inv_weave.add_lines(rev.revision_id,
2177
new_inv_xml.splitlines(True))
2178
rev.inventory_sha1 = new_inv_sha1
2180
def _convert_revision_contents(self, rev, inv, present_parents):
2181
"""Convert all the files within a revision.
2183
Also upgrade the inventory to refer to the text revision ids."""
2184
rev_id = rev.revision_id
2185
mutter('converting texts of revision {%s}',
2187
parent_invs = map(self._load_updated_inventory, present_parents)
2188
entries = inv.iter_entries()
2190
for path, ie in entries:
2191
self._convert_file_version(rev, ie, parent_invs)
2193
def _convert_file_version(self, rev, ie, parent_invs):
2194
"""Convert one version of one file.
2196
The file needs to be added into the weave if it is a merge
2197
of >=2 parents or if it's changed from its parent.
2199
file_id = ie.file_id
2200
rev_id = rev.revision_id
2201
w = self.text_weaves.get(file_id)
2204
self.text_weaves[file_id] = w
2205
text_changed = False
2206
parent_candiate_entries = ie.parent_candidates(parent_invs)
2207
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
2208
# XXX: Note that this is unordered - and this is tolerable because
2209
# the previous code was also unordered.
2210
previous_entries = dict((head, parent_candiate_entries[head]) for head
2212
self.snapshot_ie(previous_entries, ie, w, rev_id)
2215
@symbol_versioning.deprecated_method(symbol_versioning.one_one)
2216
def get_parents(self, revision_ids):
2217
for revision_id in revision_ids:
2218
yield self.revisions[revision_id].parent_ids
2220
def get_parent_map(self, revision_ids):
2221
"""See graph._StackedParentsProvider.get_parent_map"""
2222
return dict((revision_id, self.revisions[revision_id])
2223
for revision_id in revision_ids
2224
if revision_id in self.revisions)
2226
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
2227
# TODO: convert this logic, which is ~= snapshot to
2228
# a call to:. This needs the path figured out. rather than a work_tree
2229
# a v4 revision_tree can be given, or something that looks enough like
2230
# one to give the file content to the entry if it needs it.
2231
# and we need something that looks like a weave store for snapshot to
2233
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
2234
if len(previous_revisions) == 1:
2235
previous_ie = previous_revisions.values()[0]
2236
if ie._unchanged(previous_ie):
2237
ie.revision = previous_ie.revision
2240
text = self.branch.repository._text_store.get(ie.text_id)
2241
file_lines = text.readlines()
2242
w.add_lines(rev_id, previous_revisions, file_lines)
2243
self.text_count += 1
2245
w.add_lines(rev_id, previous_revisions, [])
2246
ie.revision = rev_id
2248
def _make_order(self):
2249
"""Return a suitable order for importing revisions.
2251
The order must be such that an revision is imported after all
2252
its (present) parents.
2254
todo = set(self.revisions.keys())
2255
done = self.absent_revisions.copy()
2258
# scan through looking for a revision whose parents
2260
for rev_id in sorted(list(todo)):
2261
rev = self.revisions[rev_id]
2262
parent_ids = set(rev.parent_ids)
2263
if parent_ids.issubset(done):
2264
# can take this one now
2265
order.append(rev_id)
2271
class ConvertBzrDir5To6(Converter):
2272
"""Converts format 5 bzr dirs to format 6."""
2274
def convert(self, to_convert, pb):
2275
"""See Converter.convert()."""
2276
self.bzrdir = to_convert
2278
self.pb.note('starting upgrade from format 5 to 6')
2279
self._convert_to_prefixed()
2280
return BzrDir.open(self.bzrdir.root_transport.base)
2282
def _convert_to_prefixed(self):
2283
from bzrlib.store import TransportStore
2284
self.bzrdir.transport.delete('branch-format')
2285
for store_name in ["weaves", "revision-store"]:
2286
self.pb.note("adding prefixes to %s" % store_name)
2287
store_transport = self.bzrdir.transport.clone(store_name)
2288
store = TransportStore(store_transport, prefixed=True)
2289
for urlfilename in store_transport.list_dir('.'):
2290
filename = urlutils.unescape(urlfilename)
2291
if (filename.endswith(".weave") or
2292
filename.endswith(".gz") or
2293
filename.endswith(".sig")):
2294
file_id, suffix = os.path.splitext(filename)
2298
new_name = store._mapper.map((file_id,)) + suffix
2299
# FIXME keep track of the dirs made RBC 20060121
2301
store_transport.move(filename, new_name)
2302
except errors.NoSuchFile: # catches missing dirs strangely enough
2303
store_transport.mkdir(osutils.dirname(new_name))
2304
store_transport.move(filename, new_name)
2305
self.bzrdir.transport.put_bytes(
2307
BzrDirFormat6().get_format_string(),
2308
mode=self.bzrdir._get_file_mode())
2311
class ConvertBzrDir6ToMeta(Converter):
2312
"""Converts format 6 bzr dirs to metadirs."""
2314
def convert(self, to_convert, pb):
2315
"""See Converter.convert()."""
2316
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2317
from bzrlib.branch import BzrBranchFormat5
2318
self.bzrdir = to_convert
2321
self.total = 20 # the steps we know about
2322
self.garbage_inventories = []
2323
self.dir_mode = self.bzrdir._get_dir_mode()
2324
self.file_mode = self.bzrdir._get_file_mode()
2326
self.pb.note('starting upgrade from format 6 to metadir')
2327
self.bzrdir.transport.put_bytes(
2329
"Converting to format 6",
2330
mode=self.file_mode)
2331
# its faster to move specific files around than to open and use the apis...
2332
# first off, nuke ancestry.weave, it was never used.
2334
self.step('Removing ancestry.weave')
2335
self.bzrdir.transport.delete('ancestry.weave')
2336
except errors.NoSuchFile:
2338
# find out whats there
2339
self.step('Finding branch files')
2340
last_revision = self.bzrdir.open_branch().last_revision()
2341
bzrcontents = self.bzrdir.transport.list_dir('.')
2342
for name in bzrcontents:
2343
if name.startswith('basis-inventory.'):
2344
self.garbage_inventories.append(name)
2345
# create new directories for repository, working tree and branch
2346
repository_names = [('inventory.weave', True),
2347
('revision-store', True),
2349
self.step('Upgrading repository ')
2350
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2351
self.make_lock('repository')
2352
# we hard code the formats here because we are converting into
2353
# the meta format. The meta format upgrader can take this to a
2354
# future format within each component.
2355
self.put_format('repository', RepositoryFormat7())
2356
for entry in repository_names:
2357
self.move_entry('repository', entry)
2359
self.step('Upgrading branch ')
2360
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2361
self.make_lock('branch')
2362
self.put_format('branch', BzrBranchFormat5())
2363
branch_files = [('revision-history', True),
2364
('branch-name', True),
2366
for entry in branch_files:
2367
self.move_entry('branch', entry)
2369
checkout_files = [('pending-merges', True),
2370
('inventory', True),
2371
('stat-cache', False)]
2372
# If a mandatory checkout file is not present, the branch does not have
2373
# a functional checkout. Do not create a checkout in the converted
2375
for name, mandatory in checkout_files:
2376
if mandatory and name not in bzrcontents:
2377
has_checkout = False
2381
if not has_checkout:
2382
self.pb.note('No working tree.')
2383
# If some checkout files are there, we may as well get rid of them.
2384
for name, mandatory in checkout_files:
2385
if name in bzrcontents:
2386
self.bzrdir.transport.delete(name)
2388
from bzrlib.workingtree import WorkingTreeFormat3
2389
self.step('Upgrading working tree')
2390
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2391
self.make_lock('checkout')
2393
'checkout', WorkingTreeFormat3())
2394
self.bzrdir.transport.delete_multi(
2395
self.garbage_inventories, self.pb)
2396
for entry in checkout_files:
2397
self.move_entry('checkout', entry)
2398
if last_revision is not None:
2399
self.bzrdir.transport.put_bytes(
2400
'checkout/last-revision', last_revision)
2401
self.bzrdir.transport.put_bytes(
2403
BzrDirMetaFormat1().get_format_string(),
2404
mode=self.file_mode)
2405
return BzrDir.open(self.bzrdir.root_transport.base)
2407
def make_lock(self, name):
2408
"""Make a lock for the new control dir name."""
2409
self.step('Make %s lock' % name)
2410
ld = lockdir.LockDir(self.bzrdir.transport,
2412
file_modebits=self.file_mode,
2413
dir_modebits=self.dir_mode)
2416
def move_entry(self, new_dir, entry):
2417
"""Move then entry name into new_dir."""
2419
mandatory = entry[1]
2420
self.step('Moving %s' % name)
2422
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2423
except errors.NoSuchFile:
2427
def put_format(self, dirname, format):
2428
self.bzrdir.transport.put_bytes('%s/format' % dirname,
2429
format.get_format_string(),
2433
class ConvertMetaToMeta(Converter):
2434
"""Converts the components of metadirs."""
2436
def __init__(self, target_format):
2437
"""Create a metadir to metadir converter.
2439
:param target_format: The final metadir format that is desired.
2441
self.target_format = target_format
2443
def convert(self, to_convert, pb):
2444
"""See Converter.convert()."""
2445
self.bzrdir = to_convert
2449
self.step('checking repository format')
2451
repo = self.bzrdir.open_repository()
2452
except errors.NoRepositoryPresent:
2455
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2456
from bzrlib.repository import CopyConverter
2457
self.pb.note('starting repository conversion')
2458
converter = CopyConverter(self.target_format.repository_format)
2459
converter.convert(repo, pb)
2461
branch = self.bzrdir.open_branch()
2462
except errors.NotBranchError:
2465
# TODO: conversions of Branch and Tree should be done by
2466
# InterXFormat lookups/some sort of registry.
2467
# Avoid circular imports
2468
from bzrlib import branch as _mod_branch
2469
old = branch._format.__class__
2470
new = self.target_format.get_branch_format().__class__
2472
if (old == _mod_branch.BzrBranchFormat5 and
2473
new in (_mod_branch.BzrBranchFormat6,
2474
_mod_branch.BzrBranchFormat7)):
2475
branch_converter = _mod_branch.Converter5to6()
2476
elif (old == _mod_branch.BzrBranchFormat6 and
2477
new == _mod_branch.BzrBranchFormat7):
2478
branch_converter = _mod_branch.Converter6to7()
2480
raise errors.BadConversionTarget("No converter", new)
2481
branch_converter.convert(branch)
2482
branch = self.bzrdir.open_branch()
2483
old = branch._format.__class__
2485
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2486
except (errors.NoWorkingTree, errors.NotLocalUrl):
2489
# TODO: conversions of Branch and Tree should be done by
2490
# InterXFormat lookups
2491
if (isinstance(tree, workingtree.WorkingTree3) and
2492
not isinstance(tree, workingtree_4.WorkingTree4) and
2493
isinstance(self.target_format.workingtree_format,
2494
workingtree_4.WorkingTreeFormat4)):
2495
workingtree_4.Converter3to4().convert(tree)
2499
# This is not in remote.py because it's small, and needs to be registered.
2500
# Putting it in remote.py creates a circular import problem.
2501
# we can make it a lazy object if the control formats is turned into something
2503
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2504
"""Format representing bzrdirs accessed via a smart server"""
2506
def get_format_description(self):
2507
return 'bzr remote bzrdir'
2510
def probe_transport(klass, transport):
2511
"""Return a RemoteBzrDirFormat object if it looks possible."""
2513
medium = transport.get_smart_medium()
2514
except (NotImplementedError, AttributeError,
2515
errors.TransportNotPossible, errors.NoSmartMedium,
2516
errors.SmartProtocolError):
2517
# no smart server, so not a branch for this format type.
2518
raise errors.NotBranchError(path=transport.base)
2520
# Decline to open it if the server doesn't support our required
2521
# version (3) so that the VFS-based transport will do it.
2522
if medium.should_probe():
2524
server_version = medium.protocol_version()
2525
except errors.SmartProtocolError:
2526
# Apparently there's no usable smart server there, even though
2527
# the medium supports the smart protocol.
2528
raise errors.NotBranchError(path=transport.base)
2529
if server_version != '2':
2530
raise errors.NotBranchError(path=transport.base)
2533
def initialize_on_transport(self, transport):
2535
# hand off the request to the smart server
2536
client_medium = transport.get_smart_medium()
2537
except errors.NoSmartMedium:
2538
# TODO: lookup the local format from a server hint.
2539
local_dir_format = BzrDirMetaFormat1()
2540
return local_dir_format.initialize_on_transport(transport)
2541
client = _SmartClient(client_medium)
2542
path = client.remote_path_from_transport(transport)
2543
response = client.call('BzrDirFormat.initialize', path)
2544
if response[0] != 'ok':
2545
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
2546
return remote.RemoteBzrDir(transport)
2548
def _open(self, transport):
2549
return remote.RemoteBzrDir(transport)
2551
def __eq__(self, other):
2552
if not isinstance(other, RemoteBzrDirFormat):
2554
return self.get_format_description() == other.get_format_description()
2557
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2560
class BzrDirFormatInfo(object):
2562
def __init__(self, native, deprecated, hidden, experimental):
2563
self.deprecated = deprecated
2564
self.native = native
2565
self.hidden = hidden
2566
self.experimental = experimental
2569
class BzrDirFormatRegistry(registry.Registry):
2570
"""Registry of user-selectable BzrDir subformats.
2572
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2573
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2577
"""Create a BzrDirFormatRegistry."""
2578
self._aliases = set()
2579
super(BzrDirFormatRegistry, self).__init__()
2582
"""Return a set of the format names which are aliases."""
2583
return frozenset(self._aliases)
2585
def register_metadir(self, key,
2586
repository_format, help, native=True, deprecated=False,
2592
"""Register a metadir subformat.
2594
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2595
by the Repository format.
2597
:param repository_format: The fully-qualified repository format class
2599
:param branch_format: Fully-qualified branch format class name as
2601
:param tree_format: Fully-qualified tree format class name as
2604
# This should be expanded to support setting WorkingTree and Branch
2605
# formats, once BzrDirMetaFormat1 supports that.
2606
def _load(full_name):
2607
mod_name, factory_name = full_name.rsplit('.', 1)
2609
mod = __import__(mod_name, globals(), locals(),
2611
except ImportError, e:
2612
raise ImportError('failed to load %s: %s' % (full_name, e))
2614
factory = getattr(mod, factory_name)
2615
except AttributeError:
2616
raise AttributeError('no factory %s in module %r'
2621
bd = BzrDirMetaFormat1()
2622
if branch_format is not None:
2623
bd.set_branch_format(_load(branch_format))
2624
if tree_format is not None:
2625
bd.workingtree_format = _load(tree_format)
2626
if repository_format is not None:
2627
bd.repository_format = _load(repository_format)
2629
self.register(key, helper, help, native, deprecated, hidden,
2630
experimental, alias)
2632
def register(self, key, factory, help, native=True, deprecated=False,
2633
hidden=False, experimental=False, alias=False):
2634
"""Register a BzrDirFormat factory.
2636
The factory must be a callable that takes one parameter: the key.
2637
It must produce an instance of the BzrDirFormat when called.
2639
This function mainly exists to prevent the info object from being
2642
registry.Registry.register(self, key, factory, help,
2643
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2645
self._aliases.add(key)
2647
def register_lazy(self, key, module_name, member_name, help, native=True,
2648
deprecated=False, hidden=False, experimental=False, alias=False):
2649
registry.Registry.register_lazy(self, key, module_name, member_name,
2650
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2652
self._aliases.add(key)
2654
def set_default(self, key):
2655
"""Set the 'default' key to be a clone of the supplied key.
2657
This method must be called once and only once.
2659
registry.Registry.register(self, 'default', self.get(key),
2660
self.get_help(key), info=self.get_info(key))
2661
self._aliases.add('default')
2663
def set_default_repository(self, key):
2664
"""Set the FormatRegistry default and Repository default.
2666
This is a transitional method while Repository.set_default_format
2669
if 'default' in self:
2670
self.remove('default')
2671
self.set_default(key)
2672
format = self.get('default')()
2674
def make_bzrdir(self, key):
2675
return self.get(key)()
2677
def help_topic(self, topic):
2678
output = textwrap.dedent("""\
2679
These formats can be used for creating branches, working trees, and
2683
default_realkey = None
2684
default_help = self.get_help('default')
2686
for key in self.keys():
2687
if key == 'default':
2689
help = self.get_help(key)
2690
if help == default_help:
2691
default_realkey = key
2693
help_pairs.append((key, help))
2695
def wrapped(key, help, info):
2697
help = '(native) ' + help
2698
return ':%s:\n%s\n\n' % (key,
2699
textwrap.fill(help, initial_indent=' ',
2700
subsequent_indent=' '))
2701
if default_realkey is not None:
2702
output += wrapped(default_realkey, '(default) %s' % default_help,
2703
self.get_info('default'))
2704
deprecated_pairs = []
2705
experimental_pairs = []
2706
for key, help in help_pairs:
2707
info = self.get_info(key)
2710
elif info.deprecated:
2711
deprecated_pairs.append((key, help))
2712
elif info.experimental:
2713
experimental_pairs.append((key, help))
2715
output += wrapped(key, help, info)
2716
if len(experimental_pairs) > 0:
2717
output += "Experimental formats are shown below.\n\n"
2718
for key, help in experimental_pairs:
2719
info = self.get_info(key)
2720
output += wrapped(key, help, info)
2721
if len(deprecated_pairs) > 0:
2722
output += "Deprecated formats are shown below.\n\n"
2723
for key, help in deprecated_pairs:
2724
info = self.get_info(key)
2725
output += wrapped(key, help, info)
2730
class RepositoryAcquisitionPolicy(object):
2731
"""Abstract base class for repository acquisition policies.
2733
A repository acquisition policy decides how a BzrDir acquires a repository
2734
for a branch that is being created. The most basic policy decision is
2735
whether to create a new repository or use an existing one.
2738
def configure_branch(self, branch):
2739
"""Apply any configuration data from this policy to the branch.
2741
Default implementation does nothing.
2745
def acquire_repository(self, make_working_trees=None, shared=False):
2746
"""Acquire a repository for this bzrdir.
2748
Implementations may create a new repository or use a pre-exising
2750
:param make_working_trees: If creating a repository, set
2751
make_working_trees to this value (if non-None)
2752
:param shared: If creating a repository, make it shared if True
2753
:return: A repository
2755
raise NotImplemented(RepositoryAcquisitionPolicy.acquire_repository)
2758
class CreateRepository(RepositoryAcquisitionPolicy):
2759
"""A policy of creating a new repository"""
2761
def __init__(self, bzrdir):
2762
RepositoryAcquisitionPolicy.__init__(self)
2763
self._bzrdir = bzrdir
2765
def acquire_repository(self, make_working_trees=None, shared=False):
2766
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
2768
Creates the desired repository in the bzrdir we already have.
2770
repository = self._bzrdir.create_repository(shared=shared)
2771
if make_working_trees is not None:
2772
repository.set_make_working_trees(make_working_trees)
2776
class UseExistingRepository(RepositoryAcquisitionPolicy):
2777
"""A policy of reusing an existing repository"""
2779
def __init__(self, repository):
2780
RepositoryAcquisitionPolicy.__init__(self)
2781
self._repository = repository
2783
def acquire_repository(self, make_working_trees=None, shared=False):
2784
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
2786
Returns an existing repository to use
2788
return self._repository
2791
format_registry = BzrDirFormatRegistry()
2792
format_registry.register('weave', BzrDirFormat6,
2793
'Pre-0.8 format. Slower than knit and does not'
2794
' support checkouts or shared repositories.',
2796
format_registry.register_metadir('knit',
2797
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2798
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2799
branch_format='bzrlib.branch.BzrBranchFormat5',
2800
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2801
format_registry.register_metadir('metaweave',
2802
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2803
'Transitional format in 0.8. Slower than knit.',
2804
branch_format='bzrlib.branch.BzrBranchFormat5',
2805
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2807
format_registry.register_metadir('dirstate',
2808
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2809
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2810
'above when accessed over the network.',
2811
branch_format='bzrlib.branch.BzrBranchFormat5',
2812
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2813
# directly from workingtree_4 triggers a circular import.
2814
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2816
format_registry.register_metadir('dirstate-tags',
2817
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2818
help='New in 0.15: Fast local operations and improved scaling for '
2819
'network operations. Additionally adds support for tags.'
2820
' Incompatible with bzr < 0.15.',
2821
branch_format='bzrlib.branch.BzrBranchFormat6',
2822
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2824
format_registry.register_metadir('rich-root',
2825
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
2826
help='New in 1.0. Better handling of tree roots. Incompatible with'
2828
branch_format='bzrlib.branch.BzrBranchFormat6',
2829
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2831
format_registry.register_metadir('dirstate-with-subtree',
2832
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2833
help='New in 0.15: Fast local operations and improved scaling for '
2834
'network operations. Additionally adds support for versioning nested '
2835
'bzr branches. Incompatible with bzr < 0.15.',
2836
branch_format='bzrlib.branch.BzrBranchFormat6',
2837
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2841
format_registry.register_metadir('pack-0.92',
2842
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2843
help='New in 0.92: Pack-based format with data compatible with '
2844
'dirstate-tags format repositories. Interoperates with '
2845
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2846
'Previously called knitpack-experimental. '
2847
'For more information, see '
2848
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2849
branch_format='bzrlib.branch.BzrBranchFormat6',
2850
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2852
format_registry.register_metadir('pack-0.92-subtree',
2853
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2854
help='New in 0.92: Pack-based format with data compatible with '
2855
'dirstate-with-subtree format repositories. Interoperates with '
2856
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2857
'Previously called knitpack-experimental. '
2858
'For more information, see '
2859
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2860
branch_format='bzrlib.branch.BzrBranchFormat6',
2861
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2865
format_registry.register_metadir('rich-root-pack',
2866
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
2867
help='New in 1.0: Pack-based format with data compatible with '
2868
'rich-root format repositories. Incompatible with'
2870
branch_format='bzrlib.branch.BzrBranchFormat6',
2871
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2873
# The following two formats should always just be aliases.
2874
format_registry.register_metadir('development',
2875
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1',
2876
help='Current development format. Can convert data to and from pack-0.92 '
2877
'(and anything compatible with pack-0.92) format repositories. '
2878
'Repositories and branches in this format can only be read by bzr.dev. '
2880
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2882
branch_format='bzrlib.branch.BzrBranchFormat7',
2883
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2887
format_registry.register_metadir('development-subtree',
2888
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1Subtree',
2889
help='Current development format, subtree variant. Can convert data to and '
2890
'from pack-0.92-subtree (and anything compatible with '
2891
'pack-0.92-subtree) format repositories. Repositories and branches in '
2892
'this format can only be read by bzr.dev. Please read '
2893
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2895
branch_format='bzrlib.branch.BzrBranchFormat7',
2896
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2900
# And the development formats which the will have aliased one of follow:
2901
format_registry.register_metadir('development0',
2902
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
2903
help='Trivial rename of pack-0.92 to provide a development format. '
2905
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2907
branch_format='bzrlib.branch.BzrBranchFormat6',
2908
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2912
format_registry.register_metadir('development0-subtree',
2913
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
2914
help='Trivial rename of pack-0.92-subtree to provide a development format. '
2916
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2918
branch_format='bzrlib.branch.BzrBranchFormat6',
2919
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2923
format_registry.register_metadir('development1',
2924
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1',
2925
help='A branch and pack based repository that supports stacking. '
2927
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2929
branch_format='bzrlib.branch.BzrBranchFormat7',
2930
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2934
format_registry.register_metadir('development1-subtree',
2935
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment1Subtree',
2936
help='A branch and pack based repository that supports stacking. '
2938
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2940
branch_format='bzrlib.branch.BzrBranchFormat7',
2941
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2945
# The current format that is made on 'bzr init'.
2946
format_registry.set_default('pack-0.92')