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):
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,
996
target_transport = get_transport(url, possible_transports)
997
target_transport.ensure_base()
998
cloning_format = self.cloning_metadir()
999
result = cloning_format.initialize_on_transport(target_transport)
1001
source_branch = self.open_branch()
1002
source_repository = source_branch.repository
1003
except errors.NotBranchError:
1004
source_branch = None
1006
source_repository = self.open_repository()
1007
except errors.NoRepositoryPresent:
1008
source_repository = None
1013
result_repo = result.find_repository()
1014
except errors.NoRepositoryPresent:
1016
if source_repository is None and result_repo is not None:
1018
elif source_repository is None and result_repo is None:
1019
# no repo available, make a new one
1020
result.create_repository()
1021
elif source_repository is not None and result_repo is None:
1022
# have source, and want to make a new target repo
1023
result_repo = source_repository.sprout(result,
1024
revision_id=revision_id)
1026
# fetch needed content into target.
1027
if source_repository is not None:
1029
# source_repository.copy_content_into(result_repo,
1030
# revision_id=revision_id)
1031
# so we can override the copy method
1032
result_repo.fetch(source_repository, revision_id=revision_id)
1033
if source_branch is not None:
1034
source_branch.sprout(result, revision_id=revision_id)
1036
result.create_branch()
1037
if isinstance(target_transport, LocalTransport) and (
1038
result_repo is None or result_repo.make_working_trees()):
1039
wt = result.create_workingtree(accelerator_tree=accelerator_tree,
1043
if wt.path2id('') is None:
1045
wt.set_root_id(self.open_workingtree.get_root_id())
1046
except errors.NoWorkingTree:
1052
if recurse == 'down':
1054
basis = wt.basis_tree()
1056
subtrees = basis.iter_references()
1057
recurse_branch = wt.branch
1058
elif source_branch is not None:
1059
basis = source_branch.basis_tree()
1061
subtrees = basis.iter_references()
1062
recurse_branch = source_branch
1067
for path, file_id in subtrees:
1068
target = urlutils.join(url, urlutils.escape(path))
1069
sublocation = source_branch.reference_parent(file_id, path)
1070
sublocation.bzrdir.sprout(target,
1071
basis.get_reference_revision(file_id, path),
1072
force_new_repo=force_new_repo, recurse=recurse)
1074
if basis is not None:
1079
class BzrDirPreSplitOut(BzrDir):
1080
"""A common class for the all-in-one formats."""
1082
def __init__(self, _transport, _format):
1083
"""See BzrDir.__init__."""
1084
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
1085
self._control_files = lockable_files.LockableFiles(
1086
self.get_branch_transport(None),
1087
self._format._lock_file_name,
1088
self._format._lock_class)
1090
def break_lock(self):
1091
"""Pre-splitout bzrdirs do not suffer from stale locks."""
1092
raise NotImplementedError(self.break_lock)
1094
def cloning_metadir(self):
1095
"""Produce a metadir suitable for cloning with."""
1096
return self._format.__class__()
1098
def clone(self, url, revision_id=None, force_new_repo=False):
1099
"""See BzrDir.clone()."""
1100
from bzrlib.workingtree import WorkingTreeFormat2
1101
self._make_tail(url)
1102
result = self._format._initialize_for_clone(url)
1103
self.open_repository().clone(result, revision_id=revision_id)
1104
from_branch = self.open_branch()
1105
from_branch.clone(result, revision_id=revision_id)
1107
self.open_workingtree().clone(result)
1108
except errors.NotLocalUrl:
1109
# make a new one, this format always has to have one.
1111
WorkingTreeFormat2().initialize(result)
1112
except errors.NotLocalUrl:
1113
# but we cannot do it for remote trees.
1114
to_branch = result.open_branch()
1115
WorkingTreeFormat2()._stub_initialize_remote(to_branch)
1118
def create_branch(self):
1119
"""See BzrDir.create_branch."""
1120
return self.open_branch()
1122
def destroy_branch(self):
1123
"""See BzrDir.destroy_branch."""
1124
raise errors.UnsupportedOperation(self.destroy_branch, self)
1126
def create_repository(self, shared=False):
1127
"""See BzrDir.create_repository."""
1129
raise errors.IncompatibleFormat('shared repository', self._format)
1130
return self.open_repository()
1132
def destroy_repository(self):
1133
"""See BzrDir.destroy_repository."""
1134
raise errors.UnsupportedOperation(self.destroy_repository, self)
1136
def create_workingtree(self, revision_id=None, from_branch=None,
1137
accelerator_tree=None, hardlink=False):
1138
"""See BzrDir.create_workingtree."""
1139
# this looks buggy but is not -really-
1140
# because this format creates the workingtree when the bzrdir is
1142
# clone and sprout will have set the revision_id
1143
# and that will have set it for us, its only
1144
# specific uses of create_workingtree in isolation
1145
# that can do wonky stuff here, and that only
1146
# happens for creating checkouts, which cannot be
1147
# done on this format anyway. So - acceptable wart.
1148
result = self.open_workingtree(recommend_upgrade=False)
1149
if revision_id is not None:
1150
if revision_id == _mod_revision.NULL_REVISION:
1151
result.set_parent_ids([])
1153
result.set_parent_ids([revision_id])
1156
def destroy_workingtree(self):
1157
"""See BzrDir.destroy_workingtree."""
1158
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
1160
def destroy_workingtree_metadata(self):
1161
"""See BzrDir.destroy_workingtree_metadata."""
1162
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
1165
def get_branch_transport(self, branch_format):
1166
"""See BzrDir.get_branch_transport()."""
1167
if branch_format is None:
1168
return self.transport
1170
branch_format.get_format_string()
1171
except NotImplementedError:
1172
return self.transport
1173
raise errors.IncompatibleFormat(branch_format, self._format)
1175
def get_repository_transport(self, repository_format):
1176
"""See BzrDir.get_repository_transport()."""
1177
if repository_format is None:
1178
return self.transport
1180
repository_format.get_format_string()
1181
except NotImplementedError:
1182
return self.transport
1183
raise errors.IncompatibleFormat(repository_format, self._format)
1185
def get_workingtree_transport(self, workingtree_format):
1186
"""See BzrDir.get_workingtree_transport()."""
1187
if workingtree_format is None:
1188
return self.transport
1190
workingtree_format.get_format_string()
1191
except NotImplementedError:
1192
return self.transport
1193
raise errors.IncompatibleFormat(workingtree_format, self._format)
1195
def needs_format_conversion(self, format=None):
1196
"""See BzrDir.needs_format_conversion()."""
1197
# if the format is not the same as the system default,
1198
# an upgrade is needed.
1200
format = BzrDirFormat.get_default_format()
1201
return not isinstance(self._format, format.__class__)
1203
def open_branch(self, unsupported=False):
1204
"""See BzrDir.open_branch."""
1205
from bzrlib.branch import BzrBranchFormat4
1206
format = BzrBranchFormat4()
1207
self._check_supported(format, unsupported)
1208
return format.open(self, _found=True)
1210
def sprout(self, url, revision_id=None, force_new_repo=False,
1211
possible_transports=None, accelerator_tree=None,
1213
"""See BzrDir.sprout()."""
1214
from bzrlib.workingtree import WorkingTreeFormat2
1215
self._make_tail(url)
1216
result = self._format._initialize_for_clone(url)
1218
self.open_repository().clone(result, revision_id=revision_id)
1219
except errors.NoRepositoryPresent:
1222
self.open_branch().sprout(result, revision_id=revision_id)
1223
except errors.NotBranchError:
1225
# we always want a working tree
1226
WorkingTreeFormat2().initialize(result,
1227
accelerator_tree=accelerator_tree,
1232
class BzrDir4(BzrDirPreSplitOut):
1233
"""A .bzr version 4 control object.
1235
This is a deprecated format and may be removed after sept 2006.
1238
def create_repository(self, shared=False):
1239
"""See BzrDir.create_repository."""
1240
return self._format.repository_format.initialize(self, shared)
1242
def needs_format_conversion(self, format=None):
1243
"""Format 4 dirs are always in need of conversion."""
1246
def open_repository(self):
1247
"""See BzrDir.open_repository."""
1248
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1249
return RepositoryFormat4().open(self, _found=True)
1252
class BzrDir5(BzrDirPreSplitOut):
1253
"""A .bzr version 5 control object.
1255
This is a deprecated format and may be removed after sept 2006.
1258
def open_repository(self):
1259
"""See BzrDir.open_repository."""
1260
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1261
return RepositoryFormat5().open(self, _found=True)
1263
def open_workingtree(self, _unsupported=False,
1264
recommend_upgrade=True):
1265
"""See BzrDir.create_workingtree."""
1266
from bzrlib.workingtree import WorkingTreeFormat2
1267
wt_format = WorkingTreeFormat2()
1268
# we don't warn here about upgrades; that ought to be handled for the
1270
return wt_format.open(self, _found=True)
1273
class BzrDir6(BzrDirPreSplitOut):
1274
"""A .bzr version 6 control object.
1276
This is a deprecated format and may be removed after sept 2006.
1279
def open_repository(self):
1280
"""See BzrDir.open_repository."""
1281
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1282
return RepositoryFormat6().open(self, _found=True)
1284
def open_workingtree(self, _unsupported=False,
1285
recommend_upgrade=True):
1286
"""See BzrDir.create_workingtree."""
1287
# we don't warn here about upgrades; that ought to be handled for the
1289
from bzrlib.workingtree import WorkingTreeFormat2
1290
return WorkingTreeFormat2().open(self, _found=True)
1293
class BzrDirMeta1(BzrDir):
1294
"""A .bzr meta version 1 control object.
1296
This is the first control object where the
1297
individual aspects are really split out: there are separate repository,
1298
workingtree and branch subdirectories and any subset of the three can be
1299
present within a BzrDir.
1302
def can_convert_format(self):
1303
"""See BzrDir.can_convert_format()."""
1306
def create_branch(self):
1307
"""See BzrDir.create_branch."""
1308
return self._format.get_branch_format().initialize(self)
1310
def destroy_branch(self):
1311
"""See BzrDir.create_branch."""
1312
self.transport.delete_tree('branch')
1314
def create_repository(self, shared=False):
1315
"""See BzrDir.create_repository."""
1316
return self._format.repository_format.initialize(self, shared)
1318
def destroy_repository(self):
1319
"""See BzrDir.destroy_repository."""
1320
self.transport.delete_tree('repository')
1322
def create_workingtree(self, revision_id=None, from_branch=None,
1323
accelerator_tree=None, hardlink=False):
1324
"""See BzrDir.create_workingtree."""
1325
return self._format.workingtree_format.initialize(
1326
self, revision_id, from_branch=from_branch,
1327
accelerator_tree=accelerator_tree, hardlink=hardlink)
1329
def destroy_workingtree(self):
1330
"""See BzrDir.destroy_workingtree."""
1331
wt = self.open_workingtree(recommend_upgrade=False)
1332
repository = wt.branch.repository
1333
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1334
wt.revert(old_tree=empty)
1335
self.destroy_workingtree_metadata()
1337
def destroy_workingtree_metadata(self):
1338
self.transport.delete_tree('checkout')
1340
def find_branch_format(self):
1341
"""Find the branch 'format' for this bzrdir.
1343
This might be a synthetic object for e.g. RemoteBranch and SVN.
1345
from bzrlib.branch import BranchFormat
1346
return BranchFormat.find_format(self)
1348
def _get_mkdir_mode(self):
1349
"""Figure out the mode to use when creating a bzrdir subdir."""
1350
temp_control = lockable_files.LockableFiles(self.transport, '',
1351
lockable_files.TransportLock)
1352
return temp_control._dir_mode
1354
def get_branch_reference(self):
1355
"""See BzrDir.get_branch_reference()."""
1356
from bzrlib.branch import BranchFormat
1357
format = BranchFormat.find_format(self)
1358
return format.get_reference(self)
1360
def get_branch_transport(self, branch_format):
1361
"""See BzrDir.get_branch_transport()."""
1362
if branch_format is None:
1363
return self.transport.clone('branch')
1365
branch_format.get_format_string()
1366
except NotImplementedError:
1367
raise errors.IncompatibleFormat(branch_format, self._format)
1369
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1370
except errors.FileExists:
1372
return self.transport.clone('branch')
1374
def get_repository_transport(self, repository_format):
1375
"""See BzrDir.get_repository_transport()."""
1376
if repository_format is None:
1377
return self.transport.clone('repository')
1379
repository_format.get_format_string()
1380
except NotImplementedError:
1381
raise errors.IncompatibleFormat(repository_format, self._format)
1383
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1384
except errors.FileExists:
1386
return self.transport.clone('repository')
1388
def get_workingtree_transport(self, workingtree_format):
1389
"""See BzrDir.get_workingtree_transport()."""
1390
if workingtree_format is None:
1391
return self.transport.clone('checkout')
1393
workingtree_format.get_format_string()
1394
except NotImplementedError:
1395
raise errors.IncompatibleFormat(workingtree_format, self._format)
1397
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1398
except errors.FileExists:
1400
return self.transport.clone('checkout')
1402
def needs_format_conversion(self, format=None):
1403
"""See BzrDir.needs_format_conversion()."""
1405
format = BzrDirFormat.get_default_format()
1406
if not isinstance(self._format, format.__class__):
1407
# it is not a meta dir format, conversion is needed.
1409
# we might want to push this down to the repository?
1411
if not isinstance(self.open_repository()._format,
1412
format.repository_format.__class__):
1413
# the repository needs an upgrade.
1415
except errors.NoRepositoryPresent:
1418
if not isinstance(self.open_branch()._format,
1419
format.get_branch_format().__class__):
1420
# the branch needs an upgrade.
1422
except errors.NotBranchError:
1425
my_wt = self.open_workingtree(recommend_upgrade=False)
1426
if not isinstance(my_wt._format,
1427
format.workingtree_format.__class__):
1428
# the workingtree needs an upgrade.
1430
except (errors.NoWorkingTree, errors.NotLocalUrl):
1434
def open_branch(self, unsupported=False):
1435
"""See BzrDir.open_branch."""
1436
format = self.find_branch_format()
1437
self._check_supported(format, unsupported)
1438
return format.open(self, _found=True)
1440
def open_repository(self, unsupported=False):
1441
"""See BzrDir.open_repository."""
1442
from bzrlib.repository import RepositoryFormat
1443
format = RepositoryFormat.find_format(self)
1444
self._check_supported(format, unsupported)
1445
return format.open(self, _found=True)
1447
def open_workingtree(self, unsupported=False,
1448
recommend_upgrade=True):
1449
"""See BzrDir.open_workingtree."""
1450
from bzrlib.workingtree import WorkingTreeFormat
1451
format = WorkingTreeFormat.find_format(self)
1452
self._check_supported(format, unsupported,
1454
basedir=self.root_transport.base)
1455
return format.open(self, _found=True)
1458
class BzrDirFormat(object):
1459
"""An encapsulation of the initialization and open routines for a format.
1461
Formats provide three things:
1462
* An initialization routine,
1466
Formats are placed in a dict by their format string for reference
1467
during bzrdir opening. These should be subclasses of BzrDirFormat
1470
Once a format is deprecated, just deprecate the initialize and open
1471
methods on the format class. Do not deprecate the object, as the
1472
object will be created every system load.
1475
_default_format = None
1476
"""The default format used for new .bzr dirs."""
1479
"""The known formats."""
1481
_control_formats = []
1482
"""The registered control formats - .bzr, ....
1484
This is a list of BzrDirFormat objects.
1487
_control_server_formats = []
1488
"""The registered control server formats, e.g. RemoteBzrDirs.
1490
This is a list of BzrDirFormat objects.
1493
_lock_file_name = 'branch-lock'
1495
# _lock_class must be set in subclasses to the lock type, typ.
1496
# TransportLock or LockDir
1499
def find_format(klass, transport, _server_formats=True):
1500
"""Return the format present at transport."""
1502
formats = klass._control_server_formats + klass._control_formats
1504
formats = klass._control_formats
1505
for format in formats:
1507
return format.probe_transport(transport)
1508
except errors.NotBranchError:
1509
# this format does not find a control dir here.
1511
raise errors.NotBranchError(path=transport.base)
1514
def probe_transport(klass, transport):
1515
"""Return the .bzrdir style format present in a directory."""
1517
format_string = transport.get(".bzr/branch-format").read()
1518
except errors.NoSuchFile:
1519
raise errors.NotBranchError(path=transport.base)
1522
return klass._formats[format_string]
1524
raise errors.UnknownFormatError(format=format_string, kind='bzrdir')
1527
def get_default_format(klass):
1528
"""Return the current default format."""
1529
return klass._default_format
1531
def get_format_string(self):
1532
"""Return the ASCII format string that identifies this format."""
1533
raise NotImplementedError(self.get_format_string)
1535
def get_format_description(self):
1536
"""Return the short description for this format."""
1537
raise NotImplementedError(self.get_format_description)
1539
def get_converter(self, format=None):
1540
"""Return the converter to use to convert bzrdirs needing converts.
1542
This returns a bzrlib.bzrdir.Converter object.
1544
This should return the best upgrader to step this format towards the
1545
current default format. In the case of plugins we can/should provide
1546
some means for them to extend the range of returnable converters.
1548
:param format: Optional format to override the default format of the
1551
raise NotImplementedError(self.get_converter)
1553
def initialize(self, url, possible_transports=None):
1554
"""Create a bzr control dir at this url and return an opened copy.
1556
Subclasses should typically override initialize_on_transport
1557
instead of this method.
1559
return self.initialize_on_transport(get_transport(url,
1560
possible_transports))
1562
def initialize_on_transport(self, transport):
1563
"""Initialize a new bzrdir in the base directory of a Transport."""
1564
# Since we don't have a .bzr directory, inherit the
1565
# mode from the root directory
1566
temp_control = lockable_files.LockableFiles(transport,
1567
'', lockable_files.TransportLock)
1568
temp_control._transport.mkdir('.bzr',
1569
# FIXME: RBC 20060121 don't peek under
1571
mode=temp_control._dir_mode)
1572
if sys.platform == 'win32' and isinstance(transport, LocalTransport):
1573
win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1574
file_mode = temp_control._file_mode
1576
bzrdir_transport = transport.clone('.bzr')
1577
utf8_files = [('README',
1578
"This is a Bazaar control directory.\n"
1579
"Do not change any files in this directory.\n"
1580
"See http://bazaar-vcs.org/ for more information about Bazaar.\n"),
1581
('branch-format', self.get_format_string()),
1583
# NB: no need to escape relative paths that are url safe.
1584
control_files = lockable_files.LockableFiles(bzrdir_transport,
1585
self._lock_file_name, self._lock_class)
1586
control_files.create_lock()
1587
control_files.lock_write()
1589
for (filename, content) in utf8_files:
1590
bzrdir_transport.put_bytes(filename, content,
1593
control_files.unlock()
1594
return self.open(transport, _found=True)
1596
def is_supported(self):
1597
"""Is this format supported?
1599
Supported formats must be initializable and openable.
1600
Unsupported formats may not support initialization or committing or
1601
some other features depending on the reason for not being supported.
1605
def same_model(self, target_format):
1606
return (self.repository_format.rich_root_data ==
1607
target_format.rich_root_data)
1610
def known_formats(klass):
1611
"""Return all the known formats.
1613
Concrete formats should override _known_formats.
1615
# There is double indirection here to make sure that control
1616
# formats used by more than one dir format will only be probed
1617
# once. This can otherwise be quite expensive for remote connections.
1619
for format in klass._control_formats:
1620
result.update(format._known_formats())
1624
def _known_formats(klass):
1625
"""Return the known format instances for this control format."""
1626
return set(klass._formats.values())
1628
def open(self, transport, _found=False):
1629
"""Return an instance of this format for the dir transport points at.
1631
_found is a private parameter, do not use it.
1634
found_format = BzrDirFormat.find_format(transport)
1635
if not isinstance(found_format, self.__class__):
1636
raise AssertionError("%s was asked to open %s, but it seems to need "
1638
% (self, transport, found_format))
1639
return self._open(transport)
1641
def _open(self, transport):
1642
"""Template method helper for opening BzrDirectories.
1644
This performs the actual open and any additional logic or parameter
1647
raise NotImplementedError(self._open)
1650
def register_format(klass, format):
1651
klass._formats[format.get_format_string()] = format
1654
def register_control_format(klass, format):
1655
"""Register a format that does not use '.bzr' for its control dir.
1657
TODO: This should be pulled up into a 'ControlDirFormat' base class
1658
which BzrDirFormat can inherit from, and renamed to register_format
1659
there. It has been done without that for now for simplicity of
1662
klass._control_formats.append(format)
1665
def register_control_server_format(klass, format):
1666
"""Register a control format for client-server environments.
1668
These formats will be tried before ones registered with
1669
register_control_format. This gives implementations that decide to the
1670
chance to grab it before anything looks at the contents of the format
1673
klass._control_server_formats.append(format)
1676
def _set_default_format(klass, format):
1677
"""Set default format (for testing behavior of defaults only)"""
1678
klass._default_format = format
1682
return self.get_format_string().rstrip()
1685
def unregister_format(klass, format):
1686
del klass._formats[format.get_format_string()]
1689
def unregister_control_format(klass, format):
1690
klass._control_formats.remove(format)
1693
class BzrDirFormat4(BzrDirFormat):
1694
"""Bzr dir format 4.
1696
This format is a combined format for working tree, branch and repository.
1698
- Format 1 working trees [always]
1699
- Format 4 branches [always]
1700
- Format 4 repositories [always]
1702
This format is deprecated: it indexes texts using a text it which is
1703
removed in format 5; write support for this format has been removed.
1706
_lock_class = lockable_files.TransportLock
1708
def get_format_string(self):
1709
"""See BzrDirFormat.get_format_string()."""
1710
return "Bazaar-NG branch, format 0.0.4\n"
1712
def get_format_description(self):
1713
"""See BzrDirFormat.get_format_description()."""
1714
return "All-in-one format 4"
1716
def get_converter(self, format=None):
1717
"""See BzrDirFormat.get_converter()."""
1718
# there is one and only one upgrade path here.
1719
return ConvertBzrDir4To5()
1721
def initialize_on_transport(self, transport):
1722
"""Format 4 branches cannot be created."""
1723
raise errors.UninitializableFormat(self)
1725
def is_supported(self):
1726
"""Format 4 is not supported.
1728
It is not supported because the model changed from 4 to 5 and the
1729
conversion logic is expensive - so doing it on the fly was not
1734
def _open(self, transport):
1735
"""See BzrDirFormat._open."""
1736
return BzrDir4(transport, self)
1738
def __return_repository_format(self):
1739
"""Circular import protection."""
1740
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1741
return RepositoryFormat4()
1742
repository_format = property(__return_repository_format)
1745
class BzrDirFormat5(BzrDirFormat):
1746
"""Bzr control format 5.
1748
This format is a combined format for working tree, branch and repository.
1750
- Format 2 working trees [always]
1751
- Format 4 branches [always]
1752
- Format 5 repositories [always]
1753
Unhashed stores in the repository.
1756
_lock_class = lockable_files.TransportLock
1758
def get_format_string(self):
1759
"""See BzrDirFormat.get_format_string()."""
1760
return "Bazaar-NG branch, format 5\n"
1762
def get_format_description(self):
1763
"""See BzrDirFormat.get_format_description()."""
1764
return "All-in-one format 5"
1766
def get_converter(self, format=None):
1767
"""See BzrDirFormat.get_converter()."""
1768
# there is one and only one upgrade path here.
1769
return ConvertBzrDir5To6()
1771
def _initialize_for_clone(self, url):
1772
return self.initialize_on_transport(get_transport(url), _cloning=True)
1774
def initialize_on_transport(self, transport, _cloning=False):
1775
"""Format 5 dirs always have working tree, branch and repository.
1777
Except when they are being cloned.
1779
from bzrlib.branch import BzrBranchFormat4
1780
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1781
from bzrlib.workingtree import WorkingTreeFormat2
1782
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1783
RepositoryFormat5().initialize(result, _internal=True)
1785
branch = BzrBranchFormat4().initialize(result)
1787
WorkingTreeFormat2().initialize(result)
1788
except errors.NotLocalUrl:
1789
# Even though we can't access the working tree, we need to
1790
# create its control files.
1791
WorkingTreeFormat2()._stub_initialize_remote(branch)
1794
def _open(self, transport):
1795
"""See BzrDirFormat._open."""
1796
return BzrDir5(transport, self)
1798
def __return_repository_format(self):
1799
"""Circular import protection."""
1800
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1801
return RepositoryFormat5()
1802
repository_format = property(__return_repository_format)
1805
class BzrDirFormat6(BzrDirFormat):
1806
"""Bzr control format 6.
1808
This format is a combined format for working tree, branch and repository.
1810
- Format 2 working trees [always]
1811
- Format 4 branches [always]
1812
- Format 6 repositories [always]
1815
_lock_class = lockable_files.TransportLock
1817
def get_format_string(self):
1818
"""See BzrDirFormat.get_format_string()."""
1819
return "Bazaar-NG branch, format 6\n"
1821
def get_format_description(self):
1822
"""See BzrDirFormat.get_format_description()."""
1823
return "All-in-one format 6"
1825
def get_converter(self, format=None):
1826
"""See BzrDirFormat.get_converter()."""
1827
# there is one and only one upgrade path here.
1828
return ConvertBzrDir6ToMeta()
1830
def _initialize_for_clone(self, url):
1831
return self.initialize_on_transport(get_transport(url), _cloning=True)
1833
def initialize_on_transport(self, transport, _cloning=False):
1834
"""Format 6 dirs always have working tree, branch and repository.
1836
Except when they are being cloned.
1838
from bzrlib.branch import BzrBranchFormat4
1839
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1840
from bzrlib.workingtree import WorkingTreeFormat2
1841
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1842
RepositoryFormat6().initialize(result, _internal=True)
1844
branch = BzrBranchFormat4().initialize(result)
1846
WorkingTreeFormat2().initialize(result)
1847
except errors.NotLocalUrl:
1848
# Even though we can't access the working tree, we need to
1849
# create its control files.
1850
WorkingTreeFormat2()._stub_initialize_remote(branch)
1853
def _open(self, transport):
1854
"""See BzrDirFormat._open."""
1855
return BzrDir6(transport, self)
1857
def __return_repository_format(self):
1858
"""Circular import protection."""
1859
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1860
return RepositoryFormat6()
1861
repository_format = property(__return_repository_format)
1864
class BzrDirMetaFormat1(BzrDirFormat):
1865
"""Bzr meta control format 1
1867
This is the first format with split out working tree, branch and repository
1870
- Format 3 working trees [optional]
1871
- Format 5 branches [optional]
1872
- Format 7 repositories [optional]
1875
_lock_class = lockdir.LockDir
1878
self._workingtree_format = None
1879
self._branch_format = None
1881
def __eq__(self, other):
1882
if other.__class__ is not self.__class__:
1884
if other.repository_format != self.repository_format:
1886
if other.workingtree_format != self.workingtree_format:
1890
def __ne__(self, other):
1891
return not self == other
1893
def get_branch_format(self):
1894
if self._branch_format is None:
1895
from bzrlib.branch import BranchFormat
1896
self._branch_format = BranchFormat.get_default_format()
1897
return self._branch_format
1899
def set_branch_format(self, format):
1900
self._branch_format = format
1902
def get_converter(self, format=None):
1903
"""See BzrDirFormat.get_converter()."""
1905
format = BzrDirFormat.get_default_format()
1906
if not isinstance(self, format.__class__):
1907
# converting away from metadir is not implemented
1908
raise NotImplementedError(self.get_converter)
1909
return ConvertMetaToMeta(format)
1911
def get_format_string(self):
1912
"""See BzrDirFormat.get_format_string()."""
1913
return "Bazaar-NG meta directory, format 1\n"
1915
def get_format_description(self):
1916
"""See BzrDirFormat.get_format_description()."""
1917
return "Meta directory format 1"
1919
def _open(self, transport):
1920
"""See BzrDirFormat._open."""
1921
return BzrDirMeta1(transport, self)
1923
def __return_repository_format(self):
1924
"""Circular import protection."""
1925
if getattr(self, '_repository_format', None):
1926
return self._repository_format
1927
from bzrlib.repository import RepositoryFormat
1928
return RepositoryFormat.get_default_format()
1930
def __set_repository_format(self, value):
1931
"""Allow changing the repository format for metadir formats."""
1932
self._repository_format = value
1934
repository_format = property(__return_repository_format, __set_repository_format)
1936
def __get_workingtree_format(self):
1937
if self._workingtree_format is None:
1938
from bzrlib.workingtree import WorkingTreeFormat
1939
self._workingtree_format = WorkingTreeFormat.get_default_format()
1940
return self._workingtree_format
1942
def __set_workingtree_format(self, wt_format):
1943
self._workingtree_format = wt_format
1945
workingtree_format = property(__get_workingtree_format,
1946
__set_workingtree_format)
1949
# Register bzr control format
1950
BzrDirFormat.register_control_format(BzrDirFormat)
1952
# Register bzr formats
1953
BzrDirFormat.register_format(BzrDirFormat4())
1954
BzrDirFormat.register_format(BzrDirFormat5())
1955
BzrDirFormat.register_format(BzrDirFormat6())
1956
__default_format = BzrDirMetaFormat1()
1957
BzrDirFormat.register_format(__default_format)
1958
BzrDirFormat._default_format = __default_format
1961
class Converter(object):
1962
"""Converts a disk format object from one format to another."""
1964
def convert(self, to_convert, pb):
1965
"""Perform the conversion of to_convert, giving feedback via pb.
1967
:param to_convert: The disk object to convert.
1968
:param pb: a progress bar to use for progress information.
1971
def step(self, message):
1972
"""Update the pb by a step."""
1974
self.pb.update(message, self.count, self.total)
1977
class ConvertBzrDir4To5(Converter):
1978
"""Converts format 4 bzr dirs to format 5."""
1981
super(ConvertBzrDir4To5, self).__init__()
1982
self.converted_revs = set()
1983
self.absent_revisions = set()
1987
def convert(self, to_convert, pb):
1988
"""See Converter.convert()."""
1989
self.bzrdir = to_convert
1991
self.pb.note('starting upgrade from format 4 to 5')
1992
if isinstance(self.bzrdir.transport, LocalTransport):
1993
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1994
self._convert_to_weaves()
1995
return BzrDir.open(self.bzrdir.root_transport.base)
1997
def _convert_to_weaves(self):
1998
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
2001
stat = self.bzrdir.transport.stat('weaves')
2002
if not S_ISDIR(stat.st_mode):
2003
self.bzrdir.transport.delete('weaves')
2004
self.bzrdir.transport.mkdir('weaves')
2005
except errors.NoSuchFile:
2006
self.bzrdir.transport.mkdir('weaves')
2007
# deliberately not a WeaveFile as we want to build it up slowly.
2008
self.inv_weave = Weave('inventory')
2009
# holds in-memory weaves for all files
2010
self.text_weaves = {}
2011
self.bzrdir.transport.delete('branch-format')
2012
self.branch = self.bzrdir.open_branch()
2013
self._convert_working_inv()
2014
rev_history = self.branch.revision_history()
2015
# to_read is a stack holding the revisions we still need to process;
2016
# appending to it adds new highest-priority revisions
2017
self.known_revisions = set(rev_history)
2018
self.to_read = rev_history[-1:]
2020
rev_id = self.to_read.pop()
2021
if (rev_id not in self.revisions
2022
and rev_id not in self.absent_revisions):
2023
self._load_one_rev(rev_id)
2025
to_import = self._make_order()
2026
for i, rev_id in enumerate(to_import):
2027
self.pb.update('converting revision', i, len(to_import))
2028
self._convert_one_rev(rev_id)
2030
self._write_all_weaves()
2031
self._write_all_revs()
2032
self.pb.note('upgraded to weaves:')
2033
self.pb.note(' %6d revisions and inventories', len(self.revisions))
2034
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
2035
self.pb.note(' %6d texts', self.text_count)
2036
self._cleanup_spare_files_after_format4()
2037
self.branch._transport.put_bytes(
2039
BzrDirFormat5().get_format_string(),
2040
mode=self.bzrdir._get_file_mode())
2042
def _cleanup_spare_files_after_format4(self):
2043
# FIXME working tree upgrade foo.
2044
for n in 'merged-patches', 'pending-merged-patches':
2046
## assert os.path.getsize(p) == 0
2047
self.bzrdir.transport.delete(n)
2048
except errors.NoSuchFile:
2050
self.bzrdir.transport.delete_tree('inventory-store')
2051
self.bzrdir.transport.delete_tree('text-store')
2053
def _convert_working_inv(self):
2054
inv = xml4.serializer_v4.read_inventory(
2055
self.branch._transport.get('inventory'))
2056
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
2057
self.branch._transport.put_bytes('inventory', new_inv_xml,
2058
mode=self.bzrdir._get_file_mode())
2060
def _write_all_weaves(self):
2061
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
2062
weave_transport = self.bzrdir.transport.clone('weaves')
2063
weaves = WeaveStore(weave_transport, prefixed=False)
2064
transaction = WriteTransaction()
2068
for file_id, file_weave in self.text_weaves.items():
2069
self.pb.update('writing weave', i, len(self.text_weaves))
2070
weaves._put_weave(file_id, file_weave, transaction)
2072
self.pb.update('inventory', 0, 1)
2073
controlweaves._put_weave('inventory', self.inv_weave, transaction)
2074
self.pb.update('inventory', 1, 1)
2078
def _write_all_revs(self):
2079
"""Write all revisions out in new form."""
2080
self.bzrdir.transport.delete_tree('revision-store')
2081
self.bzrdir.transport.mkdir('revision-store')
2082
revision_transport = self.bzrdir.transport.clone('revision-store')
2084
from bzrlib.xml5 import serializer_v5
2085
from bzrlib.repofmt.weaverepo import RevisionTextStore
2086
revision_store = RevisionTextStore(revision_transport,
2087
serializer_v5, False, versionedfile.PrefixMapper(),
2088
lambda:True, lambda:True)
2090
for i, rev_id in enumerate(self.converted_revs):
2091
self.pb.update('write revision', i, len(self.converted_revs))
2092
text = serializer_v5.write_revision_to_string(
2093
self.revisions[rev_id])
2095
revision_store.add_lines(key, None, osutils.split_lines(text))
2099
def _load_one_rev(self, rev_id):
2100
"""Load a revision object into memory.
2102
Any parents not either loaded or abandoned get queued to be
2104
self.pb.update('loading revision',
2105
len(self.revisions),
2106
len(self.known_revisions))
2107
if not self.branch.repository.has_revision(rev_id):
2109
self.pb.note('revision {%s} not present in branch; '
2110
'will be converted as a ghost',
2112
self.absent_revisions.add(rev_id)
2114
rev = self.branch.repository.get_revision(rev_id)
2115
for parent_id in rev.parent_ids:
2116
self.known_revisions.add(parent_id)
2117
self.to_read.append(parent_id)
2118
self.revisions[rev_id] = rev
2120
def _load_old_inventory(self, rev_id):
2121
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
2122
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
2123
inv.revision_id = rev_id
2124
rev = self.revisions[rev_id]
2127
def _load_updated_inventory(self, rev_id):
2128
inv_xml = self.inv_weave.get_text(rev_id)
2129
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml, rev_id)
2132
def _convert_one_rev(self, rev_id):
2133
"""Convert revision and all referenced objects to new format."""
2134
rev = self.revisions[rev_id]
2135
inv = self._load_old_inventory(rev_id)
2136
present_parents = [p for p in rev.parent_ids
2137
if p not in self.absent_revisions]
2138
self._convert_revision_contents(rev, inv, present_parents)
2139
self._store_new_inv(rev, inv, present_parents)
2140
self.converted_revs.add(rev_id)
2142
def _store_new_inv(self, rev, inv, present_parents):
2143
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
2144
new_inv_sha1 = sha_string(new_inv_xml)
2145
self.inv_weave.add_lines(rev.revision_id,
2147
new_inv_xml.splitlines(True))
2148
rev.inventory_sha1 = new_inv_sha1
2150
def _convert_revision_contents(self, rev, inv, present_parents):
2151
"""Convert all the files within a revision.
2153
Also upgrade the inventory to refer to the text revision ids."""
2154
rev_id = rev.revision_id
2155
mutter('converting texts of revision {%s}',
2157
parent_invs = map(self._load_updated_inventory, present_parents)
2158
entries = inv.iter_entries()
2160
for path, ie in entries:
2161
self._convert_file_version(rev, ie, parent_invs)
2163
def _convert_file_version(self, rev, ie, parent_invs):
2164
"""Convert one version of one file.
2166
The file needs to be added into the weave if it is a merge
2167
of >=2 parents or if it's changed from its parent.
2169
file_id = ie.file_id
2170
rev_id = rev.revision_id
2171
w = self.text_weaves.get(file_id)
2174
self.text_weaves[file_id] = w
2175
text_changed = False
2176
parent_candiate_entries = ie.parent_candidates(parent_invs)
2177
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
2178
# XXX: Note that this is unordered - and this is tolerable because
2179
# the previous code was also unordered.
2180
previous_entries = dict((head, parent_candiate_entries[head]) for head
2182
self.snapshot_ie(previous_entries, ie, w, rev_id)
2185
@symbol_versioning.deprecated_method(symbol_versioning.one_one)
2186
def get_parents(self, revision_ids):
2187
for revision_id in revision_ids:
2188
yield self.revisions[revision_id].parent_ids
2190
def get_parent_map(self, revision_ids):
2191
"""See graph._StackedParentsProvider.get_parent_map"""
2192
return dict((revision_id, self.revisions[revision_id])
2193
for revision_id in revision_ids
2194
if revision_id in self.revisions)
2196
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
2197
# TODO: convert this logic, which is ~= snapshot to
2198
# a call to:. This needs the path figured out. rather than a work_tree
2199
# a v4 revision_tree can be given, or something that looks enough like
2200
# one to give the file content to the entry if it needs it.
2201
# and we need something that looks like a weave store for snapshot to
2203
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
2204
if len(previous_revisions) == 1:
2205
previous_ie = previous_revisions.values()[0]
2206
if ie._unchanged(previous_ie):
2207
ie.revision = previous_ie.revision
2210
text = self.branch.repository._text_store.get(ie.text_id)
2211
file_lines = text.readlines()
2212
w.add_lines(rev_id, previous_revisions, file_lines)
2213
self.text_count += 1
2215
w.add_lines(rev_id, previous_revisions, [])
2216
ie.revision = rev_id
2218
def _make_order(self):
2219
"""Return a suitable order for importing revisions.
2221
The order must be such that an revision is imported after all
2222
its (present) parents.
2224
todo = set(self.revisions.keys())
2225
done = self.absent_revisions.copy()
2228
# scan through looking for a revision whose parents
2230
for rev_id in sorted(list(todo)):
2231
rev = self.revisions[rev_id]
2232
parent_ids = set(rev.parent_ids)
2233
if parent_ids.issubset(done):
2234
# can take this one now
2235
order.append(rev_id)
2241
class ConvertBzrDir5To6(Converter):
2242
"""Converts format 5 bzr dirs to format 6."""
2244
def convert(self, to_convert, pb):
2245
"""See Converter.convert()."""
2246
self.bzrdir = to_convert
2248
self.pb.note('starting upgrade from format 5 to 6')
2249
self._convert_to_prefixed()
2250
return BzrDir.open(self.bzrdir.root_transport.base)
2252
def _convert_to_prefixed(self):
2253
from bzrlib.store import TransportStore
2254
self.bzrdir.transport.delete('branch-format')
2255
for store_name in ["weaves", "revision-store"]:
2256
self.pb.note("adding prefixes to %s" % store_name)
2257
store_transport = self.bzrdir.transport.clone(store_name)
2258
store = TransportStore(store_transport, prefixed=True)
2259
for urlfilename in store_transport.list_dir('.'):
2260
filename = urlutils.unescape(urlfilename)
2261
if (filename.endswith(".weave") or
2262
filename.endswith(".gz") or
2263
filename.endswith(".sig")):
2264
file_id, suffix = os.path.splitext(filename)
2268
new_name = store._mapper.map((file_id,)) + suffix
2269
# FIXME keep track of the dirs made RBC 20060121
2271
store_transport.move(filename, new_name)
2272
except errors.NoSuchFile: # catches missing dirs strangely enough
2273
store_transport.mkdir(osutils.dirname(new_name))
2274
store_transport.move(filename, new_name)
2275
self.bzrdir.transport.put_bytes(
2277
BzrDirFormat6().get_format_string(),
2278
mode=self.bzrdir._get_file_mode())
2281
class ConvertBzrDir6ToMeta(Converter):
2282
"""Converts format 6 bzr dirs to metadirs."""
2284
def convert(self, to_convert, pb):
2285
"""See Converter.convert()."""
2286
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2287
from bzrlib.branch import BzrBranchFormat5
2288
self.bzrdir = to_convert
2291
self.total = 20 # the steps we know about
2292
self.garbage_inventories = []
2293
self.dir_mode = self.bzrdir._get_dir_mode()
2294
self.file_mode = self.bzrdir._get_file_mode()
2296
self.pb.note('starting upgrade from format 6 to metadir')
2297
self.bzrdir.transport.put_bytes(
2299
"Converting to format 6",
2300
mode=self.file_mode)
2301
# its faster to move specific files around than to open and use the apis...
2302
# first off, nuke ancestry.weave, it was never used.
2304
self.step('Removing ancestry.weave')
2305
self.bzrdir.transport.delete('ancestry.weave')
2306
except errors.NoSuchFile:
2308
# find out whats there
2309
self.step('Finding branch files')
2310
last_revision = self.bzrdir.open_branch().last_revision()
2311
bzrcontents = self.bzrdir.transport.list_dir('.')
2312
for name in bzrcontents:
2313
if name.startswith('basis-inventory.'):
2314
self.garbage_inventories.append(name)
2315
# create new directories for repository, working tree and branch
2316
repository_names = [('inventory.weave', True),
2317
('revision-store', True),
2319
self.step('Upgrading repository ')
2320
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2321
self.make_lock('repository')
2322
# we hard code the formats here because we are converting into
2323
# the meta format. The meta format upgrader can take this to a
2324
# future format within each component.
2325
self.put_format('repository', RepositoryFormat7())
2326
for entry in repository_names:
2327
self.move_entry('repository', entry)
2329
self.step('Upgrading branch ')
2330
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2331
self.make_lock('branch')
2332
self.put_format('branch', BzrBranchFormat5())
2333
branch_files = [('revision-history', True),
2334
('branch-name', True),
2336
for entry in branch_files:
2337
self.move_entry('branch', entry)
2339
checkout_files = [('pending-merges', True),
2340
('inventory', True),
2341
('stat-cache', False)]
2342
# If a mandatory checkout file is not present, the branch does not have
2343
# a functional checkout. Do not create a checkout in the converted
2345
for name, mandatory in checkout_files:
2346
if mandatory and name not in bzrcontents:
2347
has_checkout = False
2351
if not has_checkout:
2352
self.pb.note('No working tree.')
2353
# If some checkout files are there, we may as well get rid of them.
2354
for name, mandatory in checkout_files:
2355
if name in bzrcontents:
2356
self.bzrdir.transport.delete(name)
2358
from bzrlib.workingtree import WorkingTreeFormat3
2359
self.step('Upgrading working tree')
2360
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2361
self.make_lock('checkout')
2363
'checkout', WorkingTreeFormat3())
2364
self.bzrdir.transport.delete_multi(
2365
self.garbage_inventories, self.pb)
2366
for entry in checkout_files:
2367
self.move_entry('checkout', entry)
2368
if last_revision is not None:
2369
self.bzrdir.transport.put_bytes(
2370
'checkout/last-revision', last_revision)
2371
self.bzrdir.transport.put_bytes(
2373
BzrDirMetaFormat1().get_format_string(),
2374
mode=self.file_mode)
2375
return BzrDir.open(self.bzrdir.root_transport.base)
2377
def make_lock(self, name):
2378
"""Make a lock for the new control dir name."""
2379
self.step('Make %s lock' % name)
2380
ld = lockdir.LockDir(self.bzrdir.transport,
2382
file_modebits=self.file_mode,
2383
dir_modebits=self.dir_mode)
2386
def move_entry(self, new_dir, entry):
2387
"""Move then entry name into new_dir."""
2389
mandatory = entry[1]
2390
self.step('Moving %s' % name)
2392
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2393
except errors.NoSuchFile:
2397
def put_format(self, dirname, format):
2398
self.bzrdir.transport.put_bytes('%s/format' % dirname,
2399
format.get_format_string(),
2403
class ConvertMetaToMeta(Converter):
2404
"""Converts the components of metadirs."""
2406
def __init__(self, target_format):
2407
"""Create a metadir to metadir converter.
2409
:param target_format: The final metadir format that is desired.
2411
self.target_format = target_format
2413
def convert(self, to_convert, pb):
2414
"""See Converter.convert()."""
2415
self.bzrdir = to_convert
2419
self.step('checking repository format')
2421
repo = self.bzrdir.open_repository()
2422
except errors.NoRepositoryPresent:
2425
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2426
from bzrlib.repository import CopyConverter
2427
self.pb.note('starting repository conversion')
2428
converter = CopyConverter(self.target_format.repository_format)
2429
converter.convert(repo, pb)
2431
branch = self.bzrdir.open_branch()
2432
except errors.NotBranchError:
2435
# TODO: conversions of Branch and Tree should be done by
2436
# InterXFormat lookups
2437
# Avoid circular imports
2438
from bzrlib import branch as _mod_branch
2439
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2440
self.target_format.get_branch_format().__class__ is
2441
_mod_branch.BzrBranchFormat6):
2442
branch_converter = _mod_branch.Converter5to6()
2443
branch_converter.convert(branch)
2445
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2446
except (errors.NoWorkingTree, errors.NotLocalUrl):
2449
# TODO: conversions of Branch and Tree should be done by
2450
# InterXFormat lookups
2451
if (isinstance(tree, workingtree.WorkingTree3) and
2452
not isinstance(tree, workingtree_4.WorkingTree4) and
2453
isinstance(self.target_format.workingtree_format,
2454
workingtree_4.WorkingTreeFormat4)):
2455
workingtree_4.Converter3to4().convert(tree)
2459
# This is not in remote.py because it's small, and needs to be registered.
2460
# Putting it in remote.py creates a circular import problem.
2461
# we can make it a lazy object if the control formats is turned into something
2463
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2464
"""Format representing bzrdirs accessed via a smart server"""
2466
def get_format_description(self):
2467
return 'bzr remote bzrdir'
2470
def probe_transport(klass, transport):
2471
"""Return a RemoteBzrDirFormat object if it looks possible."""
2473
medium = transport.get_smart_medium()
2474
except (NotImplementedError, AttributeError,
2475
errors.TransportNotPossible, errors.NoSmartMedium,
2476
errors.SmartProtocolError):
2477
# no smart server, so not a branch for this format type.
2478
raise errors.NotBranchError(path=transport.base)
2480
# Decline to open it if the server doesn't support our required
2481
# version (3) so that the VFS-based transport will do it.
2482
if medium.should_probe():
2484
server_version = medium.protocol_version()
2485
except errors.SmartProtocolError:
2486
# Apparently there's no usable smart server there, even though
2487
# the medium supports the smart protocol.
2488
raise errors.NotBranchError(path=transport.base)
2489
if server_version != '2':
2490
raise errors.NotBranchError(path=transport.base)
2493
def initialize_on_transport(self, transport):
2495
# hand off the request to the smart server
2496
client_medium = transport.get_smart_medium()
2497
except errors.NoSmartMedium:
2498
# TODO: lookup the local format from a server hint.
2499
local_dir_format = BzrDirMetaFormat1()
2500
return local_dir_format.initialize_on_transport(transport)
2501
client = _SmartClient(client_medium)
2502
path = client.remote_path_from_transport(transport)
2503
response = client.call('BzrDirFormat.initialize', path)
2504
if response[0] != 'ok':
2505
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
2506
return remote.RemoteBzrDir(transport)
2508
def _open(self, transport):
2509
return remote.RemoteBzrDir(transport)
2511
def __eq__(self, other):
2512
if not isinstance(other, RemoteBzrDirFormat):
2514
return self.get_format_description() == other.get_format_description()
2517
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2520
class BzrDirFormatInfo(object):
2522
def __init__(self, native, deprecated, hidden, experimental):
2523
self.deprecated = deprecated
2524
self.native = native
2525
self.hidden = hidden
2526
self.experimental = experimental
2529
class BzrDirFormatRegistry(registry.Registry):
2530
"""Registry of user-selectable BzrDir subformats.
2532
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2533
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2537
"""Create a BzrDirFormatRegistry."""
2538
self._aliases = set()
2539
super(BzrDirFormatRegistry, self).__init__()
2542
"""Return a set of the format names which are aliases."""
2543
return frozenset(self._aliases)
2545
def register_metadir(self, key,
2546
repository_format, help, native=True, deprecated=False,
2552
"""Register a metadir subformat.
2554
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2555
by the Repository format.
2557
:param repository_format: The fully-qualified repository format class
2559
:param branch_format: Fully-qualified branch format class name as
2561
:param tree_format: Fully-qualified tree format class name as
2564
# This should be expanded to support setting WorkingTree and Branch
2565
# formats, once BzrDirMetaFormat1 supports that.
2566
def _load(full_name):
2567
mod_name, factory_name = full_name.rsplit('.', 1)
2569
mod = __import__(mod_name, globals(), locals(),
2571
except ImportError, e:
2572
raise ImportError('failed to load %s: %s' % (full_name, e))
2574
factory = getattr(mod, factory_name)
2575
except AttributeError:
2576
raise AttributeError('no factory %s in module %r'
2581
bd = BzrDirMetaFormat1()
2582
if branch_format is not None:
2583
bd.set_branch_format(_load(branch_format))
2584
if tree_format is not None:
2585
bd.workingtree_format = _load(tree_format)
2586
if repository_format is not None:
2587
bd.repository_format = _load(repository_format)
2589
self.register(key, helper, help, native, deprecated, hidden,
2590
experimental, alias)
2592
def register(self, key, factory, help, native=True, deprecated=False,
2593
hidden=False, experimental=False, alias=False):
2594
"""Register a BzrDirFormat factory.
2596
The factory must be a callable that takes one parameter: the key.
2597
It must produce an instance of the BzrDirFormat when called.
2599
This function mainly exists to prevent the info object from being
2602
registry.Registry.register(self, key, factory, help,
2603
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2605
self._aliases.add(key)
2607
def register_lazy(self, key, module_name, member_name, help, native=True,
2608
deprecated=False, hidden=False, experimental=False, alias=False):
2609
registry.Registry.register_lazy(self, key, module_name, member_name,
2610
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2612
self._aliases.add(key)
2614
def set_default(self, key):
2615
"""Set the 'default' key to be a clone of the supplied key.
2617
This method must be called once and only once.
2619
registry.Registry.register(self, 'default', self.get(key),
2620
self.get_help(key), info=self.get_info(key))
2621
self._aliases.add('default')
2623
def set_default_repository(self, key):
2624
"""Set the FormatRegistry default and Repository default.
2626
This is a transitional method while Repository.set_default_format
2629
if 'default' in self:
2630
self.remove('default')
2631
self.set_default(key)
2632
format = self.get('default')()
2634
def make_bzrdir(self, key):
2635
return self.get(key)()
2637
def help_topic(self, topic):
2638
output = textwrap.dedent("""\
2639
These formats can be used for creating branches, working trees, and
2643
default_realkey = None
2644
default_help = self.get_help('default')
2646
for key in self.keys():
2647
if key == 'default':
2649
help = self.get_help(key)
2650
if help == default_help:
2651
default_realkey = key
2653
help_pairs.append((key, help))
2655
def wrapped(key, help, info):
2657
help = '(native) ' + help
2658
return ':%s:\n%s\n\n' % (key,
2659
textwrap.fill(help, initial_indent=' ',
2660
subsequent_indent=' '))
2661
if default_realkey is not None:
2662
output += wrapped(default_realkey, '(default) %s' % default_help,
2663
self.get_info('default'))
2664
deprecated_pairs = []
2665
experimental_pairs = []
2666
for key, help in help_pairs:
2667
info = self.get_info(key)
2670
elif info.deprecated:
2671
deprecated_pairs.append((key, help))
2672
elif info.experimental:
2673
experimental_pairs.append((key, help))
2675
output += wrapped(key, help, info)
2676
if len(experimental_pairs) > 0:
2677
output += "Experimental formats are shown below.\n\n"
2678
for key, help in experimental_pairs:
2679
info = self.get_info(key)
2680
output += wrapped(key, help, info)
2681
if len(deprecated_pairs) > 0:
2682
output += "Deprecated formats are shown below.\n\n"
2683
for key, help in deprecated_pairs:
2684
info = self.get_info(key)
2685
output += wrapped(key, help, info)
2690
class RepositoryAcquisitionPolicy(object):
2691
"""Abstract base class for repository acquisition policies.
2693
A repository acquisition policy decides how a BzrDir acquires a repository
2694
for a branch that is being created. The most basic policy decision is
2695
whether to create a new repository or use an existing one.
2698
def configure_branch(self, branch):
2699
"""Apply any configuration data from this policy to the branch.
2701
Default implementation does nothing.
2705
def acquire_repository(self, make_working_trees=None, shared=False):
2706
"""Acquire a repository for this bzrdir.
2708
Implementations may create a new repository or use a pre-exising
2710
:param make_working_trees: If creating a repository, set
2711
make_working_trees to this value (if non-None)
2712
:param shared: If creating a repository, make it shared if True
2713
:return: A repository
2715
raise NotImplemented(RepositoryAcquisitionPolicy.acquire_repository)
2718
class CreateRepository(RepositoryAcquisitionPolicy):
2719
"""A policy of creating a new repository"""
2721
def __init__(self, bzrdir):
2722
RepositoryAcquisitionPolicy.__init__(self)
2723
self._bzrdir = bzrdir
2725
def acquire_repository(self, make_working_trees=None, shared=False):
2726
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
2728
Creates the desired repository in the bzrdir we already have.
2730
repository = self._bzrdir.create_repository(shared=shared)
2731
if make_working_trees is not None:
2732
repository.set_make_working_trees(make_working_trees)
2736
class UseExistingRepository(RepositoryAcquisitionPolicy):
2737
"""A policy of reusing an existing repository"""
2739
def __init__(self, repository):
2740
RepositoryAcquisitionPolicy.__init__(self)
2741
self._repository = repository
2743
def acquire_repository(self, make_working_trees=None, shared=False):
2744
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
2746
Returns an existing repository to use
2748
return self._repository
2751
format_registry = BzrDirFormatRegistry()
2752
format_registry.register('weave', BzrDirFormat6,
2753
'Pre-0.8 format. Slower than knit and does not'
2754
' support checkouts or shared repositories.',
2756
format_registry.register_metadir('knit',
2757
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2758
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2759
branch_format='bzrlib.branch.BzrBranchFormat5',
2760
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2761
format_registry.register_metadir('metaweave',
2762
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2763
'Transitional format in 0.8. Slower than knit.',
2764
branch_format='bzrlib.branch.BzrBranchFormat5',
2765
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2767
format_registry.register_metadir('dirstate',
2768
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2769
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2770
'above when accessed over the network.',
2771
branch_format='bzrlib.branch.BzrBranchFormat5',
2772
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2773
# directly from workingtree_4 triggers a circular import.
2774
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2776
format_registry.register_metadir('dirstate-tags',
2777
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2778
help='New in 0.15: Fast local operations and improved scaling for '
2779
'network operations. Additionally adds support for tags.'
2780
' Incompatible with bzr < 0.15.',
2781
branch_format='bzrlib.branch.BzrBranchFormat6',
2782
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2784
format_registry.register_metadir('rich-root',
2785
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
2786
help='New in 1.0. Better handling of tree roots. Incompatible with'
2788
branch_format='bzrlib.branch.BzrBranchFormat6',
2789
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2791
format_registry.register_metadir('dirstate-with-subtree',
2792
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2793
help='New in 0.15: Fast local operations and improved scaling for '
2794
'network operations. Additionally adds support for versioning nested '
2795
'bzr branches. Incompatible with bzr < 0.15.',
2796
branch_format='bzrlib.branch.BzrBranchFormat6',
2797
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2801
format_registry.register_metadir('pack-0.92',
2802
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2803
help='New in 0.92: Pack-based format with data compatible with '
2804
'dirstate-tags format repositories. Interoperates with '
2805
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2806
'Previously called knitpack-experimental. '
2807
'For more information, see '
2808
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2809
branch_format='bzrlib.branch.BzrBranchFormat6',
2810
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2812
format_registry.register_metadir('pack-0.92-subtree',
2813
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2814
help='New in 0.92: Pack-based format with data compatible with '
2815
'dirstate-with-subtree format repositories. Interoperates with '
2816
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2817
'Previously called knitpack-experimental. '
2818
'For more information, see '
2819
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2820
branch_format='bzrlib.branch.BzrBranchFormat6',
2821
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2825
format_registry.register_metadir('rich-root-pack',
2826
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
2827
help='New in 1.0: Pack-based format with data compatible with '
2828
'rich-root format repositories. Incompatible with'
2830
branch_format='bzrlib.branch.BzrBranchFormat6',
2831
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2833
# The following two formats should always just be aliases.
2834
format_registry.register_metadir('development',
2835
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
2836
help='Current development format. Can convert data to and from pack-0.92 '
2837
'(and anything compatible with pack-0.92) format repositories. '
2838
'Repositories in this format can only be read by bzr.dev. '
2840
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2842
branch_format='bzrlib.branch.BzrBranchFormat6',
2843
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2847
format_registry.register_metadir('development-subtree',
2848
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
2849
help='Current development format, subtree variant. Can convert data to and '
2850
'from pack-0.92 (and anything compatible with pack-0.92) format '
2851
'repositories. Repositories in this format can only be read by '
2852
'bzr.dev. Please read '
2853
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2855
branch_format='bzrlib.branch.BzrBranchFormat6',
2856
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2860
# And the development formats which the will have aliased one of follow:
2861
format_registry.register_metadir('development0',
2862
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
2863
help='Trivial rename of pack-0.92 to provide a development format. '
2865
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2867
branch_format='bzrlib.branch.BzrBranchFormat6',
2868
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2872
format_registry.register_metadir('development0-subtree',
2873
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
2874
help='Trivial rename of pack-0.92-subtree to provide a development format. '
2876
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2878
branch_format='bzrlib.branch.BzrBranchFormat6',
2879
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2883
format_registry.set_default('pack-0.92')