/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/bzr/bzrdir.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2011 Canonical Ltd
 
2
#
 
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.
 
7
#
 
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.
 
12
#
 
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
 
16
 
 
17
"""BzrDir logic. The BzrDir is the basic control directory used by bzr.
 
18
 
 
19
At format 7 this was split out into Branch, Repository and Checkout control
 
20
directories.
 
21
 
 
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
 
25
objects returned.
 
26
"""
 
27
 
 
28
from __future__ import absolute_import
 
29
 
 
30
import sys
 
31
 
 
32
from ..lazy_import import lazy_import
 
33
lazy_import(globals(), """
 
34
import breezy
 
35
from breezy import (
 
36
    branch as _mod_branch,
 
37
    cleanup,
 
38
    fetch,
 
39
    graph,
 
40
    lockable_files,
 
41
    lockdir,
 
42
    osutils,
 
43
    pyutils,
 
44
    repository,
 
45
    revision as _mod_revision,
 
46
    transport as _mod_transport,
 
47
    ui,
 
48
    urlutils,
 
49
    win32utils,
 
50
    )
 
51
from breezy.bzr import (
 
52
    branch as _mod_bzrbranch,
 
53
    remote,
 
54
    vf_search,
 
55
    workingtree_3,
 
56
    workingtree_4,
 
57
    )
 
58
from breezy.bzr import fullhistory as fullhistorybranch
 
59
from breezy.bzr import knitpack_repo
 
60
from breezy.transport import (
 
61
    do_catching_redirections,
 
62
    local,
 
63
    )
 
64
from breezy.i18n import gettext
 
65
""")
 
66
 
 
67
from ..trace import (
 
68
    mutter,
 
69
    note,
 
70
    )
 
71
 
 
72
from .. import (
 
73
    config,
 
74
    controldir,
 
75
    errors,
 
76
    registry,
 
77
    )
 
78
 
 
79
 
 
80
class MissingFeature(errors.BzrError):
 
81
 
 
82
    _fmt = ("Missing feature %(feature)s not provided by this "
 
83
            "version of Bazaar or any plugin.")
 
84
 
 
85
    def __init__(self, feature):
 
86
        self.feature = feature
 
87
 
 
88
 
 
89
class FeatureAlreadyRegistered(errors.BzrError):
 
90
 
 
91
    _fmt = 'The feature %(feature)s has already been registered.'
 
92
 
 
93
    def __init__(self, feature):
 
94
        self.feature = feature
 
95
 
 
96
 
 
97
class BzrDir(controldir.ControlDir):
 
98
    """A .bzr control diretory.
 
99
 
 
100
    BzrDir instances let you create or open any of the things that can be
 
101
    found within .bzr - checkouts, branches and repositories.
 
102
 
 
103
    :ivar transport:
 
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).
 
108
 
 
109
    Everything in the bzrdir should have the same file permissions.
 
110
 
 
111
    :cvar hooks: An instance of BzrDirHooks.
 
112
    """
 
113
 
 
114
    def break_lock(self):
 
115
        """Invoke break_lock on the first object in the bzrdir.
 
116
 
 
117
        If there is a tree, the tree is opened and break_lock() called.
 
118
        Otherwise, branch is tried, and finally repository.
 
119
        """
 
120
        # XXX: This seems more like a UI function than something that really
 
121
        # belongs in this class.
 
122
        try:
 
123
            thing_to_unlock = self.open_workingtree()
 
124
        except (errors.NotLocalUrl, errors.NoWorkingTree):
 
125
            try:
 
126
                thing_to_unlock = self.open_branch()
 
127
            except errors.NotBranchError:
 
128
                try:
 
129
                    thing_to_unlock = self.open_repository()
 
130
                except errors.NoRepositoryPresent:
 
131
                    return
 
132
        thing_to_unlock.break_lock()
 
133
 
 
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
 
139
        try:
 
140
            self.open_repository()._format.check_conversion_target(
 
141
                target_repo_format)
 
142
        except errors.NoRepositoryPresent:
 
143
            # No repo, no problem.
 
144
            pass
 
145
 
 
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.
 
150
 
 
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
 
161
            to_transport.
 
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.
 
164
        """
 
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.
 
167
 
 
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)
 
172
 
 
173
        # Figure out what objects we want:
 
174
        try:
 
175
            local_repo = self.find_repository()
 
176
        except errors.NoRepositoryPresent:
 
177
            local_repo = None
 
178
        try:
 
179
            local_branch = self.open_branch()
 
180
        except errors.NotBranchError:
 
181
            local_branch = None
 
182
        else:
 
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:
 
187
                try:
 
188
                    stacked_on = local_branch.get_stacked_on_url()
 
189
                except (_mod_branch.UnstackableBranchFormat,
 
190
                        errors.UnstackableRepositoryFormat,
 
191
                        errors.NotStacked):
 
192
                    pass
 
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,
 
195
        # or something.
 
196
        if local_repo:
 
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()
 
200
        else:
 
201
            make_working_trees = False
 
202
            want_shared = False
 
203
            repo_format_name = None
 
204
 
 
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)
 
212
        if repo_format_name:
 
213
            try:
 
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)
 
226
                else:
 
227
                    result_repo.fetch(local_repo, revision_id=revision_id)
 
228
            finally:
 
229
                result_repo.unlock()
 
230
        else:
 
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
 
235
        #   clone it.
 
236
        if local_branch is not None:
 
237
            result_branch = local_branch.clone(result, revision_id=revision_id,
 
238
                repository_policy=repository_policy)
 
239
        try:
 
240
            # Cheaper to check if the target is not local, than to try making
 
241
            # the tree and fail.
 
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):
 
246
            pass
 
247
        return result
 
248
 
 
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)
 
253
        t.ensure_base()
 
254
 
 
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.
 
258
 
 
259
        This controls whether a new repository is created, and the format of
 
260
        that repository, or some existing shared repository used instead.
 
261
 
 
262
        If stack_on is supplied, will not seek a containing shared repo.
 
263
 
 
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
 
268
            relative to.
 
269
        """
 
