1
# Copyright (C) 2006-2011 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
from __future__ import absolute_import
32
from ..lazy_import import lazy_import
33
lazy_import(globals(), """
36
branch as _mod_branch,
45
revision as _mod_revision,
46
transport as _mod_transport,
51
from breezy.bzr import (
52
branch as _mod_bzrbranch,
58
from breezy.bzr import fullhistory as fullhistorybranch
59
from breezy.bzr import knitpack_repo
60
from breezy.transport import (
61
do_catching_redirections,
64
from breezy.i18n import gettext
80
class MissingFeature(errors.BzrError):
82
_fmt = ("Missing feature %(feature)s not provided by this "
83
"version of Bazaar or any plugin.")
85
def __init__(self, feature):
86
self.feature = feature
89
class FeatureAlreadyRegistered(errors.BzrError):
91
_fmt = 'The feature %(feature)s has already been registered.'
93
def __init__(self, feature):
94
self.feature = feature
97
class BzrDir(controldir.ControlDir):
98
"""A .bzr control diretory.
100
BzrDir instances let you create or open any of the things that can be
101
found within .bzr - checkouts, branches and repositories.
104
the transport which this bzr dir is rooted at (i.e. file:///.../.bzr/)
105
:ivar root_transport:
106
a transport connected to the directory this bzr was opened from
107
(i.e. the parent directory holding the .bzr directory).
109
Everything in the bzrdir should have the same file permissions.
111
:cvar hooks: An instance of BzrDirHooks.
114
def break_lock(self):
115
"""Invoke break_lock on the first object in the bzrdir.
117
If there is a tree, the tree is opened and break_lock() called.
118
Otherwise, branch is tried, and finally repository.
120
# XXX: This seems more like a UI function than something that really
121
# belongs in this class.
123
thing_to_unlock = self.open_workingtree()
124
except (errors.NotLocalUrl, errors.NoWorkingTree):
126
thing_to_unlock = self.open_branch()
127
except errors.NotBranchError:
129
thing_to_unlock = self.open_repository()
130
except errors.NoRepositoryPresent:
132
thing_to_unlock.break_lock()
134
def check_conversion_target(self, target_format):
135
"""Check that a bzrdir as a whole can be converted to a new format."""
136
# The only current restriction is that the repository content can be
137
# fetched compatibly with the target.
138
target_repo_format = target_format.repository_format
140
self.open_repository()._format.check_conversion_target(
142
except errors.NoRepositoryPresent:
143
# No repo, no problem.
146
def clone_on_transport(self, transport, revision_id=None,
147
force_new_repo=False, preserve_stacking=False, stacked_on=None,
148
create_prefix=False, use_existing_dir=True, no_tree=False):
149
"""Clone this bzrdir and its contents to transport verbatim.
151
:param transport: The transport for the location to produce the clone
152
at. If the target directory does not exist, it will be created.
153
:param revision_id: The tip revision-id to use for any branch or
154
working tree. If not None, then the clone operation may tune
155
itself to download less data.
156
:param force_new_repo: Do not use a shared repository for the target,
157
even if one is available.
158
:param preserve_stacking: When cloning a stacked branch, stack the
159
new branch on top of the other branch's stacked-on branch.
160
:param create_prefix: Create any missing directories leading up to
162
:param use_existing_dir: Use an existing directory if one exists.
163
:param no_tree: If set to true prevents creation of a working tree.
165
# Overview: put together a broad description of what we want to end up
166
# with; then make as few api calls as possible to do it.
168
# We may want to create a repo/branch/tree, if we do so what format
169
# would we want for each:
170
require_stacking = (stacked_on is not None)
171
format = self.cloning_metadir(require_stacking)
173
# Figure out what objects we want:
175
local_repo = self.find_repository()
176
except errors.NoRepositoryPresent:
179
local_branch = self.open_branch()
180
except errors.NotBranchError:
183
# enable fallbacks when branch is not a branch reference
184
if local_branch.repository.has_same_location(local_repo):
185
local_repo = local_branch.repository
186
if preserve_stacking:
188
stacked_on = local_branch.get_stacked_on_url()
189
except (_mod_branch.UnstackableBranchFormat,
190
errors.UnstackableRepositoryFormat,
193
# Bug: We create a metadir without knowing if it can support stacking,
194
# we should look up the policy needs first, or just use it as a hint,
197
make_working_trees = local_repo.make_working_trees() and not no_tree
198
want_shared = local_repo.is_shared()
199
repo_format_name = format.repository_format.network_name()
201
make_working_trees = False
203
repo_format_name = None
205
result_repo, result, require_stacking, repository_policy = \
206
format.initialize_on_transport_ex(transport,
207
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
208
force_new_repo=force_new_repo, stacked_on=stacked_on,
209
stack_on_pwd=self.root_transport.base,
210
repo_format_name=repo_format_name,
211
make_working_trees=make_working_trees, shared_repo=want_shared)
214
# If the result repository is in the same place as the
215
# resulting bzr dir, it will have no content, further if the
216
# result is not stacked then we know all content should be
217
# copied, and finally if we are copying up to a specific
218
# revision_id then we can use the pending-ancestry-result which
219
# does not require traversing all of history to describe it.
220
if (result_repo.user_url == result.user_url
221
and not require_stacking and
222
revision_id is not None):
223
fetch_spec = vf_search.PendingAncestryResult(
224
[revision_id], local_repo)
225
result_repo.fetch(local_repo, fetch_spec=fetch_spec)
227
result_repo.fetch(local_repo, revision_id=revision_id)
231
if result_repo is not None:
232
raise AssertionError('result_repo not None(%r)' % result_repo)
233
# 1 if there is a branch present
234
# make sure its content is available in the target repository
236
if local_branch is not None:
237
result_branch = local_branch.clone(result, revision_id=revision_id,
238
repository_policy=repository_policy)
240
# Cheaper to check if the target is not local, than to try making
242
result.root_transport.local_abspath('.')
243
if result_repo is None or result_repo.make_working_trees():
244
self.open_workingtree().clone(result, revision_id=revision_id)
245
except (errors.NoWorkingTree, errors.NotLocalUrl):
249
# TODO: This should be given a Transport, and should chdir up; otherwise
250
# this will open a new connection.
251
def _make_tail(self, url):
252
t = _mod_transport.get_transport(url)
255
def determine_repository_policy(self, force_new_repo=False, stack_on=None,
256
stack_on_pwd=None, require_stacking=False):
257
"""Return an object representing a policy to use.
259
This controls whether a new repository is created, and the format of
260
that repository, or some existing shared repository used instead.
262
If stack_on is supplied, will not seek a containing shared repo.
264
:param force_new_repo: If True, require a new repository to be created.
265
:param stack_on: If supplied, the location to stack on. If not
266
supplied, a default_stack_on location may be used.
267
:param stack_on_pwd: If stack_on is relative, the location it is
270
def repository_policy(found_bzrdir):
273
config = found_bzrdir.get_config()
275
stack_on = config.get_default_stack_on()
276
if stack_on is not None:
277
stack_on_pwd = found_bzrdir.user_url
279
# does it have a repository ?
281
repository = found_bzrdir.open_repository()
282
except errors.NoRepositoryPresent:
285
if (found_bzrdir.user_url != self.user_url
286
and not repository.is_shared()):
287
# Don't look higher, can't use a higher shared repo.
295
return UseExistingRepository(repository, stack_on,
296
stack_on_pwd, require_stacking=require_stacking), True
298
return CreateRepository(self, stack_on, stack_on_pwd,
299
require_stacking=require_stacking), True
301
if not force_new_repo:
303
policy = self._find_containing(repository_policy)
304
if policy is not None:
308
return UseExistingRepository(
309
self.open_repository(), stack_on, stack_on_pwd,
310
require_stacking=require_stacking)
311
except errors.NoRepositoryPresent:
313
return CreateRepository(self, stack_on, stack_on_pwd,
314
require_stacking=require_stacking)
316
def _find_or_create_repository(self, force_new_repo):
317
"""Create a new repository if needed, returning the repository."""
318
policy = self.determine_repository_policy(force_new_repo)
319
return policy.acquire_repository()[0]
321
def _find_source_repo(self, add_cleanup, source_branch):
322
"""Find the source branch and repo for a sprout operation.
324
This is helper intended for use by _sprout.
326
:returns: (source_branch, source_repository). Either or both may be
327
None. If not None, they will be read-locked (and their unlock(s)
328
scheduled via the add_cleanup param).
330
if source_branch is not None:
331
add_cleanup(source_branch.lock_read().unlock)
332
return source_branch, source_branch.repository
334
source_branch = self.open_branch()
335
source_repository = source_branch.repository
336
except errors.NotBranchError:
339
source_repository = self.open_repository()
340
except errors.NoRepositoryPresent:
341
source_repository = None
343
add_cleanup(source_repository.lock_read().unlock)
345
add_cleanup(source_branch.lock_read().unlock)
346
return source_branch, source_repository
348
def sprout(self, url, revision_id=None, force_new_repo=False,
349
recurse='down', possible_transports=None,
350
accelerator_tree=None, hardlink=False, stacked=False,
351
source_branch=None, create_tree_if_local=True,
353
"""Create a copy of this controldir prepared for use as a new line of
356
If url's last component does not exist, it will be created.
358
Attributes related to the identity of the source branch like
359
branch nickname will be cleaned, a working tree is created
360
whether one existed before or not; and a local branch is always
363
if revision_id is not None, then the clone operation may tune
364
itself to download less data.
366
:param accelerator_tree: A tree which can be used for retrieving file
367
contents more quickly than the revision tree, i.e. a workingtree.
368
The revision tree will be used for cases where accelerator_tree's
369
content is different.
370
:param hardlink: If true, hard-link files from accelerator_tree,
372
:param stacked: If true, create a stacked branch referring to the
373
location of this control directory.
374
:param create_tree_if_local: If true, a working-tree will be created
375
when working locally.
376
:return: The created control directory
378
operation = cleanup.OperationWithCleanups(self._sprout)
379
return operation.run(url, revision_id=revision_id,
380
force_new_repo=force_new_repo, recurse=recurse,
381
possible_transports=possible_transports,
382
accelerator_tree=accelerator_tree, hardlink=hardlink,
383
stacked=stacked, source_branch=source_branch,
384
create_tree_if_local=create_tree_if_local)
386
def _sprout(self, op, url, revision_id=None, force_new_repo=False,
387
recurse='down', possible_transports=None,
388
accelerator_tree=None, hardlink=False, stacked=False,
389
source_branch=None, create_tree_if_local=True, lossy=False):
390
add_cleanup = op.add_cleanup
391
fetch_spec_factory = fetch.FetchSpecFactory()
392
if revision_id is not None:
393
fetch_spec_factory.add_revision_ids([revision_id])
394
fetch_spec_factory.source_branch_stop_revision_id = revision_id
395
if possible_transports is None:
396
possible_transports = []
398
possible_transports = list(possible_transports) + [
400
target_transport = _mod_transport.get_transport(url,
402
target_transport.ensure_base()
403
cloning_format = self.cloning_metadir(stacked)
404
# Create/update the result branch
406
result = controldir.ControlDir.open_from_transport(target_transport)
407
except errors.NotBranchError:
408
result = cloning_format.initialize_on_transport(target_transport)
409
source_branch, source_repository = self._find_source_repo(
410
add_cleanup, source_branch)
411
fetch_spec_factory.source_branch = source_branch
412
# if a stacked branch wasn't requested, we don't create one
413
# even if the origin was stacked
414
if stacked and source_branch is not None:
415
stacked_branch_url = self.root_transport.base
417
stacked_branch_url = None
418
repository_policy = result.determine_repository_policy(
419
force_new_repo, stacked_branch_url, require_stacking=stacked)
420
result_repo, is_new_repo = repository_policy.acquire_repository(
421
possible_transports=possible_transports)
422
add_cleanup(result_repo.lock_write().unlock)
423
fetch_spec_factory.source_repo = source_repository
424
fetch_spec_factory.target_repo = result_repo
425
if stacked or (len(result_repo._fallback_repositories) != 0):
426
target_repo_kind = fetch.TargetRepoKinds.STACKED
428
target_repo_kind = fetch.TargetRepoKinds.EMPTY
430
target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
431
fetch_spec_factory.target_repo_kind = target_repo_kind
432
if source_repository is not None:
433
fetch_spec = fetch_spec_factory.make_fetch_spec()
434
result_repo.fetch(source_repository, fetch_spec=fetch_spec)
436
if source_branch is None:
437
# this is for sprouting a controldir without a branch; is that
439
# Not especially, but it's part of the contract.
440
result_branch = result.create_branch()
442
result_branch = source_branch.sprout(result,
443
revision_id=revision_id, repository_policy=repository_policy,
444
repository=result_repo)
445
mutter("created new branch %r" % (result_branch,))
447
# Create/update the result working tree
448
if (create_tree_if_local and not result.has_workingtree() and
449
isinstance(target_transport, local.LocalTransport) and
450
(result_repo is None or result_repo.make_working_trees())):
451
wt = result.create_workingtree(accelerator_tree=accelerator_tree,
452
hardlink=hardlink, from_branch=result_branch)
455
if not wt.is_versioned(''):
457
wt.set_root_id(self.open_workingtree.get_root_id())
458
except errors.NoWorkingTree:
464
if recurse == 'down':
467
basis = wt.basis_tree()
468
elif result_branch is not None:
469
basis = result_branch.basis_tree()
470
elif source_branch is not None:
471
basis = source_branch.basis_tree()
472
if basis is not None:
473
add_cleanup(basis.lock_read().unlock)
474
subtrees = basis.iter_references()
477
for path, file_id in subtrees:
478
target = urlutils.join(url, urlutils.escape(path))
479
sublocation = source_branch.reference_parent(path, file_id)
480
sublocation.controldir.sprout(target,
481
basis.get_reference_revision(path, file_id),
482
force_new_repo=force_new_repo, recurse=recurse,
486
def _available_backup_name(self, base):
487
"""Find a non-existing backup file name based on base.
489
See breezy.osutils.available_backup_name about race conditions.
491
return osutils.available_backup_name(base, self.root_transport.has)
493
def backup_bzrdir(self):
494
"""Backup this bzr control directory.
496
:return: Tuple with old path name and new path name
499
with ui.ui_factory.nested_progress_bar() as pb:
500
old_path = self.root_transport.abspath('.bzr')
501
backup_dir = self._available_backup_name('backup.bzr')
502
new_path = self.root_transport.abspath(backup_dir)
503
ui.ui_factory.note(gettext('making backup of {0}\n to {1}').format(
504
urlutils.unescape_for_display(old_path, 'utf-8'),
505
urlutils.unescape_for_display(new_path, 'utf-8')))
506
self.root_transport.copy_tree('.bzr', backup_dir)
507
return (old_path, new_path)
509
def retire_bzrdir(self, limit=10000):
510
"""Permanently disable the bzrdir.
512
This is done by renaming it to give the user some ability to recover
513
if there was a problem.
515
This will have horrible consequences if anyone has anything locked or
517
:param limit: number of times to retry
522
to_path = '.bzr.retired.%d' % i
523
self.root_transport.rename('.bzr', to_path)
524
note(gettext("renamed {0} to {1}").format(
525
self.root_transport.abspath('.bzr'), to_path))
527
except (errors.TransportError, IOError, errors.PathError):
534
def _find_containing(self, evaluate):
535
"""Find something in a containing control directory.
537
This method will scan containing control dirs, until it finds what
538
it is looking for, decides that it will never find it, or runs out
539
of containing control directories to check.
541
It is used to implement find_repository and
542
determine_repository_policy.
544
:param evaluate: A function returning (value, stop). If stop is True,
545
the value will be returned.
549
result, stop = evaluate(found_bzrdir)
552
next_transport = found_bzrdir.root_transport.clone('..')
553
if (found_bzrdir.user_url == next_transport.base):
554
# top of the file system
556
# find the next containing bzrdir
558
found_bzrdir = self.open_containing_from_transport(
560
except errors.NotBranchError:
563
def find_repository(self):
564
"""Find the repository that should be used.
566
This does not require a branch as we use it to find the repo for
567
new branches as well as to hook existing branches up to their
570
def usable_repository(found_bzrdir):
571
# does it have a repository ?
573
repository = found_bzrdir.open_repository()
574
except errors.NoRepositoryPresent:
576
if found_bzrdir.user_url == self.user_url:
577
return repository, True
578
elif repository.is_shared():
579
return repository, True
583
found_repo = self._find_containing(usable_repository)
584
if found_repo is None:
585
raise errors.NoRepositoryPresent(self)
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
if (st.st_mode & 0o7777 == 00000):
610
# FTP allows stat but does not return dir/file modes
611
self._dir_mode = None
612
self._file_mode = None
614
self._dir_mode = (st.st_mode & 0o7777) | 0o0700
615
# Remove the sticky and execute bits for files
616
self._file_mode = self._dir_mode & ~0o7111
618
def _get_file_mode(self):
619
"""Return Unix mode for newly created files, or None.
621
if not self._mode_check_done:
622
self._find_creation_modes()
623
return self._file_mode
625
def _get_dir_mode(self):
626
"""Return Unix mode for newly created directories, or None.
628
if not self._mode_check_done:
629
self._find_creation_modes()
630
return self._dir_mode
632
def get_config(self):
633
"""Get configuration for this BzrDir."""
634
return config.BzrDirConfig(self)
636
def _get_config(self):
637
"""By default, no configuration is available."""
640
def __init__(self, _transport, _format):
641
"""Initialize a Bzr control dir object.
643
Only really common logic should reside here, concrete classes should be
644
made with varying behaviours.
646
:param _format: the format that is creating this BzrDir instance.
647
:param _transport: the transport this dir is based at.
649
self._format = _format
650
# these are also under the more standard names of
651
# control_transport and user_transport
652
self.transport = _transport.clone('.bzr')
653
self.root_transport = _transport
654
self._mode_check_done = False
657
def user_transport(self):
658
return self.root_transport
661
def control_transport(self):
662
return self.transport
664
def is_control_filename(self, filename):
665
"""True if filename is the name of a path which is reserved for bzrdir's.
667
:param filename: A filename within the root transport of this bzrdir.
669
This is true IF and ONLY IF the filename is part of the namespace reserved
670
for bzr control dirs. Currently this is the '.bzr' directory in the root
671
of the root_transport.
673
# this might be better on the BzrDirFormat class because it refers to
674
# all the possible bzrdir disk formats.
675
# This method is tested via the workingtree is_control_filename tests-
676
# it was extracted from WorkingTree.is_control_filename. If the method's
677
# contract is extended beyond the current trivial implementation, please
678
# add new tests for it to the appropriate place.
679
return filename == '.bzr' or filename.startswith('.bzr/')
681
def _cloning_metadir(self):
682
"""Produce a metadir suitable for cloning with.
684
:returns: (destination_bzrdir_format, source_repository)
686
result_format = self._format.__class__()
689
branch = self.open_branch(ignore_fallbacks=True)
690
source_repository = branch.repository
691
result_format._branch_format = branch._format
692
except errors.NotBranchError:
694
source_repository = self.open_repository()
695
except errors.NoRepositoryPresent:
696
source_repository = None
698
# XXX TODO: This isinstance is here because we have not implemented
699
# the fix recommended in bug # 103195 - to delegate this choice the
701
repo_format = source_repository._format
702
if isinstance(repo_format, remote.RemoteRepositoryFormat):
703
source_repository._ensure_real()
704
repo_format = source_repository._real_repository._format
705
result_format.repository_format = repo_format
707
# TODO: Couldn't we just probe for the format in these cases,
708
# rather than opening the whole tree? It would be a little
709
# faster. mbp 20070401
710
tree = self.open_workingtree(recommend_upgrade=False)
711
except (errors.NoWorkingTree, errors.NotLocalUrl):
712
result_format.workingtree_format = None
714
result_format.workingtree_format = tree._format.__class__()
715
return result_format, source_repository
717
def cloning_metadir(self, require_stacking=False):
718
"""Produce a metadir suitable for cloning or sprouting with.
720
These operations may produce workingtrees (yes, even though they're
721
"cloning" something that doesn't have a tree), so a viable workingtree
722
format must be selected.
724
:require_stacking: If True, non-stackable formats will be upgraded
725
to similar stackable formats.
726
:returns: a ControlDirFormat with all component formats either set
727
appropriately or set to None if that component should not be
730
format, repository = self._cloning_metadir()
731
if format._workingtree_format is None:
733
if repository is None:
734
# No repository either
736
# We have a repository, so set a working tree? (Why? This seems to
737
# contradict the stated return value in the docstring).
738
tree_format = repository._format._matchingcontroldir.workingtree_format
739
format.workingtree_format = tree_format.__class__()
741
format.require_stacking()
744
def get_branch_transport(self, branch_format, name=None):
745
"""Get the transport for use by branch format in this BzrDir.
747
Note that bzr dirs that do not support format strings will raise
748
IncompatibleFormat if the branch format they are given has
749
a format string, and vice versa.
751
If branch_format is None, the transport is returned with no
752
checking. If it is not None, then the returned transport is
753
guaranteed to point to an existing directory ready for use.
755
raise NotImplementedError(self.get_branch_transport)
757
def get_repository_transport(self, repository_format):
758
"""Get the transport for use by repository format in this BzrDir.
760
Note that bzr dirs that do not support format strings will raise
761
IncompatibleFormat if the repository format they are given has
762
a format string, and vice versa.
764
If repository_format is None, the transport is returned with no
765
checking. If it is not None, then the returned transport is
766
guaranteed to point to an existing directory ready for use.
768
raise NotImplementedError(self.get_repository_transport)
770
def get_workingtree_transport(self, tree_format):
771
"""Get the transport for use by workingtree format in this BzrDir.
773
Note that bzr dirs that do not support format strings will raise
774
IncompatibleFormat if the workingtree format they are given has a
775
format string, and vice versa.
777
If workingtree_format is None, the transport is returned with no
778
checking. If it is not None, then the returned transport is
779
guaranteed to point to an existing directory ready for use.
781
raise NotImplementedError(self.get_workingtree_transport)
784
def create(cls, base, format=None, possible_transports=None):
785
"""Create a new BzrDir at the url 'base'.
787
:param format: If supplied, the format of branch to create. If not
788
supplied, the default is used.
789
:param possible_transports: If supplied, a list of transports that
790
can be reused to share a remote connection.
792
if cls is not BzrDir:
793
raise AssertionError("BzrDir.create always creates the "
794
"default format, not one of %r" % cls)
795
return controldir.ControlDir.create(base, format=format,
796
possible_transports=possible_transports)
799
return "<%s at %r>" % (self.__class__.__name__, self.user_url)
801
def update_feature_flags(self, updated_flags):
802
"""Update the features required by this bzrdir.
804
:param updated_flags: Dictionary mapping feature names to necessities
805
A necessity can be None to indicate the feature should be removed
807
self.control_files.lock_write()
809
self._format._update_feature_flags(updated_flags)
810
self.transport.put_bytes('branch-format', self._format.as_string())
812
self.control_files.unlock()
815
class BzrDirMeta1(BzrDir):
816
"""A .bzr meta version 1 control object.
818
This is the first control object where the
819
individual aspects are really split out: there are separate repository,
820
workingtree and branch subdirectories and any subset of the three can be
821
present within a BzrDir.
824
def _get_branch_path(self, name):
825
"""Obtain the branch path to use.
827
This uses the API specified branch name first, and then falls back to
828
the branch name specified in the URL. If neither of those is specified,
829
it uses the default branch.
831
:param name: Optional branch name to use
832
:return: Relative path to branch
836
return urlutils.join('branches', name.encode("utf-8"))
838
def _read_branch_list(self):
839
"""Read the branch list.
841
:return: List of utf-8 encoded branch names.
844
f = self.control_transport.get('branch-list')
845
except errors.NoSuchFile:
851
ret.append(name.rstrip(b"\n"))
856
def _write_branch_list(self, branches):
857
"""Write out the branch list.
859
:param branches: List of utf-8 branch names to write
861
self.transport.put_bytes('branch-list',
862
"".join([name+"\n" for name in branches]))
864
def __init__(self, _transport, _format):
865
super(BzrDirMeta1, self).__init__(_transport, _format)
866
self.control_files = lockable_files.LockableFiles(
867
self.control_transport, self._format._lock_file_name,
868
self._format._lock_class)
870
def can_convert_format(self):
871
"""See BzrDir.can_convert_format()."""
874
def create_branch(self, name=None, repository=None,
875
append_revisions_only=None):
876
"""See ControlDir.create_branch."""
878
name = self._get_selected_branch()
879
return self._format.get_branch_format().initialize(self, name=name,
880
repository=repository,
881
append_revisions_only=append_revisions_only)
883
def destroy_branch(self, name=None):
884
"""See ControlDir.destroy_branch."""
886
name = self._get_selected_branch()
887
path = self._get_branch_path(name)
889
self.control_files.lock_write()
891
branches = self._read_branch_list()
893
branches.remove(name.encode("utf-8"))
895
raise errors.NotBranchError(name)
896
self._write_branch_list(branches)
898
self.control_files.unlock()
900
self.transport.delete_tree(path)
901
except errors.NoSuchFile:
902
raise errors.NotBranchError(path=urlutils.join(self.transport.base,
903
path), controldir=self)
905
def create_repository(self, shared=False):
906
"""See BzrDir.create_repository."""
907
return self._format.repository_format.initialize(self, shared)
909
def destroy_repository(self):
910
"""See BzrDir.destroy_repository."""
912
self.transport.delete_tree('repository')
913
except errors.NoSuchFile:
914
raise errors.NoRepositoryPresent(self)
916
def create_workingtree(self, revision_id=None, from_branch=None,
917
accelerator_tree=None, hardlink=False):
918
"""See BzrDir.create_workingtree."""
919
return self._format.workingtree_format.initialize(
920
self, revision_id, from_branch=from_branch,
921
accelerator_tree=accelerator_tree, hardlink=hardlink)
923
def destroy_workingtree(self):
924
"""See BzrDir.destroy_workingtree."""
925
wt = self.open_workingtree(recommend_upgrade=False)
926
repository = wt.branch.repository
927
empty = repository.revision_tree(_mod_revision.NULL_REVISION)
928
# We ignore the conflicts returned by wt.revert since we're about to
929
# delete the wt metadata anyway, all that should be left here are
930
# detritus. But see bug #634470 about subtree .bzr dirs.
931
conflicts = wt.revert(old_tree=empty)
932
self.destroy_workingtree_metadata()
934
def destroy_workingtree_metadata(self):
935
self.transport.delete_tree('checkout')
937
def find_branch_format(self, name=None):
938
"""Find the branch 'format' for this bzrdir.
940
This might be a synthetic object for e.g. RemoteBranch and SVN.
942
from .branch import BranchFormatMetadir
943
return BranchFormatMetadir.find_format(self, name=name)
945
def _get_mkdir_mode(self):
946
"""Figure out the mode to use when creating a bzrdir subdir."""
947
temp_control = lockable_files.LockableFiles(self.transport, '',
948
lockable_files.TransportLock)
949
return temp_control._dir_mode
951
def get_branch_reference(self, name=None):
952
"""See BzrDir.get_branch_reference()."""
953
from .branch import BranchFormatMetadir
954
format = BranchFormatMetadir.find_format(self, name=name)
955
return format.get_reference(self, name=name)
957
def set_branch_reference(self, target_branch, name=None):
958
format = _mod_bzrbranch.BranchReferenceFormat()
959
if (self.control_url == target_branch.controldir.control_url and
960
name == target_branch.name):
961
raise controldir.BranchReferenceLoop(target_branch)
962
return format.initialize(self, target_branch=target_branch, name=name)
964
def get_branch_transport(self, branch_format, name=None):
965
"""See BzrDir.get_branch_transport()."""
967
name = self._get_selected_branch()
968
path = self._get_branch_path(name)
969
# XXX: this shouldn't implicitly create the directory if it's just
970
# promising to get a transport -- mbp 20090727
971
if branch_format is None:
972
return self.transport.clone(path)
974
branch_format.get_format_string()
975
except NotImplementedError:
976
raise errors.IncompatibleFormat(branch_format, self._format)
978
branches = self._read_branch_list()
979
utf8_name = name.encode("utf-8")
980
if not utf8_name in branches:
981
self.control_files.lock_write()
983
branches = self._read_branch_list()
984
dirname = urlutils.dirname(utf8_name)
985
if dirname != "" and dirname in branches:
986
raise errors.ParentBranchExists(name)
988
b.startswith(utf8_name+"/") for b in branches]
989
if any(child_branches):
990
raise errors.AlreadyBranchError(name)
991
branches.append(utf8_name)
992
self._write_branch_list(branches)
994
self.control_files.unlock()
995
branch_transport = self.transport.clone(path)
996
mode = self._get_mkdir_mode()
997
branch_transport.create_prefix(mode=mode)
999
self.transport.mkdir(path, mode=mode)
1000
except errors.FileExists:
1002
return self.transport.clone(path)
1004
def get_repository_transport(self, repository_format):
1005
"""See BzrDir.get_repository_transport()."""
1006
if repository_format is None:
1007
return self.transport.clone('repository')
1009
repository_format.get_format_string()
1010
except NotImplementedError:
1011
raise errors.IncompatibleFormat(repository_format, self._format)
1013
self.transport.mkdir('repository', mode=self._get_mkdir_mode())
1014
except errors.FileExists:
1016
return self.transport.clone('repository')
1018
def get_workingtree_transport(self, workingtree_format):
1019
"""See BzrDir.get_workingtree_transport()."""
1020
if workingtree_format is None:
1021
return self.transport.clone('checkout')
1023
workingtree_format.get_format_string()
1024
except NotImplementedError:
1025
raise errors.IncompatibleFormat(workingtree_format, self._format)
1027
self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
1028
except errors.FileExists:
1030
return self.transport.clone('checkout')
1032
def get_branches(self):
1033
"""See ControlDir.get_branches."""
1036
ret[""] = self.open_branch(name="")
1037
except (errors.NotBranchError, errors.NoRepositoryPresent):
1040
for name in self._read_branch_list():
1041
ret[name] = self.open_branch(name=name.decode('utf-8'))
1045
def has_workingtree(self):
1046
"""Tell if this bzrdir contains a working tree.
1048
Note: if you're going to open the working tree, you should just go
1049
ahead and try, and not ask permission first.
1051
from .workingtree import WorkingTreeFormatMetaDir
1053
WorkingTreeFormatMetaDir.find_format_string(self)
1054
except errors.NoWorkingTree:
1058
def needs_format_conversion(self, format):
1059
"""See BzrDir.needs_format_conversion()."""
1060
if (not isinstance(self._format, format.__class__) or
1061
self._format.get_format_string() != format.get_format_string()):
1062
# it is not a meta dir format, conversion is needed.
1064
# we might want to push this down to the repository?
1066
if not isinstance(self.open_repository()._format,
1067
format.repository_format.__class__):
1068
# the repository needs an upgrade.
1070
except errors.NoRepositoryPresent:
1072
for branch in self.list_branches():
1073
if not isinstance(branch._format,
1074
format.get_branch_format().__class__):
1075
# the branch needs an upgrade.
1078
my_wt = self.open_workingtree(recommend_upgrade=False)
1079
if not isinstance(my_wt._format,
1080
format.workingtree_format.__class__):
1081
# the workingtree needs an upgrade.
1083
except (errors.NoWorkingTree, errors.NotLocalUrl):
1087
def open_branch(self, name=None, unsupported=False,
1088
ignore_fallbacks=False, possible_transports=None):
1089
"""See ControlDir.open_branch."""
1091
name = self._get_selected_branch()
1092
format = self.find_branch_format(name=name)
1093
format.check_support_status(unsupported)
1094
return format.open(self, name=name,
1095
_found=True, ignore_fallbacks=ignore_fallbacks,
1096
possible_transports=possible_transports)
1098
def open_repository(self, unsupported=False):
1099
"""See BzrDir.open_repository."""
1100
from .repository import RepositoryFormatMetaDir
1101
format = RepositoryFormatMetaDir.find_format(self)
1102
format.check_support_status(unsupported)
1103
return format.open(self, _found=True)
1105
def open_workingtree(self, unsupported=False,
1106
recommend_upgrade=True):
1107
"""See BzrDir.open_workingtree."""
1108
from .workingtree import WorkingTreeFormatMetaDir
1109
format = WorkingTreeFormatMetaDir.find_format(self)
1110
format.check_support_status(unsupported, recommend_upgrade,
1111
basedir=self.root_transport.base)
1112
return format.open(self, _found=True)
1114
def _get_config(self):
1115
return config.TransportConfig(self.transport, 'control.conf')
1118
class BzrFormat(object):
1119
"""Base class for all formats of things living in metadirs.
1121
This class manages the format string that is stored in the 'format'
1122
or 'branch-format' file.
1124
All classes for (branch-, repository-, workingtree-) formats that
1125
live in meta directories and have their own 'format' file
1126
(i.e. different from .bzr/branch-format) derive from this class,
1127
as well as the relevant base class for their kind
1128
(BranchFormat, WorkingTreeFormat, RepositoryFormat).
1130
Each format is identified by a "format" or "branch-format" file with a
1131
single line containing the base format name and then an optional list of
1134
Feature flags are supported as of bzr 2.5. Setting feature flags on formats
1135
will render them inaccessible to older versions of bzr.
1137
:ivar features: Dictionary mapping feature names to their necessity
1140
_present_features = set()
1146
def register_feature(cls, name):
1147
"""Register a feature as being present.
1149
:param name: Name of the feature
1152
raise ValueError("spaces are not allowed in feature names")
1153
if name in cls._present_features:
1154
raise FeatureAlreadyRegistered(name)
1155
cls._present_features.add(name)
1158
def unregister_feature(cls, name):
1159
"""Unregister a feature."""
1160
cls._present_features.remove(name)
1162
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
1164
for name, necessity in self.features.items():
1165
if name in self._present_features:
1167
if necessity == b"optional":
1168
mutter("ignoring optional missing feature %s", name)
1170
elif necessity == b"required":
1171
raise MissingFeature(name)
1173
mutter("treating unknown necessity as require for %s",
1175
raise MissingFeature(name)
1178
def get_format_string(cls):
1179
"""Return the ASCII format string that identifies this format."""
1180
raise NotImplementedError(cls.get_format_string)
1183
def from_string(cls, text):
1184
format_string = cls.get_format_string()
1185
if not text.startswith(format_string):
1186
raise AssertionError("Invalid format header %r for %r" % (text, cls))
1187
lines = text[len(format_string):].splitlines()
1189
for lineno, line in enumerate(lines):
1191
(necessity, feature) = line.split(b" ", 1)
1193
raise errors.ParseFormatError(format=cls, lineno=lineno+2,
1194
line=line, text=text)
1195
ret.features[feature] = necessity
1198
def as_string(self):
1199
"""Return the string representation of this format.
1201
lines = [self.get_format_string()]
1202
lines.extend([(item[1] + b" " + item[0] + b"\n")
1203
for item in self.features.items()])
1204
return b"".join(lines)
1207
def _find_format(klass, registry, kind, format_string):
1209
first_line = format_string[:format_string.index(b"\n")+1]
1211
first_line = format_string
1213
cls = registry.get(first_line)
1215
raise errors.UnknownFormatError(format=first_line, kind=kind)
1216
return cls.from_string(format_string)
1218
def network_name(self):
1219
"""A simple byte string uniquely identifying this format for RPC calls.
1221
Metadir branch formats use their format string.
1223
return self.as_string()
1225
def __eq__(self, other):
1226
return (self.__class__ is other.__class__ and
1227
self.features == other.features)
1229
def _update_feature_flags(self, updated_flags):
1230
"""Update the feature flags in this format.
1232
:param updated_flags: Updated feature flags
1234
for name, necessity in updated_flags.items():
1235
if necessity is None:
1237
del self.features[name]
1241
self.features[name] = necessity
1244
class BzrDirFormat(BzrFormat, controldir.ControlDirFormat):
1245
"""ControlDirFormat base class for .bzr/ directories.
1247
Formats are placed in a dict by their format string for reference
1248
during bzrdir opening. These should be subclasses of BzrDirFormat
1251
Once a format is deprecated, just deprecate the initialize and open
1252
methods on the format class. Do not deprecate the object, as the
1253
object will be created every system load.
1256
_lock_file_name = 'branch-lock'
1258
# _lock_class must be set in subclasses to the lock type, typ.
1259
# TransportLock or LockDir
1261
def initialize_on_transport(self, transport):
1262
"""Initialize a new bzrdir in the base directory of a Transport."""
1264
# can we hand off the request to the smart server rather than using
1266
client_medium = transport.get_smart_medium()
1267
except errors.NoSmartMedium:
1268
return self._initialize_on_transport_vfs(transport)
1270
# Current RPC's only know how to create bzr metadir1 instances, so
1271
# we still delegate to vfs methods if the requested format is not a
1273
if not isinstance(self, BzrDirMetaFormat1):
1274
return self._initialize_on_transport_vfs(transport)
1275
from .remote import RemoteBzrDirFormat
1276
remote_format = RemoteBzrDirFormat()
1277
self._supply_sub_formats_to(remote_format)
1278
return remote_format.initialize_on_transport(transport)
1280
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
1281
create_prefix=False, force_new_repo=False, stacked_on=None,
1282
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
1283
shared_repo=False, vfs_only=False):
1284
"""Create this format on transport.
1286
The directory to initialize will be created.
1288
:param force_new_repo: Do not use a shared repository for the target,
1289
even if one is available.
1290
:param create_prefix: Create any missing directories leading up to
1292
:param use_existing_dir: Use an existing directory if one exists.
1293
:param stacked_on: A url to stack any created branch on, None to follow
1294
any target stacking policy.
1295
:param stack_on_pwd: If stack_on is relative, the location it is
1297
:param repo_format_name: If non-None, a repository will be
1298
made-or-found. Should none be found, or if force_new_repo is True
1299
the repo_format_name is used to select the format of repository to
1301
:param make_working_trees: Control the setting of make_working_trees
1302
for a new shared repository when one is made. None to use whatever
1303
default the format has.
1304
:param shared_repo: Control whether made repositories are shared or
1306
:param vfs_only: If True do not attempt to use a smart server
1307
:return: repo, controldir, require_stacking, repository_policy. repo is
1308
None if none was created or found, bzrdir is always valid.
1309
require_stacking is the result of examining the stacked_on
1310
parameter and any stacking policy found for the target.
1313
# Try to hand off to a smart server
1315
client_medium = transport.get_smart_medium()
1316
except errors.NoSmartMedium:
1319
from .remote import RemoteBzrDirFormat
1320
# TODO: lookup the local format from a server hint.
1321
remote_dir_format = RemoteBzrDirFormat()
1322
remote_dir_format._network_name = self.network_name()
1323
self._supply_sub_formats_to(remote_dir_format)
1324
return remote_dir_format.initialize_on_transport_ex(transport,
1325
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
1326
force_new_repo=force_new_repo, stacked_on=stacked_on,
1327
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
1328
make_working_trees=make_working_trees, shared_repo=shared_repo)
1329
# XXX: Refactor the create_prefix/no_create_prefix code into a
1330
# common helper function
1331
# The destination may not exist - if so make it according to policy.
1332
def make_directory(transport):
1333
transport.mkdir('.')
1335
def redirected(transport, e, redirection_notice):
1336
note(redirection_notice)
1337
return transport._redirected_to(e.source, e.target)
1339
transport = do_catching_redirections(make_directory, transport,
1341
except errors.FileExists:
1342
if not use_existing_dir:
1344
except errors.NoSuchFile:
1345
if not create_prefix:
1347
transport.create_prefix()
1349
require_stacking = (stacked_on is not None)
1350
# Now the target directory exists, but doesn't have a .bzr
1351
# directory. So we need to create it, along with any work to create
1352
# all of the dependent branches, etc.
1354
result = self.initialize_on_transport(transport)
1355
if repo_format_name:
1357
# use a custom format
1358
result._format.repository_format = \
1359
repository.network_format_registry.get(repo_format_name)
1360
except AttributeError:
1361
# The format didn't permit it to be set.
1363
# A repository is desired, either in-place or shared.
1364
repository_policy = result.determine_repository_policy(
1365
force_new_repo, stacked_on, stack_on_pwd,
1366
require_stacking=require_stacking)
1367
result_repo, is_new_repo = repository_policy.acquire_repository(
1368
make_working_trees, shared_repo)
1369
if not require_stacking and repository_policy._require_stacking:
1370
require_stacking = True
1371
result._format.require_stacking()
1372
result_repo.lock_write()
1375
repository_policy = None
1376
return result_repo, result, require_stacking, repository_policy
1378
def _initialize_on_transport_vfs(self, transport):
1379
"""Initialize a new bzrdir using VFS calls.
1381
:param transport: The transport to create the .bzr directory in.
1384
# Since we are creating a .bzr directory, inherit the
1385
# mode from the root directory
1386
temp_control = lockable_files.LockableFiles(transport,
1387
'', lockable_files.TransportLock)
1389
temp_control._transport.mkdir('.bzr',
1390
# FIXME: RBC 20060121 don't peek under
1392
mode=temp_control._dir_mode)
1393
except errors.FileExists:
1394
raise errors.AlreadyControlDirError(transport.base)
1395
if sys.platform == 'win32' and isinstance(transport, local.LocalTransport):
1396
win32utils.set_file_attr_hidden(transport._abspath('.bzr'))
1397
file_mode = temp_control._file_mode
1399
bzrdir_transport = transport.clone('.bzr')
1400
utf8_files = [('README',
1401
b"This is a Bazaar control directory.\n"
1402
b"Do not change any files in this directory.\n"
1403
b"See http://bazaar.canonical.com/ for more information about Bazaar.\n"),
1404
('branch-format', self.as_string()),
1406
# NB: no need to escape relative paths that are url safe.
1407
control_files = lockable_files.LockableFiles(bzrdir_transport,
1408
self._lock_file_name, self._lock_class)
1409
control_files.create_lock()
1410
control_files.lock_write()
1412
for (filename, content) in utf8_files:
1413
bzrdir_transport.put_bytes(filename, content,
1416
control_files.unlock()
1417
return self.open(transport, _found=True)
1419
def open(self, transport, _found=False):
1420
"""Return an instance of this format for the dir transport points at.
1422
_found is a private parameter, do not use it.
1425
found_format = controldir.ControlDirFormat.find_format(transport)
1426
if not isinstance(found_format, self.__class__):
1427
raise AssertionError("%s was asked to open %s, but it seems to need "
1429
% (self, transport, found_format))
1430
# Allow subclasses - use the found format.
1431
self._supply_sub_formats_to(found_format)
1432
return found_format._open(transport)
1433
return self._open(transport)
1435
def _open(self, transport):
1436
"""Template method helper for opening BzrDirectories.
1438
This performs the actual open and any additional logic or parameter
1441
raise NotImplementedError(self._open)
1443
def _supply_sub_formats_to(self, other_format):
1444
"""Give other_format the same values for sub formats as this has.
1446
This method is expected to be used when parameterising a
1447
RemoteBzrDirFormat instance with the parameters from a
1448
BzrDirMetaFormat1 instance.
1450
:param other_format: other_format is a format which should be
1451
compatible with whatever sub formats are supported by self.
1454
other_format.features = dict(self.features)
1456
def supports_transport(self, transport):
1457
# bzr formats can be opened over all known transports
1460
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
1462
controldir.ControlDirFormat.check_support_status(self,
1463
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
1465
BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
1466
recommend_upgrade=recommend_upgrade, basedir=basedir)
1469
class BzrDirMetaFormat1(BzrDirFormat):
1470
"""Bzr meta control format 1
1472
This is the first format with split out working tree, branch and repository
1477
- Format 3 working trees [optional]
1478
- Format 5 branches [optional]
1479
- Format 7 repositories [optional]
1482
_lock_class = lockdir.LockDir
1484
fixed_components = False
1486
colocated_branches = True
1489
BzrDirFormat.__init__(self)
1490
self._workingtree_format = None
1491
self._branch_format = None
1492
self._repository_format = None
1494
def __eq__(self, other):
1495
if other.__class__ is not self.__class__:
1497
if other.repository_format != self.repository_format:
1499
if other.workingtree_format != self.workingtree_format:
1501
if other.features != self.features:
1505
def __ne__(self, other):
1506
return not self == other
1508
def get_branch_format(self):
1509
if self._branch_format is None:
1510
from .branch import format_registry as branch_format_registry
1511
self._branch_format = branch_format_registry.get_default()
1512
return self._branch_format
1514
def set_branch_format(self, format):
1515
self._branch_format = format
1517
def require_stacking(self, stack_on=None, possible_transports=None,
1519
"""We have a request to stack, try to ensure the formats support it.
1521
:param stack_on: If supplied, it is the URL to a branch that we want to
1522
stack on. Check to see if that format supports stacking before
1525
# Stacking is desired. requested by the target, but does the place it
1526
# points at support stacking? If it doesn't then we should
1527
# not implicitly upgrade. We check this here.
1528
new_repo_format = None
1529
new_branch_format = None
1531
# a bit of state for get_target_branch so that we don't try to open it
1532
# 2 times, for both repo *and* branch
1533
target = [None, False, None] # target_branch, checked, upgrade anyway
1534
def get_target_branch():
1536
# We've checked, don't check again
1538
if stack_on is None:
1539
# No target format, that means we want to force upgrading
1540
target[:] = [None, True, True]
1543
target_dir = BzrDir.open(stack_on,
1544
possible_transports=possible_transports)
1545
except errors.NotBranchError:
1546
# Nothing there, don't change formats
1547
target[:] = [None, True, False]
1549
except errors.JailBreak:
1550
# JailBreak, JFDI and upgrade anyway
1551
target[:] = [None, True, True]
1554
target_branch = target_dir.open_branch()
1555
except errors.NotBranchError:
1556
# No branch, don't upgrade formats
1557
target[:] = [None, True, False]
1559
target[:] = [target_branch, True, False]
1562
if (not _skip_repo and
1563
not self.repository_format.supports_external_lookups):
1564
# We need to upgrade the Repository.
1565
target_branch, _, do_upgrade = get_target_branch()
1566
if target_branch is None:
1567
# We don't have a target branch, should we upgrade anyway?
1569
# stack_on is inaccessible, JFDI.
1570
# TODO: bad monkey, hard-coded formats...
1571
if self.repository_format.rich_root_data:
1572
new_repo_format = knitpack_repo.RepositoryFormatKnitPack5RichRoot()
1574
new_repo_format = knitpack_repo.RepositoryFormatKnitPack5()
1576
# If the target already supports stacking, then we know the
1577
# project is already able to use stacking, so auto-upgrade
1579
new_repo_format = target_branch.repository._format
1580
if not new_repo_format.supports_external_lookups:
1581
# target doesn't, source doesn't, so don't auto upgrade
1583
new_repo_format = None
1584
if new_repo_format is not None:
1585
self.repository_format = new_repo_format
1586
note(gettext('Source repository format does not support stacking,'
1587
' using format:\n %s'),
1588
new_repo_format.get_format_description())
1590
if not self.get_branch_format().supports_stacking():
1591
# We just checked the repo, now lets check if we need to
1592
# upgrade the branch format
1593
target_branch, _, do_upgrade = get_target_branch()
1594
if target_branch is None:
1596
# TODO: bad monkey, hard-coded formats...
1597
from .branch import BzrBranchFormat7
1598
new_branch_format = BzrBranchFormat7()
1600
new_branch_format = target_branch._format
1601
if not new_branch_format.supports_stacking():
1602
new_branch_format = None
1603
if new_branch_format is not None:
1604
# Does support stacking, use its format.
1605
self.set_branch_format(new_branch_format)
1606
note(gettext('Source branch format does not support stacking,'
1607
' using format:\n %s'),
1608
new_branch_format.get_format_description())
1610
def get_converter(self, format=None):
1611
"""See BzrDirFormat.get_converter()."""
1613
format = BzrDirFormat.get_default_format()
1614
if (isinstance(self, BzrDirMetaFormat1) and
1615
isinstance(format, BzrDirMetaFormat1Colo)):
1616
return ConvertMetaToColo(format)
1617
if (isinstance(self, BzrDirMetaFormat1Colo) and
1618
isinstance(format, BzrDirMetaFormat1)):
1619
return ConvertMetaToColo(format)
1620
if not isinstance(self, format.__class__):
1621
# converting away from metadir is not implemented
1622
raise NotImplementedError(self.get_converter)
1623
return ConvertMetaToMeta(format)
1626
def get_format_string(cls):
1627
"""See BzrDirFormat.get_format_string()."""
1628
return b"Bazaar-NG meta directory, format 1\n"
1630
def get_format_description(self):
1631
"""See BzrDirFormat.get_format_description()."""
1632
return "Meta directory format 1"
1634
def _open(self, transport):
1635
"""See BzrDirFormat._open."""
1636
# Create a new format instance because otherwise initialisation of new
1637
# metadirs share the global default format object leading to alias
1639
format = BzrDirMetaFormat1()
1640
self._supply_sub_formats_to(format)
1641
return BzrDirMeta1(transport, format)
1643
def __return_repository_format(self):
1644
"""Circular import protection."""
1645
if self._repository_format:
1646
return self._repository_format
1647
from .repository import format_registry
1648
return format_registry.get_default()
1650
def _set_repository_format(self, value):
1651
"""Allow changing the repository format for metadir formats."""
1652
self._repository_format = value
1654
repository_format = property(__return_repository_format,
1655
_set_repository_format)
1657
def _supply_sub_formats_to(self, other_format):
1658
"""Give other_format the same values for sub formats as this has.
1660
This method is expected to be used when parameterising a
1661
RemoteBzrDirFormat instance with the parameters from a
1662
BzrDirMetaFormat1 instance.
1664
:param other_format: other_format is a format which should be
1665
compatible with whatever sub formats are supported by self.
1668
super(BzrDirMetaFormat1, self)._supply_sub_formats_to(other_format)
1669
if getattr(self, '_repository_format', None) is not None:
1670
other_format.repository_format = self.repository_format
1671
if self._branch_format is not None:
1672
other_format._branch_format = self._branch_format
1673
if self._workingtree_format is not None:
1674
other_format.workingtree_format = self.workingtree_format
1676
def __get_workingtree_format(self):
1677
if self._workingtree_format is None:
1678
from .workingtree import (
1679
format_registry as wt_format_registry,
1681
self._workingtree_format = wt_format_registry.get_default()
1682
return self._workingtree_format
1684
def __set_workingtree_format(self, wt_format):
1685
self._workingtree_format = wt_format
1688
return "<%r>" % (self.__class__.__name__,)
1690
workingtree_format = property(__get_workingtree_format,
1691
__set_workingtree_format)
1694
class BzrDirMetaFormat1Colo(BzrDirMetaFormat1):
1695
"""BzrDirMeta1 format with support for colocated branches."""
1697
colocated_branches = True
1700
def get_format_string(cls):
1701
"""See BzrDirFormat.get_format_string()."""
1702
return b"Bazaar meta directory, format 1 (with colocated branches)\n"
1704
def get_format_description(self):
1705
"""See BzrDirFormat.get_format_description()."""
1706
return "Meta directory format 1 with support for colocated branches"
1708
def _open(self, transport):
1709
"""See BzrDirFormat._open."""
1710
# Create a new format instance because otherwise initialisation of new
1711
# metadirs share the global default format object leading to alias
1713
format = BzrDirMetaFormat1Colo()
1714
self._supply_sub_formats_to(format)
1715
return BzrDirMeta1(transport, format)
1718
class ConvertMetaToMeta(controldir.Converter):
1719
"""Converts the components of metadirs."""
1721
def __init__(self, target_format):
1722
"""Create a metadir to metadir converter.
1724
:param target_format: The final metadir format that is desired.
1726
self.target_format = target_format
1728
def convert(self, to_convert, pb):
1729
"""See Converter.convert()."""
1730
self.controldir = to_convert
1731
self.pb = ui.ui_factory.nested_progress_bar()
1734
self.step('checking repository format')
1736
repo = self.controldir.open_repository()
1737
except errors.NoRepositoryPresent:
1740
repo_fmt = self.target_format.repository_format
1741
if not isinstance(repo._format, repo_fmt.__class__):
1742
from ..repository import CopyConverter
1743
ui.ui_factory.note(gettext('starting repository conversion'))
1744
if not repo_fmt.supports_overriding_transport:
1745
raise AssertionError(
1746
"Repository in metadir does not support "
1747
"overriding transport")
1748
converter = CopyConverter(self.target_format.repository_format)
1749
converter.convert(repo, pb)
1750
for branch in self.controldir.list_branches():
1751
# TODO: conversions of Branch and Tree should be done by
1752
# InterXFormat lookups/some sort of registry.
1753
# Avoid circular imports
1754
old = branch._format.__class__
1755
new = self.target_format.get_branch_format().__class__
1757
if (old == fullhistorybranch.BzrBranchFormat5 and
1758
new in (_mod_bzrbranch.BzrBranchFormat6,
1759
_mod_bzrbranch.BzrBranchFormat7,
1760
_mod_bzrbranch.BzrBranchFormat8)):
1761
branch_converter = _mod_bzrbranch.Converter5to6()
1762
elif (old == _mod_bzrbranch.BzrBranchFormat6 and
1763
new in (_mod_bzrbranch.BzrBranchFormat7,
1764
_mod_bzrbranch.BzrBranchFormat8)):
1765
branch_converter = _mod_bzrbranch.Converter6to7()
1766
elif (old == _mod_bzrbranch.BzrBranchFormat7 and
1767
new is _mod_bzrbranch.BzrBranchFormat8):
1768
branch_converter = _mod_bzrbranch.Converter7to8()
1770
raise errors.BadConversionTarget("No converter", new,
1772
branch_converter.convert(branch)
1773
branch = self.controldir.open_branch()
1774
old = branch._format.__class__
1776
tree = self.controldir.open_workingtree(recommend_upgrade=False)
1777
except (errors.NoWorkingTree, errors.NotLocalUrl):
1780
# TODO: conversions of Branch and Tree should be done by
1781
# InterXFormat lookups
1782
if (isinstance(tree, workingtree_3.WorkingTree3) and
1783
not isinstance(tree, workingtree_4.DirStateWorkingTree) and
1784
isinstance(self.target_format.workingtree_format,
1785
workingtree_4.DirStateWorkingTreeFormat)):
1786
workingtree_4.Converter3to4().convert(tree)
1787
if (isinstance(tree, workingtree_4.DirStateWorkingTree) and
1788
not isinstance(tree, workingtree_4.WorkingTree5) and
1789
isinstance(self.target_format.workingtree_format,
1790
workingtree_4.WorkingTreeFormat5)):
1791
workingtree_4.Converter4to5().convert(tree)
1792
if (isinstance(tree, workingtree_4.DirStateWorkingTree) and
1793
not isinstance(tree, workingtree_4.WorkingTree6) and
1794
isinstance(self.target_format.workingtree_format,
1795
workingtree_4.WorkingTreeFormat6)):
1796
workingtree_4.Converter4or5to6().convert(tree)
1801
class ConvertMetaToColo(controldir.Converter):
1802
"""Add colocated branch support."""
1804
def __init__(self, target_format):
1805
"""Create a converter.that upgrades a metadir to the colo format.
1807
:param target_format: The final metadir format that is desired.
1809
self.target_format = target_format
1811
def convert(self, to_convert, pb):
1812
"""See Converter.convert()."""
1813
to_convert.transport.put_bytes('branch-format',
1814
self.target_format.as_string())
1815
return BzrDir.open_from_transport(to_convert.root_transport)
1818
class ConvertMetaToColo(controldir.Converter):
1819
"""Convert a 'development-colo' bzrdir to a '2a' bzrdir."""
1821
def __init__(self, target_format):
1822
"""Create a converter that converts a 'development-colo' metadir
1825
:param target_format: The final metadir format that is desired.
1827
self.target_format = target_format
1829
def convert(self, to_convert, pb):
1830
"""See Converter.convert()."""
1831
to_convert.transport.put_bytes('branch-format',
1832
self.target_format.as_string())
1833
return BzrDir.open_from_transport(to_convert.root_transport)
1836
class CreateRepository(controldir.RepositoryAcquisitionPolicy):
1837
"""A policy of creating a new repository"""
1839
def __init__(self, controldir, stack_on=None, stack_on_pwd=None,
1840
require_stacking=False):
1843
:param controldir: The controldir to create the repository on.
1844
:param stack_on: A location to stack on
1845
:param stack_on_pwd: If stack_on is relative, the location it is
1848
super(CreateRepository, self).__init__(
1849
stack_on, stack_on_pwd, require_stacking)
1850
self._controldir = controldir
1852
def acquire_repository(self, make_working_trees=None, shared=False,
1853
possible_transports=None):
1854
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
1856
Creates the desired repository in the controldir we already have.
1858
if possible_transports is None:
1859
possible_transports = []
1861
possible_transports = list(possible_transports)
1862
possible_transports.append(self._controldir.root_transport)
1863
stack_on = self._get_full_stack_on()
1865
format = self._controldir._format
1866
format.require_stacking(stack_on=stack_on,
1867
possible_transports=possible_transports)
1868
if not self._require_stacking:
1869
# We have picked up automatic stacking somewhere.
1870
note(gettext('Using default stacking branch {0} at {1}').format(
1871
self._stack_on, self._stack_on_pwd))
1872
repository = self._controldir.create_repository(shared=shared)
1873
self._add_fallback(repository,
1874
possible_transports=possible_transports)
1875
if make_working_trees is not None:
1876
repository.set_make_working_trees(make_working_trees)
1877
return repository, True
1880
class UseExistingRepository(controldir.RepositoryAcquisitionPolicy):
1881
"""A policy of reusing an existing repository"""
1883
def __init__(self, repository, stack_on=None, stack_on_pwd=None,
1884
require_stacking=False):
1887
:param repository: The repository to use.
1888
:param stack_on: A location to stack on
1889
:param stack_on_pwd: If stack_on is relative, the location it is
1892
super(UseExistingRepository, self).__init__(
1893
stack_on, stack_on_pwd, require_stacking)
1894
self._repository = repository
1896
def acquire_repository(self, make_working_trees=None, shared=False,
1897
possible_transports=None):
1898
"""Implementation of RepositoryAcquisitionPolicy.acquire_repository
1900
Returns an existing repository to use.
1902
if possible_transports is None:
1903
possible_transports = []
1905
possible_transports = list(possible_transports)
1906
possible_transports.append(self._repository.controldir.transport)
1907
self._add_fallback(self._repository,
1908
possible_transports=possible_transports)
1909
return self._repository, False
1912
controldir.ControlDirFormat._default_format = BzrDirMetaFormat1()