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 NotVersionedError is
863
bzrdir, relpath = klass.open_containing(location)
865
tree, branch = bzrdir._get_tree_branch()
866
except errors.NotBranchError:
868
repo = klass.open_containing(location)[0].find_repository()
869
return None, None, repo, relpath
870
except (errors.NoRepositoryPresent):
871
raise errors.NotBranchError(location)
872
return tree, branch, branch.repository, relpath
874
def open_repository(self, _unsupported=False):
875
"""Open the repository object at this BzrDir if one is present.
877
This will not follow the Branch object pointer - it's strictly a direct
878
open facility. Most client code should use open_branch().repository to
881
:param _unsupported: a private parameter, not part of the api.
882
TODO: static convenience version of this?
884
raise NotImplementedError(self.open_repository)
886
def open_workingtree(self, _unsupported=False,
887
recommend_upgrade=True, from_branch=None):
888
"""Open the workingtree object at this BzrDir if one is present.
890
:param recommend_upgrade: Optional keyword parameter, when True (the
891
default), emit through the ui module a recommendation that the user
892
upgrade the working tree when the workingtree being opened is old
893
(but still fully supported).
894
:param from_branch: override bzrdir branch (for lightweight checkouts)
896
raise NotImplementedError(self.open_workingtree)
898
def has_branch(self):
899
"""Tell if this bzrdir contains a branch.
901
Note: if you're going to open the branch, you should just go ahead
902
and try, and not ask permission first. (This method just opens the
903
branch and discards it, and that's somewhat expensive.)
908
except errors.NotBranchError:
911
def has_workingtree(self):
912
"""Tell if this bzrdir contains a working tree.
914
This will still raise an exception if the bzrdir has a workingtree that
915
is remote & inaccessible.
917
Note: if you're going to open the working tree, you should just go ahead
918
and try, and not ask permission first. (This method just opens the
919
workingtree and discards it, and that's somewhat expensive.)
922
self.open_workingtree(recommend_upgrade=False)
924
except errors.NoWorkingTree:
927
def _cloning_metadir(self):
928
"""Produce a metadir suitable for cloning with."""
929
result_format = self._format.__class__()
932
branch = self.open_branch()
933
source_repository = branch.repository
934
except errors.NotBranchError:
936
source_repository = self.open_repository()
937
except errors.NoRepositoryPresent:
938
source_repository = None
940
# XXX TODO: This isinstance is here because we have not implemented
941
# the fix recommended in bug # 103195 - to delegate this choice the
943
repo_format = source_repository._format
944
if not isinstance(repo_format, remote.RemoteRepositoryFormat):
945
result_format.repository_format = repo_format
947
# TODO: Couldn't we just probe for the format in these cases,
948
# rather than opening the whole tree? It would be a little
949
# faster. mbp 20070401
950
tree = self.open_workingtree(recommend_upgrade=False)
951
except (errors.NoWorkingTree, errors.NotLocalUrl):
952
result_format.workingtree_format = None
954
result_format.workingtree_format = tree._format.__class__()
955
return result_format, source_repository
957
def cloning_metadir(self):
958
"""Produce a metadir suitable for cloning or sprouting with.
960
These operations may produce workingtrees (yes, even though they're
961
"cloning" something that doesn't have a tree), so a viable workingtree
962
format must be selected.
964
format, repository = self._cloning_metadir()
965
if format._workingtree_format is None:
966
if repository is None:
968
tree_format = repository._format._matchingbzrdir.workingtree_format
969
format.workingtree_format = tree_format.__class__()
972
def checkout_metadir(self):
973
return self.cloning_metadir()
975
def sprout(self, url, revision_id=None, force_new_repo=False,
976
recurse='down', possible_transports=None,
977
accelerator_tree=None, hardlink=False):
978
"""Create a copy of this bzrdir prepared for use as a new line of
981
If url's last component does not exist, it will be created.
983
Attributes related to the identity of the source branch like
984
branch nickname will be cleaned, a working tree is created
985
whether one existed before or not; and a local branch is always
988
if revision_id is not None, then the clone operation may tune
989
itself to download less data.
990
:param accelerator_tree: A tree which can be used for retrieving file
991
contents more quickly than the revision tree, i.e. a workingtree.
992
The revision tree will be used for cases where accelerator_tree's
993
content is different.
994
:param hardlink: If true, hard-link files from accelerator_tree,
997
target_transport = get_transport(url, possible_transports)
998
target_transport.ensure_base()
999
cloning_format = self.cloning_metadir()
1000
result = cloning_format.initialize_on_transport(target_transport)
1002
source_branch = self.open_branch()
1003
source_repository = source_branch.repository
1004
except errors.NotBranchError:
1005
source_branch = None
1007
source_repository = self.open_repository()
1008
except errors.NoRepositoryPresent:
1009
source_repository = None
1014
result_repo = result.find_repository()
1015
except errors.NoRepositoryPresent:
1017
if source_repository is None and result_repo is not None:
1019
elif source_repository is None and result_repo is None:
1020
# no repo available, make a new one
1021
result.create_repository()
1022
elif source_repository is not None and result_repo is None:
1023
# have source, and want to make a new target repo
1024
result_repo = source_repository.sprout(result,
1025
revision_id=revision_id)
1027
# fetch needed content into target.
1028
if source_repository is not None:
1030
# source_repository.copy_content_into(result_repo,
1031
# revision_id=revision_id)
1032
# so we can override the copy method
1033
result_repo.fetch(source_repository, revision_id=revision_id)
1034
if source_branch is not None:
1035
source_branch.sprout(result, revision_id=revision_id)
1037
result.create_branch()
1038
if isinstance(target_transport, LocalTransport) and (
1039
result_repo is None or result_repo.make_working_trees()):
1040
wt = result.create_workingtree(accelerator_tree=accelerator_tree,
1044
if wt.path2id('') is None:
1046
wt.set_root_id(self.open_workingtree.get_root_id())
1047
except errors.NoWorkingTree:
1053
if recurse == 'down':
1055
basis = wt.basis_tree()
1057
subtrees = basis.iter_references()
1058
recurse_branch = wt.branch
1059
elif source_branch is not None:
1060
basis = source_branch.basis_tree()
1062
subtrees = basis.iter_references()
1063
recurse_branch = source_branch
1068
for path, file_id in subtrees:
1069
target = urlutils.join(url, urlutils.escape(path))
1070
sublocation = source_branch.reference_parent(file_id, path)
1071
sublocation.bzrdir.sprout(target,
1072
basis.get_reference_revision(file_id, path),
1073
force_new_repo=force_new_repo, recurse=recurse)
1075
if basis is not None:
1080
class BzrDirPreSplitOut(BzrDir):
1081
"""A common class for the all-in-one formats."""
1083
def __init__(self, _transport, _format):
1084
"""See BzrDir.__init__."""
1085
super(BzrDirPreSplitOut, self).__init__(_transport, _format)
1086
self._control_files = lockable_files.LockableFiles(
1087
self.get_branch_transport(None),
1088
self._format._lock_file_name,
1089
self._format._lock_class)
1091
def break_lock(self):
1092
"""Pre-splitout bzrdirs do not suffer from stale locks."""
1093
raise NotImplementedError(self.break_lock)
1095
def cloning_metadir(self):
1096
"""Produce a metadir suitable for cloning with."""
1097
return self._format.__class__()
1099
def clone(self, url, revision_id=None, force_new_repo=False):
1100
"""See BzrDir.clone()."""
1101
from bzrlib.workingtree import WorkingTreeFormat2
1102
self._make_tail(url)
1103
result = self._format._initialize_for_clone(url)
1104
self.open_repository().clone(result, revision_id=revision_id)
1105
from_branch = self.open_branch()
1106
from_branch.clone(result, revision_id=revision_id)
1108
self.open_workingtree().clone(result)
1109
except errors.NotLocalUrl:
1110
# make a new one, this format always has to have one.
1112
WorkingTreeFormat2().initialize(result)
1113
except errors.NotLocalUrl:
1114
# but we cannot do it for remote trees.
1115
to_branch = result.open_branch()
1116
WorkingTreeFormat2()._stub_initialize_remote(to_branch)
1119
def create_branch(self):
1120
"""See BzrDir.create_branch."""
1121
return self.open_branch()
1123
def destroy_branch(self):
1124
"""See BzrDir.destroy_branch."""
1125
raise errors.UnsupportedOperation(self.destroy_branch, self)
1127
def create_repository(self, shared=False):
1128
"""See BzrDir.create_repository."""
1130
raise errors.IncompatibleFormat('shared repository', self._format)
1131
return self.open_repository()
1133
def destroy_repository(self):
1134
"""See BzrDir.destroy_repository."""
1135
raise errors.UnsupportedOperation(self.destroy_repository, self)
1137
def create_workingtree(self, revision_id=None, from_branch=None,
1138
accelerator_tree=None, hardlink=False):
1139
"""See BzrDir.create_workingtree."""
1140
# this looks buggy but is not -really-
1141
# because this format creates the workingtree when the bzrdir is
1143
# clone and sprout will have set the revision_id
1144
# and that will have set it for us, its only
1145
# specific uses of create_workingtree in isolation
1146
# that can do wonky stuff here, and that only
1147
# happens for creating checkouts, which cannot be
1148
# done on this format anyway. So - acceptable wart.
1149
result = self.open_workingtree(recommend_upgrade=False)
1150
if revision_id is not None:
1151
if revision_id == _mod_revision.NULL_REVISION:
1152
result.set_parent_ids([])
1154
result.set_parent_ids([revision_id])
1157
def destroy_workingtree(self):
1158
"""See BzrDir.destroy_workingtree."""
1159
raise errors.UnsupportedOperation(self.destroy_workingtree, self)
1161
def destroy_workingtree_metadata(self):
1162
"""See BzrDir.destroy_workingtree_metadata."""
1163
raise errors.UnsupportedOperation(self.destroy_workingtree_metadata,
1166
def get_branch_transport(self, branch_format):
1167
"""See BzrDir.get_branch_transport()."""
1168
if branch_format is None:
1169
return self.transport
1171
branch_format.get_format_string()
1172
except NotImplementedError:
1173
return self.transport
1174
raise errors.IncompatibleFormat(branch_format, self._format)
1176
def get_repository_transport(self, repository_format):
1177
"""See BzrDir.get_repository_transport()."""
1178
if repository_format is None:
1179
return self.transport
1181
repository_format.get_format_string()
1182
except NotImplementedError:
1183
return self.transport
1184
raise errors.IncompatibleFormat(repository_format, self._format)
1186
def get_workingtree_transport(self, workingtree_format):
1187
"""See BzrDir.get_workingtree_transport()."""
1188
if workingtree_format is None:
1189
return self.transport
1191
workingtree_format.get_format_string()
1192
except NotImplementedError:
1193
return self.transport
1194
raise errors.IncompatibleFormat(workingtree_format, self._format)
1196
def needs_format_conversion(self, format=None):
1197
"""See BzrDir.needs_format_conversion()."""
1198
# if the format is not the same as the system default,
1199
# an upgrade is needed.
1201
format = BzrDirFormat.get_default_format()
1202
return not isinstance(self._format, format.__class__)
1204
def open_branch(self, unsupported=False):
1205
"""See BzrDir.open_branch."""
1206
from bzrlib.branch import BzrBranchFormat4
1207
format = BzrBranchFormat4()
1208
self._check_supported(format, unsupported)
1209
return format.open(self, _found=True)
1211
def sprout(self, url, revision_id=None, force_new_repo=False,
1212
possible_transports=None, accelerator_tree=None,
1214
"""See BzrDir.sprout()."""
1215
from bzrlib.workingtree import WorkingTreeFormat2
1216
self._make_tail(url)
1217
result = self._format._initialize_for_clone(url)
1219
self.open_repository().clone(result, revision_id=revision_id)
1220
except errors.NoRepositoryPresent:
1223
self.open_branch().sprout(result, revision_id=revision_id)
1224
except errors.NotBranchError:
1226
# we always want a working tree
1227
WorkingTreeFormat2().initialize(result,
1228
accelerator_tree=accelerator_tree,
1233
class BzrDir4(BzrDirPreSplitOut):
1234
"""A .bzr version 4 control object.
1236
This is a deprecated format and may be removed after sept 2006.
1239
def create_repository(self, shared=False):
1240
"""See BzrDir.create_repository."""
1241
return self._format.repository_format.initialize(self, shared)
1243
def needs_format_conversion(self, format=None):
1244
"""Format 4 dirs are always in need of conversion."""
1247
def open_repository(self):
1248
"""See BzrDir.open_repository."""
1249
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1250
return RepositoryFormat4().open(self, _found=True)
1253
class BzrDir5(BzrDirPreSplitOut):
1254
"""A .bzr version 5 control object.
1256
This is a deprecated format and may be removed after sept 2006.
1259
def open_repository(self):
1260
"""See BzrDir.open_repository."""
1261
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1262
return RepositoryFormat5().open(self, _found=True)
1264
def open_workingtree(self, _unsupported=False,
1265
recommend_upgrade=True):
1266
"""See BzrDir.create_workingtree."""
1267
from bzrlib.workingtree import WorkingTreeFormat2
1268
wt_format = WorkingTreeFormat2()
1269
# we don't warn here about upgrades; that ought to be handled for the
1271
return wt_format.open(self, _found=True)
1274
class BzrDir6(BzrDirPreSplitOut):
1275
"""A .bzr version 6 control object.
1277
This is a deprecated format and may be removed after sept 2006.
1280
def open_repository(self):
1281
"""See BzrDir.open_repository."""
1282
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1283
return RepositoryFormat6().open(self, _found=True)
1285
def open_workingtree(self, _unsupported=False,
1286
recommend_upgrade=True):
1287
"""See BzrDir.create_workingtree."""
1288
# we don't warn here about upgrades; that ought to be handled for the
1290
from bzrlib.workingtree import WorkingTreeFormat2
1291
return WorkingTreeFormat2().open(self, _found=True)
1294
class BzrDirMeta1(BzrDir):
1295
"""A .bzr meta version 1 control object.
1297
This is the first control object where the
1298
individual aspects are really split out: there are separate repository,
1299
workingtree and branch subdirectories and any subset of the three can be
1300
present within a BzrDir.
1303
def can_convert_format(self):
1304
"""See BzrDir.can_convert_format()."""
1307
def create_branch(self):
1308
"""See BzrDir.create_branch."""
1309
return self._format.get_branch_format().initialize(self)
1311
def destroy_branch(self):
1312
"""See BzrDir.create_branch."""
1313
self.transport.delete_tree('branch')
1315
def create_repository(self, shared=False):
1316
"""See BzrDir.create_repository."""
1317
return self._format.repository_format.initialize(self, shared)
1319
def destroy_repository(self):
1320
"""See BzrDir.destroy_repository."""
1321
self.transport.delete_tree('repository')
1323
def create_workingtree(self, revision_id=None, from_branch=None,
1324
accelerator_tree=None, hardlink=False):
1325
"""See BzrDir.create_workingtree."""
1326
return self._format.workingtree_format.initialize(
1327
self, revision_id, from_branch=from_branch,
1328
accelerator_tree=accelerator_tree, hardlink=hardlink)
1330
def destroy_workingtree(self):
1331
"""See BzrDir.destroy_workingtree."""
1332
wt = self.open_workingtree(recommend_upgrade=False)
1333
repository = wt.branch.repository
1334
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
1335
wt.revert(old_tree=empty)
1336
self.destroy_workingtree_metadata()
1338
def destroy_workingtree_metadata(self):
1339
self.transport.delete_tree('checkout')
1341
def find_branch_format(self):
1342
"""Find the branch 'format' for this bzrdir.
1344
This might be a synthetic object for e.g. RemoteBranch and SVN.
1346
from bzrlib.branch import BranchFormat
1347
return BranchFormat.find_format(self)
1349
def _get_mkdir_mode(self):
1350
"""Figure out the mode to use when creating a bzrdir subdir."""
1351
temp_control = lockable_files.LockableFiles(self.transport, '',
1352
lockable_files.TransportLock)
1353
return temp_control._dir_mode
1355
def get_branch_reference(self):
1356
"""See BzrDir.get_branch_reference()."""
1357
from bzrlib.branch import BranchFormat
1358
format = BranchFormat.find_format(self)
1359
return format.get_reference(self)
1361
def get_branch_transport(self, branch_format):
1362
"""See BzrDir.get_branch_transport()."""
1363
if branch_format is None:
1364
return self.transport.clone('branch')
1366
branch_format.get_format_string()
1367
except NotImplementedError:
1368
raise errors.IncompatibleFormat(branch_format, self._format)
1370
self.transport.mkdir('branch', mode=self._get_mkdir_mode())
1371
except errors.FileExists:
1373
return self.transport.clone('branch')
1375
def get_repository_transport(self, repository_format):
1376
"""See BzrDir.get_repository_transport()."""
1377
if repository_format is None:
1378
return self.transport.clone('repository')
1380
repository_format.get_format_string()
1381
except NotImplementedError:
1382
raise errors.IncompatibleFormat(repository_format, self._format)
1384
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1385
except errors.FileExists:
1387
return self.transport.clone('repository')
1389
def get_workingtree_transport(self, workingtree_format):
1390
"""See BzrDir.get_workingtree_transport()."""
1391
if workingtree_format is None:
1392
return self.transport.clone('checkout')
1394
workingtree_format.get_format_string()
1395
except NotImplementedError:
1396
raise errors.IncompatibleFormat(workingtree_format, self._format)
1398
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1399
except errors.FileExists:
1401
return self.transport.clone('checkout')
1403
def needs_format_conversion(self, format=None):
1404
"""See BzrDir.needs_format_conversion()."""
1406
format = BzrDirFormat.get_default_format()
1407
if not isinstance(self._format, format.__class__):
1408
# it is not a meta dir format, conversion is needed.
1410
# we might want to push this down to the repository?
1412
if not isinstance(self.open_repository()._format,
1413
format.repository_format.__class__):
1414
# the repository needs an upgrade.
1416
except errors.NoRepositoryPresent:
1419
if not isinstance(self.open_branch()._format,
1420
format.get_branch_format().__class__):
1421
# the branch needs an upgrade.
1423
except errors.NotBranchError:
1426
my_wt = self.open_workingtree(recommend_upgrade=False)
1427
if not isinstance(my_wt._format,
1428
format.workingtree_format.__class__):
1429
# the workingtree needs an upgrade.
1431
except (errors.NoWorkingTree, errors.NotLocalUrl):
1435
def open_branch(self, unsupported=False):
1436
"""See BzrDir.open_branch."""
1437
format = self.find_branch_format()
1438
self._check_supported(format, unsupported)
1439
return format.open(self, _found=True)
1441
def open_repository(self, unsupported=False):
1442
"""See BzrDir.open_repository."""
1443
from bzrlib.repository import RepositoryFormat
1444
format = RepositoryFormat.find_format(self)
1445
self._check_supported(format, unsupported)
1446
return format.open(self, _found=True)
1448
def open_workingtree(self, unsupported=False,
1449
recommend_upgrade=True):
1450
"""See BzrDir.open_workingtree."""
1451
from bzrlib.workingtree import WorkingTreeFormat
1452
format = WorkingTreeFormat.find_format(self)
1453
self._check_supported(format, unsupported,
1455
basedir=self.root_transport.base)
1456
return format.open(self, _found=True)
1459
class BzrDirFormat(object):
1460
"""An encapsulation of the initialization and open routines for a format.
1462
Formats provide three things:
1463
* An initialization routine,
1467
Formats are placed in a dict by their format string for reference
1468
during bzrdir opening. These should be subclasses of BzrDirFormat
1471
Once a format is deprecated, just deprecate the initialize and open
1472
methods on the format class. Do not deprecate the object, as the
1473
object will be created every system load.
1476
_default_format = None
1477
"""The default format used for new .bzr dirs."""
1480
"""The known formats."""
1482
_control_formats = []
1483
"""The registered control formats - .bzr, ....
1485
This is a list of BzrDirFormat objects.
1488
_control_server_formats = []
1489
"""The registered control server formats, e.g. RemoteBzrDirs.
1491
This is a list of BzrDirFormat objects.
1494
_lock_file_name = 'branch-lock'
1496
# _lock_class must be set in subclasses to the lock type, typ.
1497
# TransportLock or LockDir
1500
def find_format(klass, transport, _server_formats=True):
1501
"""Return the format present at transport."""
1503
formats = klass._control_server_formats + klass._control_formats
1505
formats = klass._control_formats
1506
for format in formats:
1508
return format.probe_transport(transport)
1509
except errors.NotBranchError:
1510
# this format does not find a control dir here.
1512
raise errors.NotBranchError(path=transport.base)
1515
def probe_transport(klass, transport):
1516
"""Return the .bzrdir style format present in a directory."""
1518
format_string = transport.get(".bzr/branch-format").read()
1519
except errors.NoSuchFile:
1520
raise errors.NotBranchError(path=transport.base)
1523
return klass._formats[format_string]
1525
raise errors.UnknownFormatError(format=format_string, kind='bzrdir')
1528
def get_default_format(klass):
1529
"""Return the current default format."""
1530
return klass._default_format
1532
def get_format_string(self):
1533
"""Return the ASCII format string that identifies this format."""
1534
raise NotImplementedError(self.get_format_string)
1536
def get_format_description(self):
1537
"""Return the short description for this format."""
1538
raise NotImplementedError(self.get_format_description)
1540
def get_converter(self, format=None):
1541
"""Return the converter to use to convert bzrdirs needing converts.
1543
This returns a bzrlib.bzrdir.Converter object.
1545
This should return the best upgrader to step this format towards the
1546
current default format. In the case of plugins we can/should provide
1547
some means for them to extend the range of returnable converters.
1549
:param format: Optional format to override the default format of the
1552
raise NotImplementedError(self.get_converter)
1554
def initialize(self, url, possible_transports=None):
1555
"""Create a bzr control dir at this url and return an opened copy.
1557
Subclasses should typically override initialize_on_transport
1558
instead of this method.
1560
return self.initialize_on_transport(get_transport(url,
1561
possible_transports))
1563
def initialize_on_transport(self, transport):
1564
"""Initialize a new bzrdir in the base directory of a Transport."""
1565
# Since we don't have a .bzr directory, inherit the
1566
# mode from the root directory
1567
temp_control = lockable_files.LockableFiles(transport,
1568
'', lockable_files.TransportLock)
1569
temp_control._transport.mkdir('.bzr',
1570
# FIXME: RBC 20060121 don't peek under
1572
mode=temp_control._dir_mode)
1573
if sys.platform == 'win32' and isinstance(transport, LocalTransport):
1574
win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1575
file_mode = temp_control._file_mode
1577
bzrdir_transport = transport.clone('.bzr')
1578
utf8_files = [('README',
1579
"This is a Bazaar control directory.\n"
1580
"Do not change any files in this directory.\n"
1581
"See http://bazaar-vcs.org/ for more information about Bazaar.\n"),
1582
('branch-format', self.get_format_string()),
1584
# NB: no need to escape relative paths that are url safe.
1585
control_files = lockable_files.LockableFiles(bzrdir_transport,
1586
self._lock_file_name, self._lock_class)
1587
control_files.create_lock()
1588
control_files.lock_write()
1590
for (filename, content) in utf8_files:
1591
bzrdir_transport.put_bytes(filename, content,
1594
control_files.unlock()
1595
return self.open(transport, _found=True)
1597
def is_supported(self):
1598
"""Is this format supported?
1600
Supported formats must be initializable and openable.
1601
Unsupported formats may not support initialization or committing or
1602
some other features depending on the reason for not being supported.
1606
def same_model(self, target_format):
1607
return (self.repository_format.rich_root_data ==
1608
target_format.rich_root_data)
1611
def known_formats(klass):
1612
"""Return all the known formats.
1614
Concrete formats should override _known_formats.
1616
# There is double indirection here to make sure that control
1617
# formats used by more than one dir format will only be probed
1618
# once. This can otherwise be quite expensive for remote connections.
1620
for format in klass._control_formats:
1621
result.update(format._known_formats())
1625
def _known_formats(klass):
1626
"""Return the known format instances for this control format."""
1627
return set(klass._formats.values())
1629
def open(self, transport, _found=False):
1630
"""Return an instance of this format for the dir transport points at.
1632
_found is a private parameter, do not use it.
1635
found_format = BzrDirFormat.find_format(transport)
1636
if not isinstance(found_format, self.__class__):
1637
raise AssertionError("%s was asked to open %s, but it seems to need "
1639
% (self, transport, found_format))
1640
return self._open(transport)
1642
def _open(self, transport):
1643
"""Template method helper for opening BzrDirectories.
1645
This performs the actual open and any additional logic or parameter
1648
raise NotImplementedError(self._open)
1651
def register_format(klass, format):
1652
klass._formats[format.get_format_string()] = format
1655
def register_control_format(klass, format):
1656
"""Register a format that does not use '.bzr' for its control dir.
1658
TODO: This should be pulled up into a 'ControlDirFormat' base class
1659
which BzrDirFormat can inherit from, and renamed to register_format
1660
there. It has been done without that for now for simplicity of
1663
klass._control_formats.append(format)
1666
def register_control_server_format(klass, format):
1667
"""Register a control format for client-server environments.
1669
These formats will be tried before ones registered with
1670
register_control_format. This gives implementations that decide to the
1671
chance to grab it before anything looks at the contents of the format
1674
klass._control_server_formats.append(format)
1677
def _set_default_format(klass, format):
1678
"""Set default format (for testing behavior of defaults only)"""
1679
klass._default_format = format
1683
return self.get_format_string().rstrip()
1686
def unregister_format(klass, format):
1687
del klass._formats[format.get_format_string()]
1690
def unregister_control_format(klass, format):
1691
klass._control_formats.remove(format)
1694
class BzrDirFormat4(BzrDirFormat):
1695
"""Bzr dir format 4.
1697
This format is a combined format for working tree, branch and repository.
1699
- Format 1 working trees [always]
1700
- Format 4 branches [always]
1701
- Format 4 repositories [always]
1703
This format is deprecated: it indexes texts using a text it which is
1704
removed in format 5; write support for this format has been removed.
1707
_lock_class = lockable_files.TransportLock
1709
def get_format_string(self):
1710
"""See BzrDirFormat.get_format_string()."""
1711
return "Bazaar-NG branch, format 0.0.4\n"
1713
def get_format_description(self):
1714
"""See BzrDirFormat.get_format_description()."""
1715
return "All-in-one format 4"
1717
def get_converter(self, format=None):
1718
"""See BzrDirFormat.get_converter()."""
1719
# there is one and only one upgrade path here.
1720
return ConvertBzrDir4To5()
1722
def initialize_on_transport(self, transport):
1723
"""Format 4 branches cannot be created."""
1724
raise errors.UninitializableFormat(self)
1726
def is_supported(self):
1727
"""Format 4 is not supported.
1729
It is not supported because the model changed from 4 to 5 and the
1730
conversion logic is expensive - so doing it on the fly was not
1735
def _open(self, transport):
1736
"""See BzrDirFormat._open."""
1737
return BzrDir4(transport, self)
1739
def __return_repository_format(self):
1740
"""Circular import protection."""
1741
from bzrlib.repofmt.weaverepo import RepositoryFormat4
1742
return RepositoryFormat4()
1743
repository_format = property(__return_repository_format)
1746
class BzrDirFormat5(BzrDirFormat):
1747
"""Bzr control format 5.
1749
This format is a combined format for working tree, branch and repository.
1751
- Format 2 working trees [always]
1752
- Format 4 branches [always]
1753
- Format 5 repositories [always]
1754
Unhashed stores in the repository.
1757
_lock_class = lockable_files.TransportLock
1759
def get_format_string(self):
1760
"""See BzrDirFormat.get_format_string()."""
1761
return "Bazaar-NG branch, format 5\n"
1763
def get_format_description(self):
1764
"""See BzrDirFormat.get_format_description()."""
1765
return "All-in-one format 5"
1767
def get_converter(self, format=None):
1768
"""See BzrDirFormat.get_converter()."""
1769
# there is one and only one upgrade path here.
1770
return ConvertBzrDir5To6()
1772
def _initialize_for_clone(self, url):
1773
return self.initialize_on_transport(get_transport(url), _cloning=True)
1775
def initialize_on_transport(self, transport, _cloning=False):
1776
"""Format 5 dirs always have working tree, branch and repository.
1778
Except when they are being cloned.
1780
from bzrlib.branch import BzrBranchFormat4
1781
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1782
from bzrlib.workingtree import WorkingTreeFormat2
1783
result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1784
RepositoryFormat5().initialize(result, _internal=True)
1786
branch = BzrBranchFormat4().initialize(result)
1788
WorkingTreeFormat2().initialize(result)
1789
except errors.NotLocalUrl:
1790
# Even though we can't access the working tree, we need to
1791
# create its control files.
1792
WorkingTreeFormat2()._stub_initialize_remote(branch)
1795
def _open(self, transport):
1796
"""See BzrDirFormat._open."""
1797
return BzrDir5(transport, self)
1799
def __return_repository_format(self):
1800
"""Circular import protection."""
1801
from bzrlib.repofmt.weaverepo import RepositoryFormat5
1802
return RepositoryFormat5()
1803
repository_format = property(__return_repository_format)
1806
class BzrDirFormat6(BzrDirFormat):
1807
"""Bzr control format 6.
1809
This format is a combined format for working tree, branch and repository.
1811
- Format 2 working trees [always]
1812
- Format 4 branches [always]
1813
- Format 6 repositories [always]
1816
_lock_class = lockable_files.TransportLock
1818
def get_format_string(self):
1819
"""See BzrDirFormat.get_format_string()."""
1820
return "Bazaar-NG branch, format 6\n"
1822
def get_format_description(self):
1823
"""See BzrDirFormat.get_format_description()."""
1824
return "All-in-one format 6"
1826
def get_converter(self, format=None):
1827
"""See BzrDirFormat.get_converter()."""
1828
# there is one and only one upgrade path here.
1829
return ConvertBzrDir6ToMeta()
1831
def _initialize_for_clone(self, url):
1832
return self.initialize_on_transport(get_transport(url), _cloning=True)
1834
def initialize_on_transport(self, transport, _cloning=False):
1835
"""Format 6 dirs always have working tree, branch and repository.
1837
Except when they are being cloned.
1839
from bzrlib.branch import BzrBranchFormat4
1840
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1841
from bzrlib.workingtree import WorkingTreeFormat2
1842
result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1843
RepositoryFormat6().initialize(result, _internal=True)
1845
branch = BzrBranchFormat4().initialize(result)
1847
WorkingTreeFormat2().initialize(result)
1848
except errors.NotLocalUrl:
1849
# Even though we can't access the working tree, we need to
1850
# create its control files.
1851
WorkingTreeFormat2()._stub_initialize_remote(branch)
1854
def _open(self, transport):
1855
"""See BzrDirFormat._open."""
1856
return BzrDir6(transport, self)
1858
def __return_repository_format(self):
1859
"""Circular import protection."""
1860
from bzrlib.repofmt.weaverepo import RepositoryFormat6
1861
return RepositoryFormat6()
1862
repository_format = property(__return_repository_format)
1865
class BzrDirMetaFormat1(BzrDirFormat):
1866
"""Bzr meta control format 1
1868
This is the first format with split out working tree, branch and repository
1871
- Format 3 working trees [optional]
1872
- Format 5 branches [optional]
1873
- Format 7 repositories [optional]
1876
_lock_class = lockdir.LockDir
1879
self._workingtree_format = None
1880
self._branch_format = None
1882
def __eq__(self, other):
1883
if other.__class__ is not self.__class__:
1885
if other.repository_format != self.repository_format:
1887
if other.workingtree_format != self.workingtree_format:
1891
def __ne__(self, other):
1892
return not self == other
1894
def get_branch_format(self):
1895
if self._branch_format is None:
1896
from bzrlib.branch import BranchFormat
1897
self._branch_format = BranchFormat.get_default_format()
1898
return self._branch_format
1900
def set_branch_format(self, format):
1901
self._branch_format = format
1903
def get_converter(self, format=None):
1904
"""See BzrDirFormat.get_converter()."""
1906
format = BzrDirFormat.get_default_format()
1907
if not isinstance(self, format.__class__):
1908
# converting away from metadir is not implemented
1909
raise NotImplementedError(self.get_converter)
1910
return ConvertMetaToMeta(format)
1912
def get_format_string(self):
1913
"""See BzrDirFormat.get_format_string()."""
1914
return "Bazaar-NG meta directory, format 1\n"
1916
def get_format_description(self):
1917
"""See BzrDirFormat.get_format_description()."""
1918
return "Meta directory format 1"
1920
def _open(self, transport):
1921
"""See BzrDirFormat._open."""
1922
return BzrDirMeta1(transport, self)
1924
def __return_repository_format(self):
1925
"""Circular import protection."""
1926
if getattr(self, '_repository_format', None):
1927
return self._repository_format
1928
from bzrlib.repository import RepositoryFormat
1929
return RepositoryFormat.get_default_format()
1931
def __set_repository_format(self, value):
1932
"""Allow changing the repository format for metadir formats."""
1933
self._repository_format = value
1935
repository_format = property(__return_repository_format, __set_repository_format)
1937
def __get_workingtree_format(self):
1938
if self._workingtree_format is None:
1939
from bzrlib.workingtree import WorkingTreeFormat
1940
self._workingtree_format = WorkingTreeFormat.get_default_format()
1941
return self._workingtree_format
1943
def __set_workingtree_format(self, wt_format):
1944
self._workingtree_format = wt_format
1946
workingtree_format = property(__get_workingtree_format,
1947
__set_workingtree_format)
1950
# Register bzr control format
1951
BzrDirFormat.register_control_format(BzrDirFormat)
1953
# Register bzr formats
1954
BzrDirFormat.register_format(BzrDirFormat4())
1955
BzrDirFormat.register_format(BzrDirFormat5())
1956
BzrDirFormat.register_format(BzrDirFormat6())
1957
__default_format = BzrDirMetaFormat1()
1958
BzrDirFormat.register_format(__default_format)
1959
BzrDirFormat._default_format = __default_format
1962
class Converter(object):
1963
"""Converts a disk format object from one format to another."""
1965
def convert(self, to_convert, pb):
1966
"""Perform the conversion of to_convert, giving feedback via pb.
1968
:param to_convert: The disk object to convert.
1969
:param pb: a progress bar to use for progress information.
1972
def step(self, message):
1973
"""Update the pb by a step."""
1975
self.pb.update(message, self.count, self.total)
1978
class ConvertBzrDir4To5(Converter):
1979
"""Converts format 4 bzr dirs to format 5."""
1982
super(ConvertBzrDir4To5, self).__init__()
1983
self.converted_revs = set()
1984
self.absent_revisions = set()
1988
def convert(self, to_convert, pb):
1989
"""See Converter.convert()."""
1990
self.bzrdir = to_convert
1992
self.pb.note('starting upgrade from format 4 to 5')
1993
if isinstance(self.bzrdir.transport, LocalTransport):
1994
self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
1995
self._convert_to_weaves()
1996
return BzrDir.open(self.bzrdir.root_transport.base)
1998
def _convert_to_weaves(self):
1999
self.pb.note('note: upgrade may be faster if all store files are ungzipped first')
2002
stat = self.bzrdir.transport.stat('weaves')
2003
if not S_ISDIR(stat.st_mode):
2004
self.bzrdir.transport.delete('weaves')
2005
self.bzrdir.transport.mkdir('weaves')
2006
except errors.NoSuchFile:
2007
self.bzrdir.transport.mkdir('weaves')
2008
# deliberately not a WeaveFile as we want to build it up slowly.
2009
self.inv_weave = Weave('inventory')
2010
# holds in-memory weaves for all files
2011
self.text_weaves = {}
2012
self.bzrdir.transport.delete('branch-format')
2013
self.branch = self.bzrdir.open_branch()
2014
self._convert_working_inv()
2015
rev_history = self.branch.revision_history()
2016
# to_read is a stack holding the revisions we still need to process;
2017
# appending to it adds new highest-priority revisions
2018
self.known_revisions = set(rev_history)
2019
self.to_read = rev_history[-1:]
2021
rev_id = self.to_read.pop()
2022
if (rev_id not in self.revisions
2023
and rev_id not in self.absent_revisions):
2024
self._load_one_rev(rev_id)
2026
to_import = self._make_order()
2027
for i, rev_id in enumerate(to_import):
2028
self.pb.update('converting revision', i, len(to_import))
2029
self._convert_one_rev(rev_id)
2031
self._write_all_weaves()
2032
self._write_all_revs()
2033
self.pb.note('upgraded to weaves:')
2034
self.pb.note(' %6d revisions and inventories', len(self.revisions))
2035
self.pb.note(' %6d revisions not present', len(self.absent_revisions))
2036
self.pb.note(' %6d texts', self.text_count)
2037
self._cleanup_spare_files_after_format4()
2038
self.branch._transport.put_bytes(
2040
BzrDirFormat5().get_format_string(),
2041
mode=self.bzrdir._get_file_mode())
2043
def _cleanup_spare_files_after_format4(self):
2044
# FIXME working tree upgrade foo.
2045
for n in 'merged-patches', 'pending-merged-patches':
2047
## assert os.path.getsize(p) == 0
2048
self.bzrdir.transport.delete(n)
2049
except errors.NoSuchFile:
2051
self.bzrdir.transport.delete_tree('inventory-store')
2052
self.bzrdir.transport.delete_tree('text-store')
2054
def _convert_working_inv(self):
2055
inv = xml4.serializer_v4.read_inventory(
2056
self.branch._transport.get('inventory'))
2057
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv, working=True)
2058
self.branch._transport.put_bytes('inventory', new_inv_xml,
2059
mode=self.bzrdir._get_file_mode())
2061
def _write_all_weaves(self):
2062
controlweaves = WeaveStore(self.bzrdir.transport, prefixed=False)
2063
weave_transport = self.bzrdir.transport.clone('weaves')
2064
weaves = WeaveStore(weave_transport, prefixed=False)
2065
transaction = WriteTransaction()
2069
for file_id, file_weave in self.text_weaves.items():
2070
self.pb.update('writing weave', i, len(self.text_weaves))
2071
weaves._put_weave(file_id, file_weave, transaction)
2073
self.pb.update('inventory', 0, 1)
2074
controlweaves._put_weave('inventory', self.inv_weave, transaction)
2075
self.pb.update('inventory', 1, 1)
2079
def _write_all_revs(self):
2080
"""Write all revisions out in new form."""
2081
self.bzrdir.transport.delete_tree('revision-store')
2082
self.bzrdir.transport.mkdir('revision-store')
2083
revision_transport = self.bzrdir.transport.clone('revision-store')
2085
from bzrlib.xml5 import serializer_v5
2086
from bzrlib.repofmt.weaverepo import RevisionTextStore
2087
revision_store = RevisionTextStore(revision_transport,
2088
serializer_v5, False, versionedfile.PrefixMapper(),
2089
lambda:True, lambda:True)
2091
for i, rev_id in enumerate(self.converted_revs):
2092
self.pb.update('write revision', i, len(self.converted_revs))
2093
text = serializer_v5.write_revision_to_string(
2094
self.revisions[rev_id])
2096
revision_store.add_lines(key, None, osutils.split_lines(text))
2100
def _load_one_rev(self, rev_id):
2101
"""Load a revision object into memory.
2103
Any parents not either loaded or abandoned get queued to be
2105
self.pb.update('loading revision',
2106
len(self.revisions),
2107
len(self.known_revisions))
2108
if not self.branch.repository.has_revision(rev_id):
2110
self.pb.note('revision {%s} not present in branch; '
2111
'will be converted as a ghost',
2113
self.absent_revisions.add(rev_id)
2115
rev = self.branch.repository.get_revision(rev_id)
2116
for parent_id in rev.parent_ids:
2117
self.known_revisions.add(parent_id)
2118
self.to_read.append(parent_id)
2119
self.revisions[rev_id] = rev
2121
def _load_old_inventory(self, rev_id):
2122
old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
2123
inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
2124
inv.revision_id = rev_id
2125
rev = self.revisions[rev_id]
2128
def _load_updated_inventory(self, rev_id):
2129
inv_xml = self.inv_weave.get_text(rev_id)
2130
inv = xml5.serializer_v5.read_inventory_from_string(inv_xml, rev_id)
2133
def _convert_one_rev(self, rev_id):
2134
"""Convert revision and all referenced objects to new format."""
2135
rev = self.revisions[rev_id]
2136
inv = self._load_old_inventory(rev_id)
2137
present_parents = [p for p in rev.parent_ids
2138
if p not in self.absent_revisions]
2139
self._convert_revision_contents(rev, inv, present_parents)
2140
self._store_new_inv(rev, inv, present_parents)
2141
self.converted_revs.add(rev_id)
2143
def _store_new_inv(self, rev, inv, present_parents):
2144
new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
2145
new_inv_sha1 = sha_string(new_inv_xml)
2146
self.inv_weave.add_lines(rev.revision_id,
2148
new_inv_xml.splitlines(True))
2149
rev.inventory_sha1 = new_inv_sha1
2151
def _convert_revision_contents(self, rev, inv, present_parents):
2152
"""Convert all the files within a revision.
2154
Also upgrade the inventory to refer to the text revision ids."""
2155
rev_id = rev.revision_id
2156
mutter('converting texts of revision {%s}',
2158
parent_invs = map(self._load_updated_inventory, present_parents)
2159
entries = inv.iter_entries()
2161
for path, ie in entries:
2162
self._convert_file_version(rev, ie, parent_invs)
2164
def _convert_file_version(self, rev, ie, parent_invs):
2165
"""Convert one version of one file.
2167
The file needs to be added into the weave if it is a merge
2168
of >=2 parents or if it's changed from its parent.
2170
file_id = ie.file_id
2171
rev_id = rev.revision_id
2172
w = self.text_weaves.get(file_id)
2175
self.text_weaves[file_id] = w
2176
text_changed = False
2177
parent_candiate_entries = ie.parent_candidates(parent_invs)
2178
heads = graph.Graph(self).heads(parent_candiate_entries.keys())
2179
# XXX: Note that this is unordered - and this is tolerable because
2180
# the previous code was also unordered.
2181
previous_entries = dict((head, parent_candiate_entries[head]) for head
2183
self.snapshot_ie(previous_entries, ie, w, rev_id)
2186
@symbol_versioning.deprecated_method(symbol_versioning.one_one)
2187
def get_parents(self, revision_ids):
2188
for revision_id in revision_ids:
2189
yield self.revisions[revision_id].parent_ids
2191
def get_parent_map(self, revision_ids):
2192
"""See graph._StackedParentsProvider.get_parent_map"""
2193
return dict((revision_id, self.revisions[revision_id])
2194
for revision_id in revision_ids
2195
if revision_id in self.revisions)
2197
def snapshot_ie(self, previous_revisions, ie, w, rev_id):
2198
# TODO: convert this logic, which is ~= snapshot to
2199
# a call to:. This needs the path figured out. rather than a work_tree
2200
# a v4 revision_tree can be given, or something that looks enough like
2201
# one to give the file content to the entry if it needs it.
2202
# and we need something that looks like a weave store for snapshot to
2204
#ie.snapshot(rev, PATH, previous_revisions, REVISION_TREE, InMemoryWeaveStore(self.text_weaves))
2205
if len(previous_revisions) == 1:
2206
previous_ie = previous_revisions.values()[0]
2207
if ie._unchanged(previous_ie):
2208
ie.revision = previous_ie.revision
2211
text = self.branch.repository._text_store.get(ie.text_id)
2212
file_lines = text.readlines()
2213
w.add_lines(rev_id, previous_revisions, file_lines)
2214
self.text_count += 1
2216
w.add_lines(rev_id, previous_revisions, [])
2217
ie.revision = rev_id
2219
def _make_order(self):
2220
"""Return a suitable order for importing revisions.
2222
The order must be such that an revision is imported after all
2223
its (present) parents.
2225
todo = set(self.revisions.keys())
2226
done = self.absent_revisions.copy()
2229
# scan through looking for a revision whose parents
2231
for rev_id in sorted(list(todo)):
2232
rev = self.revisions[rev_id]
2233
parent_ids = set(rev.parent_ids)
2234
if parent_ids.issubset(done):
2235
# can take this one now
2236
order.append(rev_id)
2242
class ConvertBzrDir5To6(Converter):
2243
"""Converts format 5 bzr dirs to format 6."""
2245
def convert(self, to_convert, pb):
2246
"""See Converter.convert()."""
2247
self.bzrdir = to_convert
2249
self.pb.note('starting upgrade from format 5 to 6')
2250
self._convert_to_prefixed()
2251
return BzrDir.open(self.bzrdir.root_transport.base)
2253
def _convert_to_prefixed(self):
2254
from bzrlib.store import TransportStore
2255
self.bzrdir.transport.delete('branch-format')
2256
for store_name in ["weaves", "revision-store"]:
2257
self.pb.note("adding prefixes to %s" % store_name)
2258
store_transport = self.bzrdir.transport.clone(store_name)
2259
store = TransportStore(store_transport, prefixed=True)
2260
for urlfilename in store_transport.list_dir('.'):
2261
filename = urlutils.unescape(urlfilename)
2262
if (filename.endswith(".weave") or
2263
filename.endswith(".gz") or
2264
filename.endswith(".sig")):
2265
file_id, suffix = os.path.splitext(filename)
2269
new_name = store._mapper.map((file_id,)) + suffix
2270
# FIXME keep track of the dirs made RBC 20060121
2272
store_transport.move(filename, new_name)
2273
except errors.NoSuchFile: # catches missing dirs strangely enough
2274
store_transport.mkdir(osutils.dirname(new_name))
2275
store_transport.move(filename, new_name)
2276
self.bzrdir.transport.put_bytes(
2278
BzrDirFormat6().get_format_string(),
2279
mode=self.bzrdir._get_file_mode())
2282
class ConvertBzrDir6ToMeta(Converter):
2283
"""Converts format 6 bzr dirs to metadirs."""
2285
def convert(self, to_convert, pb):
2286
"""See Converter.convert()."""
2287
from bzrlib.repofmt.weaverepo import RepositoryFormat7
2288
from bzrlib.branch import BzrBranchFormat5
2289
self.bzrdir = to_convert
2292
self.total = 20 # the steps we know about
2293
self.garbage_inventories = []
2294
self.dir_mode = self.bzrdir._get_dir_mode()
2295
self.file_mode = self.bzrdir._get_file_mode()
2297
self.pb.note('starting upgrade from format 6 to metadir')
2298
self.bzrdir.transport.put_bytes(
2300
"Converting to format 6",
2301
mode=self.file_mode)
2302
# its faster to move specific files around than to open and use the apis...
2303
# first off, nuke ancestry.weave, it was never used.
2305
self.step('Removing ancestry.weave')
2306
self.bzrdir.transport.delete('ancestry.weave')
2307
except errors.NoSuchFile:
2309
# find out whats there
2310
self.step('Finding branch files')
2311
last_revision = self.bzrdir.open_branch().last_revision()
2312
bzrcontents = self.bzrdir.transport.list_dir('.')
2313
for name in bzrcontents:
2314
if name.startswith('basis-inventory.'):
2315
self.garbage_inventories.append(name)
2316
# create new directories for repository, working tree and branch
2317
repository_names = [('inventory.weave', True),
2318
('revision-store', True),
2320
self.step('Upgrading repository ')
2321
self.bzrdir.transport.mkdir('repository', mode=self.dir_mode)
2322
self.make_lock('repository')
2323
# we hard code the formats here because we are converting into
2324
# the meta format. The meta format upgrader can take this to a
2325
# future format within each component.
2326
self.put_format('repository', RepositoryFormat7())
2327
for entry in repository_names:
2328
self.move_entry('repository', entry)
2330
self.step('Upgrading branch ')
2331
self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
2332
self.make_lock('branch')
2333
self.put_format('branch', BzrBranchFormat5())
2334
branch_files = [('revision-history', True),
2335
('branch-name', True),
2337
for entry in branch_files:
2338
self.move_entry('branch', entry)
2340
checkout_files = [('pending-merges', True),
2341
('inventory', True),
2342
('stat-cache', False)]
2343
# If a mandatory checkout file is not present, the branch does not have
2344
# a functional checkout. Do not create a checkout in the converted
2346
for name, mandatory in checkout_files:
2347
if mandatory and name not in bzrcontents:
2348
has_checkout = False
2352
if not has_checkout:
2353
self.pb.note('No working tree.')
2354
# If some checkout files are there, we may as well get rid of them.
2355
for name, mandatory in checkout_files:
2356
if name in bzrcontents:
2357
self.bzrdir.transport.delete(name)
2359
from bzrlib.workingtree import WorkingTreeFormat3
2360
self.step('Upgrading working tree')
2361
self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
2362
self.make_lock('checkout')
2364
'checkout', WorkingTreeFormat3())
2365
self.bzrdir.transport.delete_multi(
2366
self.garbage_inventories, self.pb)
2367
for entry in checkout_files:
2368
self.move_entry('checkout', entry)
2369
if last_revision is not None:
2370
self.bzrdir.transport.put_bytes(
2371
'checkout/last-revision', last_revision)
2372
self.bzrdir.transport.put_bytes(
2374
BzrDirMetaFormat1().get_format_string(),
2375
mode=self.file_mode)
2376
return BzrDir.open(self.bzrdir.root_transport.base)
2378
def make_lock(self, name):
2379
"""Make a lock for the new control dir name."""
2380
self.step('Make %s lock' % name)
2381
ld = lockdir.LockDir(self.bzrdir.transport,
2383
file_modebits=self.file_mode,
2384
dir_modebits=self.dir_mode)
2387
def move_entry(self, new_dir, entry):
2388
"""Move then entry name into new_dir."""
2390
mandatory = entry[1]
2391
self.step('Moving %s' % name)
2393
self.bzrdir.transport.move(name, '%s/%s' % (new_dir, name))
2394
except errors.NoSuchFile:
2398
def put_format(self, dirname, format):
2399
self.bzrdir.transport.put_bytes('%s/format' % dirname,
2400
format.get_format_string(),
2404
class ConvertMetaToMeta(Converter):
2405
"""Converts the components of metadirs."""
2407
def __init__(self, target_format):
2408
"""Create a metadir to metadir converter.
2410
:param target_format: The final metadir format that is desired.
2412
self.target_format = target_format
2414
def convert(self, to_convert, pb):
2415
"""See Converter.convert()."""
2416
self.bzrdir = to_convert
2420
self.step('checking repository format')
2422
repo = self.bzrdir.open_repository()
2423
except errors.NoRepositoryPresent:
2426
if not isinstance(repo._format, self.target_format.repository_format.__class__):
2427
from bzrlib.repository import CopyConverter
2428
self.pb.note('starting repository conversion')
2429
converter = CopyConverter(self.target_format.repository_format)
2430
converter.convert(repo, pb)
2432
branch = self.bzrdir.open_branch()
2433
except errors.NotBranchError:
2436
# TODO: conversions of Branch and Tree should be done by
2437
# InterXFormat lookups
2438
# Avoid circular imports
2439
from bzrlib import branch as _mod_branch
2440
if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2441
self.target_format.get_branch_format().__class__ is
2442
_mod_branch.BzrBranchFormat6):
2443
branch_converter = _mod_branch.Converter5to6()
2444
branch_converter.convert(branch)
2446
tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
2447
except (errors.NoWorkingTree, errors.NotLocalUrl):
2450
# TODO: conversions of Branch and Tree should be done by
2451
# InterXFormat lookups
2452
if (isinstance(tree, workingtree.WorkingTree3) and
2453
not isinstance(tree, workingtree_4.WorkingTree4) and
2454
isinstance(self.target_format.workingtree_format,
2455
workingtree_4.WorkingTreeFormat4)):
2456
workingtree_4.Converter3to4().convert(tree)
2460
# This is not in remote.py because it's small, and needs to be registered.
2461
# Putting it in remote.py creates a circular import problem.
2462
# we can make it a lazy object if the control formats is turned into something
2464
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2465
"""Format representing bzrdirs accessed via a smart server"""
2467
def get_format_description(self):
2468
return 'bzr remote bzrdir'
2471
def probe_transport(klass, transport):
2472
"""Return a RemoteBzrDirFormat object if it looks possible."""
2474
medium = transport.get_smart_medium()
2475
except (NotImplementedError, AttributeError,
2476
errors.TransportNotPossible, errors.NoSmartMedium,
2477
errors.SmartProtocolError):
2478
# no smart server, so not a branch for this format type.
2479
raise errors.NotBranchError(path=transport.base)
2481
# Decline to open it if the server doesn't support our required
2482
# version (3) so that the VFS-based transport will do it.
2483
if medium.should_probe():
2485
server_version = medium.protocol_version()
2486
except errors.SmartProtocolError:
2487
# Apparently there's no usable smart server there, even though
2488
# the medium supports the smart protocol.
2489
raise errors.NotBranchError(path=transport.base)
2490
if server_version != '2':
2491
raise errors.NotBranchError(path=transport.base)
2494
def initialize_on_transport(self, transport):
2496
# hand off the request to the smart server
2497
client_medium = transport.get_smart_medium()
2498
except errors.NoSmartMedium:
2499
# TODO: lookup the local format from a server hint.
2500
local_dir_format = BzrDirMetaFormat1()
2501
return local_dir_format.initialize_on_transport(transport)
2502
client = _SmartClient(client_medium)
2503
path = client.remote_path_from_transport(transport)
2504
response = client.call('BzrDirFormat.initialize', path)
2505
if response[0] != 'ok':
2506
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
2507
return remote.RemoteBzrDir(transport)
2509
def _open(self, transport):
2510
return remote.RemoteBzrDir(transport)
2512
def __eq__(self, other):
2513
if not isinstance(other, RemoteBzrDirFormat):
2515
return self.get_format_description() == other.get_format_description()
2518
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
2521
class BzrDirFormatInfo(object):
2523
def __init__(self, native, deprecated, hidden, experimental):
2524
self.deprecated = deprecated
2525
self.native = native
2526
self.hidden = hidden
2527
self.experimental = experimental
2530
class BzrDirFormatRegistry(registry.Registry):
2531
"""Registry of user-selectable BzrDir subformats.
2533
Differs from BzrDirFormat._control_formats in that it provides sub-formats,
2534
e.g. BzrDirMeta1 with weave repository. Also, it's more user-oriented.
2538
"""Create a BzrDirFormatRegistry."""
2539
self._aliases = set()
2540
super(BzrDirFormatRegistry, self).__init__()
2543
"""Return a set of the format names which are aliases."""
2544
return frozenset(self._aliases)
2546
def register_metadir(self, key,
2547
repository_format, help, native=True, deprecated=False,
2553
"""Register a metadir subformat.
2555
These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2556
by the Repository format.
2558
:param repository_format: The fully-qualified repository format class
2560
:param branch_format: Fully-qualified branch format class name as
2562
:param tree_format: Fully-qualified tree format class name as
2565
# This should be expanded to support setting WorkingTree and Branch
2566
# formats, once BzrDirMetaFormat1 supports that.
2567
def _load(full_name):
2568
mod_name, factory_name = full_name.rsplit('.', 1)
2570
mod = __import__(mod_name, globals(), locals(),
2572
except ImportError, e:
2573
raise ImportError('failed to load %s: %s' % (full_name, e))
2575
factory = getattr(mod, factory_name)
2576
except AttributeError:
2577
raise AttributeError('no factory %s in module %r'
2582
bd = BzrDirMetaFormat1()
2583
if branch_format is not None:
2584
bd.set_branch_format(_load(branch_format))
2585
if tree_format is not None:
2586
bd.workingtree_format = _load(tree_format)
2587
if repository_format is not None:
2588
bd.repository_format = _load(repository_format)
2590
self.register(key, helper, help, native, deprecated, hidden,
2591
experimental, alias)
2593
def register(self, key, factory, help, native=True, deprecated=False,
2594
hidden=False, experimental=False, alias=False):
2595
"""Register a BzrDirFormat factory.
2597
The factory must be a callable that takes one parameter: the key.
2598
It must produce an instance of the BzrDirFormat when called.
2600
This function mainly exists to prevent the info object from being
2603
registry.Registry.register(self, key, factory, help,
2604
BzrDirFormatInfo(native, deprecated, hidden, experimental))
2606
self._aliases.add(key)
2608
def register_lazy(self, key, module_name, member_name, help, native=True,
2609
deprecated=False, hidden=False, experimental=False, alias=False):
2610
registry.Registry.register_lazy(self, key, module_name, member_name,
2611
help, BzrDirFormatInfo(native, deprecated, hidden, experimental))
2613
self._aliases.add(key)
2615
def set_default(self, key):
2616
"""Set the 'default' key to be a clone of the supplied key.
2618
This method must be called once and only once.
2620
registry.Registry.register(self, 'default', self.get(key),
2621
self.get_help(key), info=self.get_info(key))
2622
self._aliases.add('default')
2624
def set_default_repository(self, key):
2625
"""Set the FormatRegistry default and Repository default.
2627
This is a transitional method while Repository.set_default_format
2630
if 'default' in self:
2631
self.remove('default')
2632
self.set_default(key)
2633
format = self.get('default')()
2635
def make_bzrdir(self, key):
2636
return self.get(key)()
2638
def help_topic(self, topic):
2639
output = textwrap.dedent("""\
2640
These formats can be used for creating branches, working trees, and
2644
default_realkey = None
2645
default_help = self.get_help('default')
2647
for key in self.keys():
2648
if key == 'default':
2650
help = self.get_help(key)
2651
if help == default_help:
2652
default_realkey = key
2654
help_pairs.append((key, help))
2656
def wrapped(key, help, info):
2658
help = '(native) ' + help
2659
return ':%s:\n%s\n\n' % (key,
2660
textwrap.fill(help, initial_indent=' ',
2661
subsequent_indent=' '))
2662
if default_realkey is not None:
2663
output += wrapped(default_realkey, '(default) %s' % default_help,
2664
self.get_info('default'))
2665
deprecated_pairs = []
2666
experimental_pairs = []
2667
for key, help in help_pairs:
2668
info = self.get_info(key)
2671
elif info.deprecated:
2672
deprecated_pairs.append((key, help))
2673
elif info.experimental:
2674
experimental_pairs.append((key, help))
2676
output += wrapped(key, help, info)
2677
if len(experimental_pairs) > 0:
2678
output += "Experimental formats are shown below.\n\n"
2679
for key, help in experimental_pairs:
2680
info = self.get_info(key)
2681
output += wrapped(key, help, info)
2682
if len(deprecated_pairs) > 0:
2683
output += "Deprecated formats are shown below.\n\n"
2684
for key, help in deprecated_pairs:
2685
info = self.get_info(key)
2686
output += wrapped(key, help, info)
2691
class RepositoryAcquisitionPolicy(object):
2692
"""Abstract base class for repository acquisition policies.
2694
A repository acquisition policy decides how a BzrDir acquires a repository
2695
for a branch that is being created. The most basic policy decision is
2696
whether to create a new repository or use an existing one.
2699
def configure_branch(self, branch):
2700
"""Apply any configuration data from this policy to the branch.
2702
Default implementation does nothing.
2706
def acquire_repository(self, make_working_trees=None, shared=False):
2707
"""Acquire a repository for this bzrdir.
2709
Implementations may create a new repository or use a pre-exising
2711
:param make_working_trees: If creating a repository, set
2712
make_working_trees to this value (if non-None)
2713
:param shared: If creating a repository, make it shared if True
2714
:return: A repository
2716
raise NotImplemented(RepositoryAcquisitionPolicy.acquire_repository)
2719
class CreateRepository(RepositoryAcquisitionPolicy):
2720
"""A policy of creating a new repository"""
2722
def __init__(self, bzrdir):
2723
RepositoryAcquisitionPolicy.__init__(self)
2724
self._bzrdir = bzrdir
2726
def acquire_repository(self, make_working_trees=None, shared=False):
2727
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
2729
Creates the desired repository in the bzrdir we already have.
2731
repository = self._bzrdir.create_repository(shared=shared)
2732
if make_working_trees is not None:
2733
repository.set_make_working_trees(make_working_trees)
2737
class UseExistingRepository(RepositoryAcquisitionPolicy):
2738
"""A policy of reusing an existing repository"""
2740
def __init__(self, repository):
2741
RepositoryAcquisitionPolicy.__init__(self)
2742
self._repository = repository
2744
def acquire_repository(self, make_working_trees=None, shared=False):
2745
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
2747
Returns an existing repository to use
2749
return self._repository
2752
format_registry = BzrDirFormatRegistry()
2753
format_registry.register('weave', BzrDirFormat6,
2754
'Pre-0.8 format. Slower than knit and does not'
2755
' support checkouts or shared repositories.',
2757
format_registry.register_metadir('knit',
2758
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2759
'Format using knits. Recommended for interoperation with bzr <= 0.14.',
2760
branch_format='bzrlib.branch.BzrBranchFormat5',
2761
tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2762
format_registry.register_metadir('metaweave',
2763
'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2764
'Transitional format in 0.8. Slower than knit.',
2765
branch_format='bzrlib.branch.BzrBranchFormat5',
2766
tree_format='bzrlib.workingtree.WorkingTreeFormat3',
2768
format_registry.register_metadir('dirstate',
2769
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2770
help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
2771
'above when accessed over the network.',
2772
branch_format='bzrlib.branch.BzrBranchFormat5',
2773
# this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
2774
# directly from workingtree_4 triggers a circular import.
2775
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2777
format_registry.register_metadir('dirstate-tags',
2778
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2779
help='New in 0.15: Fast local operations and improved scaling for '
2780
'network operations. Additionally adds support for tags.'
2781
' Incompatible with bzr < 0.15.',
2782
branch_format='bzrlib.branch.BzrBranchFormat6',
2783
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2785
format_registry.register_metadir('rich-root',
2786
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit4',
2787
help='New in 1.0. Better handling of tree roots. Incompatible with'
2789
branch_format='bzrlib.branch.BzrBranchFormat6',
2790
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2792
format_registry.register_metadir('dirstate-with-subtree',
2793
'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
2794
help='New in 0.15: Fast local operations and improved scaling for '
2795
'network operations. Additionally adds support for versioning nested '
2796
'bzr branches. Incompatible with bzr < 0.15.',
2797
branch_format='bzrlib.branch.BzrBranchFormat6',
2798
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2802
format_registry.register_metadir('pack-0.92',
2803
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack1',
2804
help='New in 0.92: Pack-based format with data compatible with '
2805
'dirstate-tags format repositories. Interoperates with '
2806
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2807
'Previously called knitpack-experimental. '
2808
'For more information, see '
2809
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2810
branch_format='bzrlib.branch.BzrBranchFormat6',
2811
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2813
format_registry.register_metadir('pack-0.92-subtree',
2814
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack3',
2815
help='New in 0.92: Pack-based format with data compatible with '
2816
'dirstate-with-subtree format repositories. Interoperates with '
2817
'bzr repositories before 0.92 but cannot be read by bzr < 0.92. '
2818
'Previously called knitpack-experimental. '
2819
'For more information, see '
2820
'http://doc.bazaar-vcs.org/latest/developers/packrepo.html.',
2821
branch_format='bzrlib.branch.BzrBranchFormat6',
2822
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2826
format_registry.register_metadir('rich-root-pack',
2827
'bzrlib.repofmt.pack_repo.RepositoryFormatKnitPack4',
2828
help='New in 1.0: Pack-based format with data compatible with '
2829
'rich-root format repositories. Incompatible with'
2831
branch_format='bzrlib.branch.BzrBranchFormat6',
2832
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2834
# The following two formats should always just be aliases.
2835
format_registry.register_metadir('development',
2836
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
2837
help='Current development format. Can convert data to and from pack-0.92 '
2838
'(and anything compatible with pack-0.92) format repositories. '
2839
'Repositories in this format can only be read by bzr.dev. '
2841
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2843
branch_format='bzrlib.branch.BzrBranchFormat6',
2844
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2848
format_registry.register_metadir('development-subtree',
2849
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
2850
help='Current development format, subtree variant. Can convert data to and '
2851
'from pack-0.92 (and anything compatible with pack-0.92) format '
2852
'repositories. Repositories in this format can only be read by '
2853
'bzr.dev. Please read '
2854
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2856
branch_format='bzrlib.branch.BzrBranchFormat6',
2857
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2861
# And the development formats which the will have aliased one of follow:
2862
format_registry.register_metadir('development0',
2863
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0',
2864
help='Trivial rename of pack-0.92 to provide a development format. '
2866
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2868
branch_format='bzrlib.branch.BzrBranchFormat6',
2869
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2873
format_registry.register_metadir('development0-subtree',
2874
'bzrlib.repofmt.pack_repo.RepositoryFormatPackDevelopment0Subtree',
2875
help='Trivial rename of pack-0.92-subtree to provide a development format. '
2877
'http://doc.bazaar-vcs.org/latest/developers/development-repo.html '
2879
branch_format='bzrlib.branch.BzrBranchFormat6',
2880
tree_format='bzrlib.workingtree.WorkingTreeFormat4',
2884
format_registry.set_default('pack-0.92')