270
        def repository_policy(found_bzrdir):
 
271
            stack_on = None
 
272
            stack_on_pwd = None
 
273
            config = found_bzrdir.get_config()
 
274
            stop = False
 
275
            stack_on = config.get_default_stack_on()
 
276
            if stack_on is not None:
 
277
                stack_on_pwd = found_bzrdir.user_url
 
278
                stop = True
 
279
            # does it have a repository ?
 
280
            try:
 
281
                repository = found_bzrdir.open_repository()
 
282
            except errors.NoRepositoryPresent:
 
283
                repository = None
 
284
            else:
 
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.
 
288
                    repository = None
 
289
                    stop = True
 
290
                else:
 
291
                    stop = True
 
292
            if not stop:
 
293
                return None, False
 
294
            if repository:
 
295
                return UseExistingRepository(repository, stack_on,
 
296
                    stack_on_pwd, require_stacking=require_stacking), True
 
297
            else:
 
298
                return CreateRepository(self, stack_on, stack_on_pwd,
 
299
                    require_stacking=require_stacking), True
 
300
 
 
301
        if not force_new_repo:
 
302
            if stack_on is None:
 
303
                policy = self._find_containing(repository_policy)
 
304
                if policy is not None:
 
305
                    return policy
 
306
            else:
 
307
                try:
 
308
                    return UseExistingRepository(
 
309
                            self.open_repository(), stack_on, stack_on_pwd,
 
310
                            require_stacking=require_stacking)
 
311
                except errors.NoRepositoryPresent:
 
312
                    pass
 
313
        return CreateRepository(self, stack_on, stack_on_pwd,
 
314
                                require_stacking=require_stacking)
 
315
 
 
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]
 
320
 
 
321
    def _find_source_repo(self, add_cleanup, source_branch):
 
322
        """Find the source branch and repo for a sprout operation.
 
323
        
 
324
        This is helper intended for use by _sprout.
 
325
 
 
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).
 
329
        """
 
330
        if source_branch is not None:
 
331
            add_cleanup(source_branch.lock_read().unlock)
 
332
            return source_branch, source_branch.repository
 
333
        try:
 
334
            source_branch = self.open_branch()
 
335
            source_repository = source_branch.repository
 
336
        except errors.NotBranchError:
 
337
            source_branch = None
 
338
            try:
 
339
                source_repository = self.open_repository()
 
340
            except errors.NoRepositoryPresent:
 
341
                source_repository = None
 
342
            else:
 
343
                add_cleanup(source_repository.lock_read().unlock)
 
344
        else:
 
345
            add_cleanup(source_branch.lock_read().unlock)
 
346
        return source_branch, source_repository
 
347
 
 
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,
 
352
               lossy=False):
 
353
        """Create a copy of this controldir prepared for use as a new line of
 
354
        development.
 
355
 
 
356
        If url's last component does not exist, it will be created.
 
357
 
 
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
 
361
        created.
 
362
 
 
363
        if revision_id is not None, then the clone operation may tune
 
364
            itself to download less data.
 
365
 
 
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,
 
371
            where possible.
 
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
 
377
        """
 
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)
 
385
 
 
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 = []
 
397
        else:
 
398
            possible_transports = list(possible_transports) + [
 
399
                self.root_transport]
 
400
        target_transport = _mod_transport.get_transport(url,
 
401
            possible_transports)
 
402
        target_transport.ensure_base()
 
403
        cloning_format = self.cloning_metadir(stacked)
 
404
        # Create/update the result branch
 
405
        try:
 
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
 
416
        else:
 
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
 
427
        elif is_new_repo:
 
428
            target_repo_kind = fetch.TargetRepoKinds.EMPTY
 
429
        else:
 
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)
 
435
 
 
436
        if source_branch is None:
 
437
            # this is for sprouting a controldir without a branch; is that
 
438
            # actually useful?
 
439
            # Not especially, but it's part of the contract.
 
440
            result_branch = result.create_branch()
 
441
        else:
 
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,))
 
446
 
 
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)
 
453
            wt.lock_write()
 
454
            try:
 
455
                if not wt.is_versioned(''):
 
456
                    try:
 
457
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
458
                    except errors.NoWorkingTree:
 
459
                        pass
 
460
            finally:
 
461
                wt.unlock()
 
462
        else:
 
463
            wt = None
 
464
        if recurse == 'down':
 
465
            basis = None
 
466
            if wt is not None:
 
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()
 
475
            else:
 
476
                subtrees = []
 
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,
 
483
                    stacked=stacked)
 
484
        return result
 
485
 
 
486
    def _available_backup_name(self, base):
 
487
        """Find a non-existing backup file name based on base.
 
488
 
 
489
        See breezy.osutils.available_backup_name about race conditions.
 
490
        """
 
491
        return osutils.available_backup_name(base, self.root_transport.has)
 
492
 
 
493
    def backup_bzrdir(self):
 
494
        """Backup this bzr control directory.
 
495
 
 
496
        :return: Tuple with old path name and new path name
 
497
        """
 
498
 
 
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)
 
508
 
 
509
    def retire_bzrdir(self, limit=10000):
 
510
        """Permanently disable the bzrdir.
 
511
 
 
512
        This is done by renaming it to give the user some ability to recover
 
513
        if there was a problem.
 
514
 
 
515
        This will have horrible consequences if anyone has anything locked or
 
516
        in use.
 
517
        :param limit: number of times to retry
 
518
        """
 
519
        i  = 0
 
520
        while True:
 
521
            try:
 
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))
 
526
                return
 
527
            except (errors.TransportError, IOError, errors.PathError):
 
528
                i += 1
 
529
                if i > limit:
 
530
                    raise
 
531
                else:
 
532
                    pass
 
533
 
 
534
    def _find_containing(self, evaluate):
 
535
        """Find something in a containing control directory.
 
536
 
 
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.
 
540
 
 
541
        It is used to implement find_repository and
 
542
        determine_repository_policy.
 
543
 
 
544
        :param evaluate: A function returning (value, stop).  If stop is True,
 
545
            the value will be returned.
 
546
        """
 
547
        found_bzrdir = self
 
548
        while True:
 
549
            result, stop = evaluate(found_bzrdir)
 
550
            if stop:
 
551
                return result
 
552
            next_transport = found_bzrdir.root_transport.clone('..')
 
553
            if (found_bzrdir.user_url == next_transport.base):
 
554
                # top of the file system
 
555
                return None
 
556
            # find the next containing bzrdir
 
557
            try:
 
558
                found_bzrdir = self.open_containing_from_transport(
 
559
                    next_transport)[0]
 
560
            except errors.NotBranchError:
 
561
                return None
 
562
 
 
563
    def find_repository(self):
 
564
        """Find the repository that should be used.
 
565
 
 
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
 
568
        repository.
 
569
        """
 
570
        def usable_repository(found_bzrdir):
 
571
            # does it have a repository ?
 
572
            try:
 
573
                repository = found_bzrdir.open_repository()
 
574
            except errors.NoRepositoryPresent:
 
575
                return None, False
 
576
            if found_bzrdir.user_url == self.user_url:
 
577
                return repository, True
 
578
            elif repository.is_shared():
 
579
                return repository, True
 
580
            else:
 
581
                return None, True
 
582
 
 
583
        found_repo = self._find_containing(usable_repository)
 
584
        if found_repo is None:
 
585
            raise errors.NoRepositoryPresent(self)
 
586
        return found_repo
 
587
 
 
588
    def _find_creation_modes(self):
 
589
        """Determine the appropriate modes for files and directories.
 
590
 
 
591
        They're always set to be consistent with the base directory,
 
592
        assuming that this transport allows setting modes.
 
593
        """
 
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:
 
597
            return
 
598
        self._mode_check_done = True
 
599
        try:
 
600
            st = self.transport.stat('.')
 
601
        except errors.TransportNotPossible:
 
602
            self._dir_mode = None
 
603
            self._file_mode = None
 
604
        else:
 
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
 
613
            else:
 
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
 
617
 
 
618
    def _get_file_mode(self):
 
619
        """Return Unix mode for newly created files, or None.
 
620
        """
 
621
        if not self._mode_check_done:
 
622
            self._find_creation_modes()
 
623
        return self._file_mode
 
624
 
 
625
    def _get_dir_mode(self):
 
626
        """Return Unix mode for newly created directories, or None.
 
627
        """
 
628
        if not self._mode_check_done:
 
629
            self._find_creation_modes()
 
630
        return self._dir_mode
 
631
 
 
632
    def get_config(self):
 
633
        """Get configuration for this BzrDir."""
 
634
        return config.BzrDirConfig(self)
 
635
 
 
636
    def _get_config(self):
 
637
        """By default, no configuration is available."""
 
638
        return None
 
639
 
 
640
    def __init__(self, _transport, _format):
 
641
        """Initialize a Bzr control dir object.
 
642
 
 
643
        Only really common logic should reside here, concrete classes should be
 
644
        made with varying behaviours.
 
645
 
 
646
        :param _format: the format that is creating this BzrDir instance.
 
647
        :param _transport: the transport this dir is based at.
 
648
        """
 
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
 
655
 
 
656
    @property 
 
657
    def user_transport(self):
 
658
        return self.root_transport
 
659
 
 
660
    @property
 
661
    def control_transport(self):
 
662
        return self.transport
 
663
 
 
664
    def is_control_filename(self, filename):
 
665
        """True if filename is the name of a path which is reserved for bzrdir's.
 
666
 
 
667
        :param filename: A filename within the root transport of this bzrdir.
 
668
 
 
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. 
 
672
        """
 
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/')
 
680
 
 
681
    def _cloning_metadir(self):
 
682
        """Produce a metadir suitable for cloning with.
 
683
 
 
684
        :returns: (destination_bzrdir_format, source_repository)
 
685
        """
 
686
        result_format = self._format.__class__()
 
687
        try:
 
688
            try:
 
689
                branch = self.open_branch(ignore_fallbacks=True)
 
690
                source_repository = branch.repository
 
691
                result_format._branch_format = branch._format
 
692
            except errors.NotBranchError:
 
693
                source_branch = None
 
694
                source_repository = self.open_repository()
 
695
        except errors.NoRepositoryPresent:
 
696
            source_repository = None
 
697
        else:
 
698
            # XXX TODO: This isinstance is here because we have not implemented
 
699
            # the fix recommended in bug # 103195 - to delegate this choice the
 
700
            # repository itself.
 
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
 
706
        try:
 
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
 
713
        else:
 
714
            result_format.workingtree_format = tree._format.__class__()
 
715
        return result_format, source_repository
 
716
 
 
717
    def cloning_metadir(self, require_stacking=False):
 
718
        """Produce a metadir suitable for cloning or sprouting with.
 
719
 
 
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.
 
723
 
 
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
 
728
            created.
 
729
        """
 
730
        format, repository = self._cloning_metadir()
 
731
        if format._workingtree_format is None:
 
732
            # No tree in self.
 
733
            if repository is None:
 
734
                # No repository either
 
735
                return format
 
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__()
 
740
        if require_stacking:
 
741
            format.require_stacking()
 
742
        return format
 
743
 
 
744
    def get_branch_transport(self, branch_format, name=None):
 
745
        """Get the transport for use by branch format in this BzrDir.
 
746
 
 
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.
 
750
 
 
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.
 
754
        """
 
755
        raise NotImplementedError(self.get_branch_transport)
 
756
 
 
757
    def get_repository_transport(self, repository_format):
 
758
        """Get the transport for use by repository format in this BzrDir.
 
759
 
 
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.
 
763
 
 
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.
 
767
        """
 
768
        raise NotImplementedError(self.get_repository_transport)
 
769
 
 
770
    def get_workingtree_transport(self, tree_format):
 
771
        """Get the transport for use by workingtree format in this BzrDir.
 
772
 
 
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.
 
776
 
 
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.
 
780
        """
 
781
        raise NotImplementedError(self.get_workingtree_transport)
 
782
 
 
783
    @classmethod
 
784
    def create(cls, base, format=None, possible_transports=None):
 
785
        """Create a new BzrDir at the url 'base'.
 
786
 
 
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.
 
791
        """
 
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)
 
797
 
 
798
    def __repr__(self):
 
799
        return "<%s at %r>" % (self.__class__.__name__, self.user_url)
 
800
 
 
801
    def update_feature_flags(self, updated_flags):
 
802
        """Update the features required by this bzrdir.
 
803
 
 
804
        :param updated_flags: Dictionary mapping feature names to necessities
 
805
            A necessity can be None to indicate the feature should be removed
 
806
        """
 
807
        self.control_files.lock_write()
 
808
        try:
 
809
            self._format._update_feature_flags(updated_flags)
 
810
            self.transport.put_bytes('branch-format', self._format.as_string())
 
811
        finally:
 
812
            self.control_files.unlock()
 
813
 
 
814
 
 
815
class BzrDirMeta1(BzrDir):
 
816
    """A .bzr meta version 1 control object.
 
817
 
 
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.
 
822
    """
 
823
 
 
824
    def _get_branch_path(self, name):
 
825
        """Obtain the branch path to use.
 
826
 
 
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.
 
830
 
 
831
        :param name: Optional branch name to use
 
832
        :return: Relative path to branch
 
833
        """
 
834
        if name == "":
 
835
            return 'branch'
 
836
        return urlutils.join('branches', name.encode("utf-8"))
 
837
 
 
838
    def _read_branch_list(self):
 
839
        """Read the branch list.
 
840
 
 
841
        :return: List of utf-8 encoded branch names.
 
842
        """
 
843
        try:
 
844
            f = self.control_transport.get('branch-list')
 
845
        except errors.NoSuchFile:
 
846
            return []
 
847
 
 
848
        ret = []
 
849
        try:
 
850
            for name in f:
 
851
                ret.append(name.rstrip(b"\n"))
 
852
        finally:
 
853
            f.close()
 
854
        return ret
 
855
 
 
856
    def _write_branch_list(self, branches):
 
857
        """Write out the branch list.
 
858
 
 
859
        :param branches: List of utf-8 branch names to write
 
860
        """
 
861
        self.transport.put_bytes('branch-list',
 
862
            "".join([name+"\n" for name in branches]))
 
863
 
 
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)
 
869
 
 
870
    def can_convert_format(self):
 
871
        """See BzrDir.can_convert_format()."""
 
872
        return True
 
873
 
 
874
    def create_branch(self, name=None, repository=None,
 
875
            append_revisions_only=None):
 
876
        """See ControlDir.create_branch."""
 
877
        if name is None:
 
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)
 
882
 
 
883
    def destroy_branch(self, name=None):
 
884
        """See ControlDir.destroy_branch."""
 
885
        if name is None:
 
886
            name = self._get_selected_branch()
 
887
        path = self._get_branch_path(name)
 
888
        if name != "":
 
889
            self.control_files.lock_write()
 
890
            try:
 
891
                branches = self._read_branch_list()
 
892
                try:
 
893
                    branches.remove(name.encode("utf-8"))
 
894
                except ValueError:
 
895
                    raise errors.NotBranchError(name)
 
896
                self._write_branch_list(branches)
 
897
            finally:
 
898
                self.control_files.unlock()
 
899
        try:
 
900
            self.transport.delete_tree(path)
 
901
        except errors.NoSuchFile:
 
902
            raise errors.NotBranchError(path=urlutils.join(self.transport.base,
 
903
                path), controldir=self)
 
904
 
 
905
    def create_repository(self, shared=False):
 
906
        """See BzrDir.create_repository."""
 
907
        return self._format.repository_format.initialize(self, shared)
 
908
 
 
909
    def destroy_repository(self):
 
910
        """See BzrDir.destroy_repository."""
 
911
        try:
 
912
            self.transport.delete_tree('repository')
 
913
        except errors.NoSuchFile:
 
914
            raise errors.NoRepositoryPresent(self)
 
915
 
 
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)
 
922
 
 
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()
 
933
 
 
934
    def destroy_workingtree_metadata(self):
 
935
        self.transport.delete_tree('checkout')
 
936
 
 
937
    def find_branch_format(self, name=None):
 
938
        """Find the branch 'format' for this bzrdir.
 
939
 
 
940
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
941
        """
 
942
        from .branch import BranchFormatMetadir
 
943
        return BranchFormatMetadir.find_format(self, name=name)
 
944
 
 
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
 
950
 
 
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)
 
956
 
 
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)
 
963
 
 
964
    def get_branch_transport(self, branch_format, name=None):
 
965
        """See BzrDir.get_branch_transport()."""
 
966
        if name is None:
 
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)
 
973
        try:
 
974
            branch_format.get_format_string()
 
975
        except NotImplementedError:
 
976
            raise errors.IncompatibleFormat(branch_format, self._format)
 
977
        if name != "":
 
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()
 
982
                try:
 
983
                    branches = self._read_branch_list()
 
984
                    dirname = urlutils.dirname(utf8_name)
 
985
                    if dirname != "" and dirname in branches:
 
986
                        raise errors.ParentBranchExists(name)
 
987
                    child_branches = [
 
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)
 
993
                finally:
 
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)
 
998
        try:
 
999
            self.transport.mkdir(path, mode=mode)
 
1000
        except errors.FileExists:
 
1001
            pass
 
1002
        return self.transport.clone(path)
 
1003
 
 
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')
 
1008
        try:
 
1009
            repository_format.get_format_string()
 
1010
        except NotImplementedError:
 
1011
            raise errors.IncompatibleFormat(repository_format, self._format)
 
1012
        try:
 
1013
            self.transport.mkdir('repository', mode=self._get_mkdir_mode())
 
1014
        except errors.FileExists:
 
1015
            pass
 
1016
        return self.transport.clone('repository')
 
1017
 
 
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')
 
1022
        try:
 
1023
            workingtree_format.get_format_string()
 
1024
        except NotImplementedError:
 
1025
            raise errors.IncompatibleFormat(workingtree_format, self._format)
 
1026
        try:
 
1027
            self.transport.mkdir('checkout', mode=self._get_mkdir_mode())
 
1028
        except errors.FileExists:
 
1029
            pass
 
1030
        return self.transport.clone('checkout')
 
1031
 
 
1032
    def get_branches(self):
 
1033
        """See ControlDir.get_branches."""
 
1034
        ret = {}
 
1035
        try:
 
1036
            ret[""] = self.open_branch(name="")
 
1037
        except (errors.NotBranchError, errors.NoRepositoryPresent):
 
1038
            pass
 
1039
 
 
1040
        for name in self._read_branch_list():
 
1041
            ret[name] = self.open_branch(name=name.decode('utf-8'))
 
1042
 
 
1043
        return ret
 
1044
 
 
1045
    def has_workingtree(self):
 
1046
        """Tell if this bzrdir contains a working tree.
 
1047
 
 
1048
        Note: if you're going to open the working tree, you should just go
 
1049
        ahead and try, and not ask permission first.
 
1050
        """
 
1051
        from .workingtree import WorkingTreeFormatMetaDir
 
1052
        try:
 
1053
            WorkingTreeFormatMetaDir.find_format_string(self)
 
1054
        except errors.NoWorkingTree:
 
1055
            return False
 
1056
        return True
 
1057
 
 
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.
 
1063
            return True
 
1064
        # we might want to push this down to the repository?
 
1065
        try:
 
1066
            if not isinstance(self.open_repository()._format,
 
1067
                              format.repository_format.__class__):
 
1068
                # the repository needs an upgrade.
 
1069
                return True
 
1070
        except errors.NoRepositoryPresent:
 
1071
            pass
 
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.
 
1076
                return True
 
1077
        try:
 
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.
 
1082
                return True
 
1083
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1084
            pass
 
1085
        return False
 
1086
 
 
1087
    def open_branch(self, name=None, unsupported=False,
 
1088
                    ignore_fallbacks=False, possible_transports=None):
 
1089
        """See ControlDir.open_branch."""
 
1090
        if name is None:
 
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)
 
1097
 
 
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)
 
1104
 
 
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)
 
1113
 
 
1114
    def _get_config(self):
 
1115
        return config.TransportConfig(self.transport, 'control.conf')
 
1116
 
 
1117
 
 
1118
class BzrFormat(object):
 
1119
    """Base class for all formats of things living in metadirs.
 
1120
 
 
1121
    This class manages the format string that is stored in the 'format'
 
1122
    or 'branch-format' file.
 
1123
 
 
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).
 
1129
 
 
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
 
1132
    feature flags.
 
1133
 
 
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.
 
1136
 
 
1137
    :ivar features: Dictionary mapping feature names to their necessity
 
1138
    """
 
1139
 
 
1140
    _present_features = set()
 
1141
 
 
1142
    def __init__(self):
 
1143
        self.features = {}
 
1144
 
 
1145
    @classmethod
 
1146
    def register_feature(cls, name):
 
1147
        """Register a feature as being present.
 
1148
 
 
1149
        :param name: Name of the feature
 
1150
        """
 
1151
        if b" " in name:
 
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)
 
1156
 
 
1157
    @classmethod
 
1158
    def unregister_feature(cls, name):
 
1159
        """Unregister a feature."""
 
1160
        cls._present_features.remove(name)
 
1161
 
 
1162
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
1163
            basedir=None):
 
1164
        for name, necessity in self.features.items():
 
1165
            if name in self._present_features:
 
1166
                continue
 
1167
            if necessity == b"optional":
 
1168
                mutter("ignoring optional missing feature %s", name)
 
1169
                continue
 
1170
            elif necessity == b"required":
 
1171
                raise MissingFeature(name)
 
1172
            else:
 
1173
                mutter("treating unknown necessity as require for %s",
 
1174
                       name)
 
1175
                raise MissingFeature(name)
 
1176
 
 
1177
    @classmethod
 
1178
    def get_format_string(cls):
 
1179
        """Return the ASCII format string that identifies this format."""
 
1180
        raise NotImplementedError(cls.get_format_string)
 
1181
 
 
1182
    @classmethod
 
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()
 
1188
        ret = cls()
 
1189
        for lineno, line in enumerate(lines):
 
1190
            try:
 
1191
                (necessity, feature) = line.split(b" ", 1)
 
1192
            except ValueError:
 
1193
                raise errors.ParseFormatError(format=cls, lineno=lineno+2,
 
1194
                    line=line, text=text)
 
1195
            ret.features[feature] = necessity
 
1196
        return ret
 
1197
 
 
1198
    def as_string(self):
 
1199
        """Return the string representation of this format.
 
1200
        """
 
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)
 
1205
 
 
1206
    @classmethod
 
1207
    def _find_format(klass, registry, kind, format_string):
 
1208
        try:
 
1209
            first_line = format_string[:format_string.index(b"\n")+1]
 
1210
        except ValueError:
 
1211
            first_line = format_string
 
1212
        try:
 
1213
            cls = registry.get(first_line)
 
1214
        except KeyError:
 
1215
            raise errors.UnknownFormatError(format=first_line, kind=kind)
 
1216
        return cls.from_string(format_string)
 
1217
 
 
1218
    def network_name(self):
 
1219
        """A simple byte string uniquely identifying this format for RPC calls.
 
1220
 
 
1221
        Metadir branch formats use their format string.
 
1222
        """
 
1223
        return self.as_string()
 
1224
 
 
1225
    def __eq__(self, other):
 
1226
        return (self.__class__ is other.__class__ and
 
1227
                self.features == other.features)
 
1228
 
 
1229
    def _update_feature_flags(self, updated_flags):
 
1230
        """Update the feature flags in this format.
 
1231
 
 
1232
        :param updated_flags: Updated feature flags
 
1233
        """
 
1234
        for name, necessity in updated_flags.items():
 
1235
            if necessity is None:
 
1236
                try:
 
1237
                    del self.features[name]
 
1238
                except KeyError:
 
1239
                    pass
 
1240
            else:
 
1241
                self.features[name] = necessity
 
1242
 
 
1243
 
 
1244
class BzrDirFormat(BzrFormat, controldir.ControlDirFormat):
 
1245
    """ControlDirFormat base class for .bzr/ directories.
 
1246
 
 
1247
    Formats are placed in a dict by their format string for reference
 
1248
    during bzrdir opening. These should be subclasses of BzrDirFormat
 
1249
    for consistency.
 
1250
 
 
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.
 
1254
    """
 
1255
 
 
1256
    _lock_file_name = 'branch-lock'
 
1257
 
 
1258
    # _lock_class must be set in subclasses to the lock type, typ.
 
1259
    # TransportLock or LockDir
 
1260
 
 
1261
    def initialize_on_transport(self, transport):
 
1262
        """Initialize a new bzrdir in the base directory of a Transport."""
 
1263
        try:
 
1264
            # can we hand off the request to the smart server rather than using
 
1265
            # vfs calls?
 
1266
            client_medium = transport.get_smart_medium()
 
1267
        except errors.NoSmartMedium:
 
1268
            return self._initialize_on_transport_vfs(transport)
 
1269
        else:
 
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
 
1272
            # metadir1
 
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)
 
1279
 
 
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.
 
1285
 
 
1286
        The directory to initialize will be created.
 
1287
 
 
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
 
1291
            to_transport.
 
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
 
1296
            relative to.
 
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
 
1300
            create.
 
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
 
1305
            not.
 
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.
 
1311
        """
 
1312
        if not vfs_only:
 
1313
            # Try to hand off to a smart server 
 
1314
            try:
 
1315
                client_medium = transport.get_smart_medium()
 
1316
            except errors.NoSmartMedium:
 
1317
                pass
 
1318
            else:
 
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('.')
 
1334
            return transport
 
1335
        def redirected(transport, e, redirection_notice):
 
1336
            note(redirection_notice)
 
1337
            return transport._redirected_to(e.source, e.target)
 
1338
        try:
 
1339
            transport = do_catching_redirections(make_directory, transport,
 
1340
                redirected)
 
1341
        except errors.FileExists:
 
1342
            if not use_existing_dir:
 
1343
                raise
 
1344
        except errors.NoSuchFile:
 
1345
            if not create_prefix:
 
1346
                raise
 
1347
            transport.create_prefix()
 
1348
 
 
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.
 
1353
 
 
1354
        result = self.initialize_on_transport(transport)
 
1355
        if repo_format_name:
 
1356
            try:
 
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.
 
1362
                pass
 
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()
 
1373
        else:
 
1374
            result_repo = None
 
1375
            repository_policy = None
 
1376
        return result_repo, result, require_stacking, repository_policy
 
1377
 
 
1378
    def _initialize_on_transport_vfs(self, transport):
 
1379
        """Initialize a new bzrdir using VFS calls.
 
1380
 
 
1381
        :param transport: The transport to create the .bzr directory in.
 
1382
        :return: A
 
1383
        """
 
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)
 
1388
        try:
 
1389
            temp_control._transport.mkdir('.bzr',
 
1390
                # FIXME: RBC 20060121 don't peek under
 
1391
                # the covers
 
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
 
1398
        del temp_control
 
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()),
 
1405
                      ]
 
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()
 
1411
        try:
 
1412
            for (filename, content) in utf8_files:
 
1413
                bzrdir_transport.put_bytes(filename, content,
 
1414
                    mode=file_mode)
 
1415
        finally:
 
1416
            control_files.unlock()
 
1417
        return self.open(transport, _found=True)
 
1418
 
 
1419
    def open(self, transport, _found=False):
 
1420
        """Return an instance of this format for the dir transport points at.
 
1421
 
 
1422
        _found is a private parameter, do not use it.
 
1423
        """
 
1424
        if not _found:
 
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 "
 
1428
                        "format %s"
 
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)
 
1434
 
 
1435
    def _open(self, transport):
 
1436
        """Template method helper for opening BzrDirectories.
 
1437
 
 
1438
        This performs the actual open and any additional logic or parameter
 
1439
        passing.
 
1440
        """
 
1441
        raise NotImplementedError(self._open)
 
1442
 
 
1443
    def _supply_sub_formats_to(self, other_format):
 
1444
        """Give other_format the same values for sub formats as this has.
 
1445
 
 
1446
        This method is expected to be used when parameterising a
 
1447
        RemoteBzrDirFormat instance with the parameters from a
 
1448
        BzrDirMetaFormat1 instance.
 
1449
 
 
1450
        :param other_format: other_format is a format which should be
 
1451
            compatible with whatever sub formats are supported by self.
 
1452
        :return: None.
 
1453
        """
 
1454
        other_format.features = dict(self.features)
 
1455
 
 
1456
    def supports_transport(self, transport):
 
1457
        # bzr formats can be opened over all known transports
 
1458
        return True
 
1459
 
 
1460
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
1461
            basedir=None):
 
1462
        controldir.ControlDirFormat.check_support_status(self,
 
1463
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
1464
            basedir=basedir)
 
1465
        BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
1466
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
1467
 
 
1468
 
 
1469
class BzrDirMetaFormat1(BzrDirFormat):
 
1470
    """Bzr meta control format 1
 
1471
 
 
1472
    This is the first format with split out working tree, branch and repository
 
1473
    disk storage.
 
1474
 
 
1475
    It has:
 
1476
 
 
1477
    - Format 3 working trees [optional]
 
1478
    - Format 5 branches [optional]
 
1479
    - Format 7 repositories [optional]
 
1480
    """
 
1481
 
 
1482
    _lock_class = lockdir.LockDir
 
1483
 
 
1484
    fixed_components = False
 
1485
 
 
1486
    colocated_branches = True
 
1487
 
 
1488
    def __init__(self):
 
1489
        BzrDirFormat.__init__(self)
 
1490
        self._workingtree_format = None
 
1491
        self._branch_format = None
 
1492
        self._repository_format = None
 
1493
 
 
1494
    def __eq__(self, other):
 
1495
        if other.__class__ is not self.__class__:
 
1496
            return False
 
1497
        if other.repository_format != self.repository_format:
 
1498
            return False
 
1499
        if other.workingtree_format != self.workingtree_format:
 
1500
            return False
 
1501
        if other.features != self.features:
 
1502
            return False
 
1503
        return True
 
1504
 
 
1505
    def __ne__(self, other):
 
1506
        return not self == other
 
1507
 
 
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
 
1513
 
 
1514
    def set_branch_format(self, format):
 
1515
        self._branch_format = format
 
1516
 
 
1517
    def require_stacking(self, stack_on=None, possible_transports=None,
 
1518
            _skip_repo=False):
 
1519
        """We have a request to stack, try to ensure the formats support it.
 
1520
 
 
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
 
1523
            forcing an upgrade.
 
1524
        """
 
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
 
1530
 
 
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():
 
1535
            if target[1]:
 
1536
                # We've checked, don't check again
 
1537
                return target
 
1538
            if stack_on is None:
 
1539
                # No target format, that means we want to force upgrading
 
1540
                target[:] = [None, True, True]
 
1541
                return target
 
1542
            try:
 
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]
 
1548
                return target
 
1549
            except errors.JailBreak:
 
1550
                # JailBreak, JFDI and upgrade anyway
 
1551
                target[:] = [None, True, True]
 
1552
                return target
 
1553
            try:
 
1554
                target_branch = target_dir.open_branch()
 
1555
            except errors.NotBranchError:
 
1556
                # No branch, don't upgrade formats
 
1557
                target[:] = [None, True, False]
 
1558
                return target
 
1559
            target[:] = [target_branch, True, False]
 
1560
            return target
 
1561
 
 
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?
 
1568
                if do_upgrade:
 
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()
 
1573
                    else:
 
1574
                        new_repo_format = knitpack_repo.RepositoryFormatKnitPack5()
 
1575
            else:
 
1576
                # If the target already supports stacking, then we know the
 
1577
                # project is already able to use stacking, so auto-upgrade
 
1578
                # for them
 
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
 
1582
                    # repo
 
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())
 
1589
 
 
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:
 
1595
                if do_upgrade:
 
1596
                    # TODO: bad monkey, hard-coded formats...
 
1597
                    from .branch import BzrBranchFormat7
 
1598
                    new_branch_format = BzrBranchFormat7()
 
1599
            else:
 
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())
 
1609
 
 
1610
    def get_converter(self, format=None):
 
1611
        """See BzrDirFormat.get_converter()."""
 
1612
        if format is None:
 
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)
 
1624
 
 
1625
    @classmethod
 
1626
    def get_format_string(cls):
 
1627
        """See BzrDirFormat.get_format_string()."""
 
1628
        return b"Bazaar-NG meta directory, format 1\n"
 
1629
 
 
1630
    def get_format_description(self):
 
1631
        """See BzrDirFormat.get_format_description()."""
 
1632
        return "Meta directory format 1"
 
1633
 
 
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
 
1638
        # problems.
 
1639
        format = BzrDirMetaFormat1()
 
1640
        self._supply_sub_formats_to(format)
 
1641
        return BzrDirMeta1(transport, format)
 
1642
 
 
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()
 
1649
 
 
1650
    def _set_repository_format(self, value):
 
1651
        """Allow changing the repository format for metadir formats."""
 
1652
        self._repository_format = value
 
1653
 
 
1654
    repository_format = property(__return_repository_format,
 
1655
        _set_repository_format)
 
1656
 
 
1657
    def _supply_sub_formats_to(self, other_format):
 
1658
        """Give other_format the same values for sub formats as this has.
 
1659
 
 
1660
        This method is expected to be used when parameterising a
 
1661
        RemoteBzrDirFormat instance with the parameters from a
 
1662
        BzrDirMetaFormat1 instance.
 
1663
 
 
1664
        :param other_format: other_format is a format which should be
 
1665
            compatible with whatever sub formats are supported by self.
 
1666
        :return: None.
 
1667
        """
 
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
 
1675
 
 
1676
    def __get_workingtree_format(self):
 
1677
        if self._workingtree_format is None:
 
1678
            from .workingtree import (
 
1679
                format_registry as wt_format_registry,
 
1680
                )
 
1681
            self._workingtree_format = wt_format_registry.get_default()
 
1682
        return self._workingtree_format
 
1683
 
 
1684
    def __set_workingtree_format(self, wt_format):
 
1685
        self._workingtree_format = wt_format
 
1686
 
 
1687
    def __repr__(self):
 
1688
        return "<%r>" % (self.__class__.__name__,)
 
1689
 
 
1690
    workingtree_format = property(__get_workingtree_format,
 
1691
                                  __set_workingtree_format)
 
1692
 
 
1693
 
 
1694
class BzrDirMetaFormat1Colo(BzrDirMetaFormat1):
 
1695
    """BzrDirMeta1 format with support for colocated branches."""
 
1696
 
 
1697
    colocated_branches = True
 
1698
 
 
1699
    @classmethod
 
1700
    def get_format_string(cls):
 
1701
        """See BzrDirFormat.get_format_string()."""
 
1702
        return b"Bazaar meta directory, format 1 (with colocated branches)\n"
 
1703
 
 
1704
    def get_format_description(self):
 
1705
        """See BzrDirFormat.get_format_description()."""
 
1706
        return "Meta directory format 1 with support for colocated branches"
 
1707
 
 
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
 
1712
        # problems.
 
1713
        format = BzrDirMetaFormat1Colo()
 
1714
        self._supply_sub_formats_to(format)
 
1715
        return BzrDirMeta1(transport, format)
 
1716
 
 
1717
 
 
1718
class ConvertMetaToMeta(controldir.Converter):
 
1719
    """Converts the components of metadirs."""
 
1720
 
 
1721
    def __init__(self, target_format):
 
1722
        """Create a metadir to metadir converter.
 
1723
 
 
1724
        :param target_format: The final metadir format that is desired.
 
1725
        """
 
1726
        self.target_format = target_format
 
1727
 
 
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()
 
1732
        self.count = 0
 
1733
        self.total = 1
 
1734
        self.step('checking repository format')
 
1735
        try:
 
1736
            repo = self.controldir.open_repository()
 
1737
        except errors.NoRepositoryPresent:
 
1738
            pass
 
1739
        else:
 
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__
 
1756
            while old != new:
 
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()
 
1769
                else:
 
1770
                    raise errors.BadConversionTarget("No converter", new,
 
1771
                        branch._format)
 
1772
                branch_converter.convert(branch)
 
1773
                branch = self.controldir.open_branch()
 
1774
                old = branch._format.__class__
 
1775
        try:
 
1776
            tree = self.controldir.open_workingtree(recommend_upgrade=False)
 
1777
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1778
            pass
 
1779
        else:
 
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)
 
1797
        self.pb.finished()
 
1798
        return to_convert
 
1799
 
 
1800
 
 
1801
class ConvertMetaToColo(controldir.Converter):
 
1802
    """Add colocated branch support."""
 
1803
 
 
1804
    def __init__(self, target_format):
 
1805
        """Create a converter.that upgrades a metadir to the colo format.
 
1806
 
 
1807
        :param target_format: The final metadir format that is desired.
 
1808
        """
 
1809
        self.target_format = target_format
 
1810
 
 
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)
 
1816
 
 
1817
 
 
1818
class ConvertMetaToColo(controldir.Converter):
 
1819
    """Convert a 'development-colo' bzrdir to a '2a' bzrdir."""
 
1820
 
 
1821
    def __init__(self, target_format):
 
1822
        """Create a converter that converts a 'development-colo' metadir
 
1823
        to a '2a' metadir.
 
1824
 
 
1825
        :param target_format: The final metadir format that is desired.
 
1826
        """
 
1827
        self.target_format = target_format
 
1828
 
 
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)
 
1834
 
 
1835
 
 
1836
class CreateRepository(controldir.RepositoryAcquisitionPolicy):
 
1837
    """A policy of creating a new repository"""
 
1838
 
 
1839
    def __init__(self, controldir, stack_on=None, stack_on_pwd=None,
 
1840
                 require_stacking=False):
 
1841
        """Constructor.
 
1842
 
 
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
 
1846
            relative to.
 
1847
        """
 
1848
        super(CreateRepository, self).__init__(
 
1849
                stack_on, stack_on_pwd, require_stacking)
 
1850
        self._controldir = controldir
 
1851
 
 
1852
    def acquire_repository(self, make_working_trees=None, shared=False,
 
1853
            possible_transports=None):
 
1854
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
 
1855
 
 
1856
        Creates the desired repository in the controldir we already have.
 
1857
        """
 
1858
        if possible_transports is None:
 
1859
            possible_transports = []
 
1860
        else:
 
1861
            possible_transports = list(possible_transports)
 
1862
        possible_transports.append(self._controldir.root_transport)
 
1863
        stack_on = self._get_full_stack_on()
 
1864
        if 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
 
1878
 
 
1879
 
 
1880
class UseExistingRepository(controldir.RepositoryAcquisitionPolicy):
 
1881
    """A policy of reusing an existing repository"""
 
1882
 
 
1883
    def __init__(self, repository, stack_on=None, stack_on_pwd=None,
 
1884
                 require_stacking=False):
 
1885
        """Constructor.
 
1886
 
 
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
 
1890
            relative to.
 
1891
        """
 
1892
        super(UseExistingRepository, self).__init__(
 
1893
                stack_on, stack_on_pwd, require_stacking)
 
1894
        self._repository = repository
 
1895
 
 
1896
    def acquire_repository(self, make_working_trees=None, shared=False,
 
1897
            possible_transports=None):
 
1898
        """Implementation of RepositoryAcquisitionPolicy.acquire_repository
 
1899
 
 
1900
        Returns an existing repository to use.
 
1901
        """
 
1902
        if possible_transports is None:
 
1903
            possible_transports = []
 
1904
        else:
 
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
 
1910
 
 
1911
 
 
1912
controldir.ControlDirFormat._default_format = BzrDirMetaFormat1()