~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:56:05 UTC
  • mfrom: (6615.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201195605-o7rl92wf6uyum3fk
(vila) Open trunk again as 2.8b1 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2010 Canonical Ltd
 
1
# Copyright (C) 2010, 2011, 2012 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
22
22
 
23
23
"""
24
24
 
 
25
from __future__ import absolute_import
 
26
 
25
27
from bzrlib.lazy_import import lazy_import
26
28
lazy_import(globals(), """
27
29
import textwrap
28
30
 
29
31
from bzrlib import (
30
32
    errors,
31
 
    graph,
32
 
    registry,
 
33
    hooks,
33
34
    revision as _mod_revision,
34
 
    symbol_versioning,
 
35
    transport as _mod_transport,
 
36
    trace,
 
37
    ui,
35
38
    urlutils,
36
39
    )
 
40
from bzrlib.transport import local
37
41
from bzrlib.push import (
38
42
    PushResult,
39
43
    )
40
 
from bzrlib.trace import (
41
 
    mutter,
42
 
    )
43
 
from bzrlib.transport import (
44
 
    get_transport,
45
 
    local,
46
 
    )
47
44
 
 
45
from bzrlib.i18n import gettext
48
46
""")
49
47
 
 
48
from bzrlib import registry
 
49
 
50
50
 
51
51
class ControlComponent(object):
52
52
    """Abstract base class for control directory components.
108
108
        """Return a sequence of all branches local to this control directory.
109
109
 
110
110
        """
 
111
        return self.get_branches().values()
 
112
 
 
113
    def get_branches(self):
 
114
        """Get all branches in this control directory, as a dictionary.
 
115
        
 
116
        :return: Dictionary mapping branch names to instances.
 
117
        """
111
118
        try:
112
 
            return [self.open_branch()]
 
119
           return { "": self.open_branch() }
113
120
        except (errors.NotBranchError, errors.NoRepositoryPresent):
114
 
            return []
 
121
           return {}
115
122
 
116
123
    def is_control_filename(self, filename):
117
124
        """True if filename is the name of a path which is reserved for
139
146
        """
140
147
        raise NotImplementedError(self.needs_format_conversion)
141
148
 
 
149
    def create_repository(self, shared=False):
 
150
        """Create a new repository in this control directory.
 
151
 
 
152
        :param shared: If a shared repository should be created
 
153
        :return: The newly created repository
 
154
        """
 
155
        raise NotImplementedError(self.create_repository)
 
156
 
142
157
    def destroy_repository(self):
143
158
        """Destroy the repository in this ControlDir."""
144
159
        raise NotImplementedError(self.destroy_repository)
145
160
 
146
 
    def create_branch(self, name=None):
 
161
    def create_branch(self, name=None, repository=None,
 
162
                      append_revisions_only=None):
147
163
        """Create a branch in this ControlDir.
148
164
 
149
165
        :param name: Name of the colocated branch to create, None for
150
 
            the default branch.
 
166
            the user selected branch or "" for the active branch.
 
167
        :param append_revisions_only: Whether this branch should only allow
 
168
            appending new revisions to its history.
151
169
 
152
170
        The controldirs format will control what branch format is created.
153
171
        For more control see BranchFormatXX.create(a_controldir).
157
175
    def destroy_branch(self, name=None):
158
176
        """Destroy a branch in this ControlDir.
159
177
 
160
 
        :param name: Name of the branch to destroy, None for the default 
161
 
            branch.
 
178
        :param name: Name of the branch to destroy, None for the 
 
179
            user selected branch or "" for the active branch.
 
180
        :raise NotBranchError: When the branch does not exist
162
181
        """
163
182
        raise NotImplementedError(self.destroy_branch)
164
183
 
191
210
        """
192
211
        raise NotImplementedError(self.destroy_workingtree_metadata)
193
212
 
 
213
    def find_branch_format(self, name=None):
 
214
        """Find the branch 'format' for this controldir.
 
215
 
 
216
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
217
        """
 
218
        raise NotImplementedError(self.find_branch_format)
 
219
 
194
220
    def get_branch_reference(self, name=None):
195
221
        """Return the referenced URL for the branch in this controldir.
196
222
 
205
231
            raise errors.NoColocatedBranchSupport(self)
206
232
        return None
207
233
 
208
 
    def get_branch_transport(self, branch_format, name=None):
209
 
        """Get the transport for use by branch format in this ControlDir.
210
 
 
211
 
        Note that bzr dirs that do not support format strings will raise
212
 
        IncompatibleFormat if the branch format they are given has
213
 
        a format string, and vice versa.
214
 
 
215
 
        If branch_format is None, the transport is returned with no
216
 
        checking. If it is not None, then the returned transport is
217
 
        guaranteed to point to an existing directory ready for use.
218
 
        """
219
 
        raise NotImplementedError(self.get_branch_transport)
220
 
 
221
 
    def get_repository_transport(self, repository_format):
222
 
        """Get the transport for use by repository format in this ControlDir.
223
 
 
224
 
        Note that bzr dirs that do not support format strings will raise
225
 
        IncompatibleFormat if the repository format they are given has
226
 
        a format string, and vice versa.
227
 
 
228
 
        If repository_format is None, the transport is returned with no
229
 
        checking. If it is not None, then the returned transport is
230
 
        guaranteed to point to an existing directory ready for use.
231
 
        """
232
 
        raise NotImplementedError(self.get_repository_transport)
233
 
 
234
 
    def get_workingtree_transport(self, tree_format):
235
 
        """Get the transport for use by workingtree format in this ControlDir.
236
 
 
237
 
        Note that bzr dirs that do not support format strings will raise
238
 
        IncompatibleFormat if the workingtree format they are given has a
239
 
        format string, and vice versa.
240
 
 
241
 
        If workingtree_format is None, the transport is returned with no
242
 
        checking. If it is not None, then the returned transport is
243
 
        guaranteed to point to an existing directory ready for use.
244
 
        """
245
 
        raise NotImplementedError(self.get_workingtree_transport)
 
234
    def set_branch_reference(self, target_branch, name=None):
 
235
        """Set the referenced URL for the branch in this controldir.
 
236
 
 
237
        :param name: Optional colocated branch name
 
238
        :param target_branch: Branch to reference
 
239
        :raises NoColocatedBranchSupport: If a branch name was specified
 
240
            but colocated branches are not supported.
 
241
        :return: The referencing branch
 
242
        """
 
243
        raise NotImplementedError(self.set_branch_reference)
246
244
 
247
245
    def open_branch(self, name=None, unsupported=False,
248
 
                    ignore_fallbacks=False):
 
246
                    ignore_fallbacks=False, possible_transports=None):
249
247
        """Open the branch object at this ControlDir if one is present.
250
248
 
251
 
        If unsupported is True, then no longer supported branch formats can
252
 
        still be opened.
253
 
 
254
 
        TODO: static convenience version of this?
 
249
        :param unsupported: if True, then no longer supported branch formats can
 
250
            still be opened.
 
251
        :param ignore_fallbacks: Whether to open fallback repositories
 
252
        :param possible_transports: Transports to use for opening e.g.
 
253
            fallback repositories.
255
254
        """
256
255
        raise NotImplementedError(self.open_branch)
257
256
 
263
262
        get at a repository.
264
263
 
265
264
        :param _unsupported: a private parameter, not part of the api.
266
 
        TODO: static convenience version of this?
267
265
        """
268
266
        raise NotImplementedError(self.open_repository)
269
267
 
276
274
        """
277
275
        raise NotImplementedError(self.find_repository)
278
276
 
279
 
    def open_workingtree(self, _unsupported=False,
 
277
    def open_workingtree(self, unsupported=False,
280
278
                         recommend_upgrade=True, from_branch=None):
281
279
        """Open the workingtree object at this ControlDir if one is present.
282
280
 
297
295
        branch and discards it, and that's somewhat expensive.)
298
296
        """
299
297
        try:
300
 
            self.open_branch(name)
 
298
            self.open_branch(name, ignore_fallbacks=True)
301
299
            return True
302
300
        except errors.NotBranchError:
303
301
            return False
304
302
 
 
303
    def _get_selected_branch(self):
 
304
        """Return the name of the branch selected by the user.
 
305
 
 
306
        :return: Name of the branch selected by the user, or "".
 
307
        """
 
308
        branch = self.root_transport.get_segment_parameters().get("branch")
 
309
        if branch is None:
 
310
            branch = ""
 
311
        return urlutils.unescape(branch)
 
312
 
305
313
    def has_workingtree(self):
306
314
        """Tell if this controldir contains a working tree.
307
315
 
334
342
        raise NotImplementedError(self.cloning_metadir)
335
343
 
336
344
    def checkout_metadir(self):
337
 
        """Produce a metadir suitable for checkouts of this controldir."""
 
345
        """Produce a metadir suitable for checkouts of this controldir.
 
346
 
 
347
        :returns: A ControlDirFormat with all component formats
 
348
            either set appropriately or set to None if that component
 
349
            should not be created.
 
350
        """
338
351
        return self.cloning_metadir()
339
352
 
340
353
    def sprout(self, url, revision_id=None, force_new_repo=False,
351
364
        whether one existed before or not; and a local branch is always
352
365
        created.
353
366
 
354
 
        if revision_id is not None, then the clone operation may tune
355
 
            itself to download less data.
 
367
        :param revision_id: if revision_id is not None, then the clone
 
368
            operation may tune itself to download less data.
356
369
        :param accelerator_tree: A tree which can be used for retrieving file
357
370
            contents more quickly than the revision tree, i.e. a workingtree.
358
371
            The revision tree will be used for cases where accelerator_tree's
364
377
        :param create_tree_if_local: If true, a working-tree will be created
365
378
            when working locally.
366
379
        """
367
 
        target_transport = get_transport(url, possible_transports)
368
 
        target_transport.ensure_base()
369
 
        cloning_format = self.cloning_metadir(stacked)
370
 
        # Create/update the result branch
371
 
        result = cloning_format.initialize_on_transport(target_transport)
372
 
        # if a stacked branch wasn't requested, we don't create one
373
 
        # even if the origin was stacked
374
 
        stacked_branch_url = None
375
 
        if source_branch is not None:
376
 
            if stacked:
377
 
                stacked_branch_url = self.root_transport.base
378
 
            source_repository = source_branch.repository
379
 
        else:
380
 
            try:
381
 
                source_branch = self.open_branch()
382
 
                source_repository = source_branch.repository
383
 
                if stacked:
384
 
                    stacked_branch_url = self.root_transport.base
385
 
            except errors.NotBranchError:
386
 
                source_branch = None
387
 
                try:
388
 
                    source_repository = self.open_repository()
389
 
                except errors.NoRepositoryPresent:
390
 
                    source_repository = None
391
 
        repository_policy = result.determine_repository_policy(
392
 
            force_new_repo, stacked_branch_url, require_stacking=stacked)
393
 
        result_repo, is_new_repo = repository_policy.acquire_repository()
394
 
        is_stacked = stacked or (len(result_repo._fallback_repositories) != 0)
395
 
        if is_new_repo and revision_id is not None and not is_stacked:
396
 
            fetch_spec = graph.PendingAncestryResult(
397
 
                [revision_id], source_repository)
398
 
        else:
399
 
            fetch_spec = None
400
 
        if source_repository is not None:
401
 
            # Fetch while stacked to prevent unstacked fetch from
402
 
            # Branch.sprout.
403
 
            if fetch_spec is None:
404
 
                result_repo.fetch(source_repository, revision_id=revision_id)
405
 
            else:
406
 
                result_repo.fetch(source_repository, fetch_spec=fetch_spec)
407
 
 
408
 
        if source_branch is None:
409
 
            # this is for sprouting a controldir without a branch; is that
410
 
            # actually useful?
411
 
            # Not especially, but it's part of the contract.
412
 
            result_branch = result.create_branch()
413
 
        else:
414
 
            result_branch = source_branch.sprout(result,
415
 
                revision_id=revision_id, repository_policy=repository_policy)
416
 
        mutter("created new branch %r" % (result_branch,))
417
 
 
418
 
        # Create/update the result working tree
419
 
        if (create_tree_if_local and
420
 
            isinstance(target_transport, local.LocalTransport) and
421
 
            (result_repo is None or result_repo.make_working_trees())):
422
 
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
423
 
                hardlink=hardlink)
424
 
            wt.lock_write()
425
 
            try:
426
 
                if wt.path2id('') is None:
427
 
                    try:
428
 
                        wt.set_root_id(self.open_workingtree.get_root_id())
429
 
                    except errors.NoWorkingTree:
430
 
                        pass
431
 
            finally:
432
 
                wt.unlock()
433
 
        else:
434
 
            wt = None
435
 
        if recurse == 'down':
436
 
            if wt is not None:
437
 
                basis = wt.basis_tree()
438
 
                basis.lock_read()
439
 
                subtrees = basis.iter_references()
440
 
            elif result_branch is not None:
441
 
                basis = result_branch.basis_tree()
442
 
                basis.lock_read()
443
 
                subtrees = basis.iter_references()
444
 
            elif source_branch is not None:
445
 
                basis = source_branch.basis_tree()
446
 
                basis.lock_read()
447
 
                subtrees = basis.iter_references()
448
 
            else:
449
 
                subtrees = []
450
 
                basis = None
451
 
            try:
452
 
                for path, file_id in subtrees:
453
 
                    target = urlutils.join(url, urlutils.escape(path))
454
 
                    sublocation = source_branch.reference_parent(file_id, path)
455
 
                    sublocation.bzrdir.sprout(target,
456
 
                        basis.get_reference_revision(file_id, path),
457
 
                        force_new_repo=force_new_repo, recurse=recurse,
458
 
                        stacked=stacked)
459
 
            finally:
460
 
                if basis is not None:
461
 
                    basis.unlock()
462
 
        return result
 
380
        raise NotImplementedError(self.sprout)
463
381
 
464
382
    def push_branch(self, source, revision_id=None, overwrite=False, 
465
383
        remember=False, create_prefix=False):
481
399
        if br_to is None:
482
400
            # We have a repository but no branch, copy the revisions, and then
483
401
            # create a branch.
 
402
            if revision_id is None:
 
403
                # No revision supplied by the user, default to the branch
 
404
                # revision
 
405
                revision_id = source.last_revision()
484
406
            repository_to.fetch(source.repository, revision_id=revision_id)
485
407
            br_to = source.clone(self, revision_id=revision_id)
486
408
            if source.get_push_location() is None or remember:
 
409
                # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
487
410
                source.set_push_location(br_to.base)
488
411
            push_result.stacked_on = None
489
412
            push_result.branch_push_result = None
495
418
        else:
496
419
            # We have successfully opened the branch, remember if necessary:
497
420
            if source.get_push_location() is None or remember:
 
421
                # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
498
422
                source.set_push_location(br_to.base)
499
423
            try:
500
424
                tree_to = self.open_workingtree()
522
446
        return push_result
523
447
 
524
448
    def _get_tree_branch(self, name=None):
525
 
        """Return the branch and tree, if any, for this bzrdir.
 
449
        """Return the branch and tree, if any, for this controldir.
526
450
 
527
451
        :param name: Name of colocated branch to open.
528
452
 
542
466
                branch = tree.branch
543
467
        return tree, branch
544
468
 
545
 
 
 
469
    def get_config(self):
 
470
        """Get configuration for this ControlDir."""
 
471
        raise NotImplementedError(self.get_config)
 
472
 
 
473
    def check_conversion_target(self, target_format):
 
474
        """Check that a controldir as a whole can be converted to a new format."""
 
475
        raise NotImplementedError(self.check_conversion_target)
 
476
 
 
477
    def clone(self, url, revision_id=None, force_new_repo=False,
 
478
              preserve_stacking=False):
 
479
        """Clone this controldir and its contents to url verbatim.
 
480
 
 
481
        :param url: The url create the clone at.  If url's last component does
 
482
            not exist, it will be created.
 
483
        :param revision_id: The tip revision-id to use for any branch or
 
484
            working tree.  If not None, then the clone operation may tune
 
485
            itself to download less data.
 
486
        :param force_new_repo: Do not use a shared repository for the target
 
487
                               even if one is available.
 
488
        :param preserve_stacking: When cloning a stacked branch, stack the
 
489
            new branch on top of the other branch's stacked-on branch.
 
490
        """
 
491
        return self.clone_on_transport(_mod_transport.get_transport(url),
 
492
                                       revision_id=revision_id,
 
493
                                       force_new_repo=force_new_repo,
 
494
                                       preserve_stacking=preserve_stacking)
 
495
 
 
496
    def clone_on_transport(self, transport, revision_id=None,
 
497
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
 
498
        create_prefix=False, use_existing_dir=True, no_tree=False):
 
499
        """Clone this controldir and its contents to transport verbatim.
 
500
 
 
501
        :param transport: The transport for the location to produce the clone
 
502
            at.  If the target directory does not exist, it will be created.
 
503
        :param revision_id: The tip revision-id to use for any branch or
 
504
            working tree.  If not None, then the clone operation may tune
 
505
            itself to download less data.
 
506
        :param force_new_repo: Do not use a shared repository for the target,
 
507
                               even if one is available.
 
508
        :param preserve_stacking: When cloning a stacked branch, stack the
 
509
            new branch on top of the other branch's stacked-on branch.
 
510
        :param create_prefix: Create any missing directories leading up to
 
511
            to_transport.
 
512
        :param use_existing_dir: Use an existing directory if one exists.
 
513
        :param no_tree: If set to true prevents creation of a working tree.
 
514
        """
 
515
        raise NotImplementedError(self.clone_on_transport)
 
516
 
 
517
    @classmethod
 
518
    def find_bzrdirs(klass, transport, evaluate=None, list_current=None):
 
519
        """Find control dirs recursively from current location.
 
520
 
 
521
        This is intended primarily as a building block for more sophisticated
 
522
        functionality, like finding trees under a directory, or finding
 
523
        branches that use a given repository.
 
524
 
 
525
        :param evaluate: An optional callable that yields recurse, value,
 
526
            where recurse controls whether this controldir is recursed into
 
527
            and value is the value to yield.  By default, all bzrdirs
 
528
            are recursed into, and the return value is the controldir.
 
529
        :param list_current: if supplied, use this function to list the current
 
530
            directory, instead of Transport.list_dir
 
531
        :return: a generator of found bzrdirs, or whatever evaluate returns.
 
532
        """
 
533
        if list_current is None:
 
534
            def list_current(transport):
 
535
                return transport.list_dir('')
 
536
        if evaluate is None:
 
537
            def evaluate(controldir):
 
538
                return True, controldir
 
539
 
 
540
        pending = [transport]
 
541
        while len(pending) > 0:
 
542
            current_transport = pending.pop()
 
543
            recurse = True
 
544
            try:
 
545
                controldir = klass.open_from_transport(current_transport)
 
546
            except (errors.NotBranchError, errors.PermissionDenied):
 
547
                pass
 
548
            else:
 
549
                recurse, value = evaluate(controldir)
 
550
                yield value
 
551
            try:
 
552
                subdirs = list_current(current_transport)
 
553
            except (errors.NoSuchFile, errors.PermissionDenied):
 
554
                continue
 
555
            if recurse:
 
556
                for subdir in sorted(subdirs, reverse=True):
 
557
                    pending.append(current_transport.clone(subdir))
 
558
 
 
559
    @classmethod
 
560
    def find_branches(klass, transport):
 
561
        """Find all branches under a transport.
 
562
 
 
563
        This will find all branches below the transport, including branches
 
564
        inside other branches.  Where possible, it will use
 
565
        Repository.find_branches.
 
566
 
 
567
        To list all the branches that use a particular Repository, see
 
568
        Repository.find_branches
 
569
        """
 
570
        def evaluate(controldir):
 
571
            try:
 
572
                repository = controldir.open_repository()
 
573
            except errors.NoRepositoryPresent:
 
574
                pass
 
575
            else:
 
576
                return False, ([], repository)
 
577
            return True, (controldir.list_branches(), None)
 
578
        ret = []
 
579
        for branches, repo in klass.find_bzrdirs(
 
580
                transport, evaluate=evaluate):
 
581
            if repo is not None:
 
582
                ret.extend(repo.find_branches())
 
583
            if branches is not None:
 
584
                ret.extend(branches)
 
585
        return ret
 
586
 
 
587
    @classmethod
 
588
    def create_branch_and_repo(klass, base, force_new_repo=False, format=None):
 
589
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
590
 
 
591
        This will use the current default ControlDirFormat unless one is
 
592
        specified, and use whatever
 
593
        repository format that that uses via controldir.create_branch and
 
594
        create_repository. If a shared repository is available that is used
 
595
        preferentially.
 
596
 
 
597
        The created Branch object is returned.
 
598
 
 
599
        :param base: The URL to create the branch at.
 
600
        :param force_new_repo: If True a new repository is always created.
 
601
        :param format: If supplied, the format of branch to create.  If not
 
602
            supplied, the default is used.
 
603
        """
 
604
        controldir = klass.create(base, format)
 
605
        controldir._find_or_create_repository(force_new_repo)
 
606
        return controldir.create_branch()
 
607
 
 
608
    @classmethod
 
609
    def create_branch_convenience(klass, base, force_new_repo=False,
 
610
                                  force_new_tree=None, format=None,
 
611
                                  possible_transports=None):
 
612
        """Create a new ControlDir, Branch and Repository at the url 'base'.
 
613
 
 
614
        This is a convenience function - it will use an existing repository
 
615
        if possible, can be told explicitly whether to create a working tree or
 
616
        not.
 
617
 
 
618
        This will use the current default ControlDirFormat unless one is
 
619
        specified, and use whatever
 
620
        repository format that that uses via ControlDir.create_branch and
 
621
        create_repository. If a shared repository is available that is used
 
622
        preferentially. Whatever repository is used, its tree creation policy
 
623
        is followed.
 
624
 
 
625
        The created Branch object is returned.
 
626
        If a working tree cannot be made due to base not being a file:// url,
 
627
        no error is raised unless force_new_tree is True, in which case no
 
628
        data is created on disk and NotLocalUrl is raised.
 
629
 
 
630
        :param base: The URL to create the branch at.
 
631
        :param force_new_repo: If True a new repository is always created.
 
632
        :param force_new_tree: If True or False force creation of a tree or
 
633
                               prevent such creation respectively.
 
634
        :param format: Override for the controldir format to create.
 
635
        :param possible_transports: An optional reusable transports list.
 
636
        """
 
637
        if force_new_tree:
 
638
            # check for non local urls
 
639
            t = _mod_transport.get_transport(base, possible_transports)
 
640
            if not isinstance(t, local.LocalTransport):
 
641
                raise errors.NotLocalUrl(base)
 
642
        controldir = klass.create(base, format, possible_transports)
 
643
        repo = controldir._find_or_create_repository(force_new_repo)
 
644
        result = controldir.create_branch()
 
645
        if force_new_tree or (repo.make_working_trees() and
 
646
                              force_new_tree is None):
 
647
            try:
 
648
                controldir.create_workingtree()
 
649
            except errors.NotLocalUrl:
 
650
                pass
 
651
        return result
 
652
 
 
653
    @classmethod
 
654
    def create_standalone_workingtree(klass, base, format=None):
 
655
        """Create a new ControlDir, WorkingTree, Branch and Repository at 'base'.
 
656
 
 
657
        'base' must be a local path or a file:// url.
 
658
 
 
659
        This will use the current default ControlDirFormat unless one is
 
660
        specified, and use whatever
 
661
        repository format that that uses for bzrdirformat.create_workingtree,
 
662
        create_branch and create_repository.
 
663
 
 
664
        :param format: Override for the controldir format to create.
 
665
        :return: The WorkingTree object.
 
666
        """
 
667
        t = _mod_transport.get_transport(base)
 
668
        if not isinstance(t, local.LocalTransport):
 
669
            raise errors.NotLocalUrl(base)
 
670
        controldir = klass.create_branch_and_repo(base,
 
671
                                               force_new_repo=True,
 
672
                                               format=format).bzrdir
 
673
        return controldir.create_workingtree()
 
674
 
 
675
    @classmethod
 
676
    def open_unsupported(klass, base):
 
677
        """Open a branch which is not supported."""
 
678
        return klass.open(base, _unsupported=True)
 
679
 
 
680
    @classmethod
 
681
    def open(klass, base, possible_transports=None, probers=None,
 
682
             _unsupported=False):
 
683
        """Open an existing controldir, rooted at 'base' (url).
 
684
 
 
685
        :param _unsupported: a private parameter to the ControlDir class.
 
686
        """
 
687
        t = _mod_transport.get_transport(base, possible_transports)
 
688
        return klass.open_from_transport(t, probers=probers,
 
689
                _unsupported=_unsupported)
 
690
 
 
691
    @classmethod
 
692
    def open_from_transport(klass, transport, _unsupported=False,
 
693
                            probers=None):
 
694
        """Open a controldir within a particular directory.
 
695
 
 
696
        :param transport: Transport containing the controldir.
 
697
        :param _unsupported: private.
 
698
        """
 
699
        for hook in klass.hooks['pre_open']:
 
700
            hook(transport)
 
701
        # Keep initial base since 'transport' may be modified while following
 
702
        # the redirections.
 
703
        base = transport.base
 
704
        def find_format(transport):
 
705
            return transport, ControlDirFormat.find_format(transport,
 
706
                probers=probers)
 
707
 
 
708
        def redirected(transport, e, redirection_notice):
 
709
            redirected_transport = transport._redirected_to(e.source, e.target)
 
710
            if redirected_transport is None:
 
711
                raise errors.NotBranchError(base)
 
712
            trace.note(gettext('{0} is{1} redirected to {2}').format(
 
713
                 transport.base, e.permanently, redirected_transport.base))
 
714
            return redirected_transport
 
715
 
 
716
        try:
 
717
            transport, format = _mod_transport.do_catching_redirections(
 
718
                find_format, transport, redirected)
 
719
        except errors.TooManyRedirections:
 
720
            raise errors.NotBranchError(base)
 
721
 
 
722
        format.check_support_status(_unsupported)
 
723
        return format.open(transport, _found=True)
 
724
 
 
725
    @classmethod
 
726
    def open_containing(klass, url, possible_transports=None):
 
727
        """Open an existing branch which contains url.
 
728
 
 
729
        :param url: url to search from.
 
730
 
 
731
        See open_containing_from_transport for more detail.
 
732
        """
 
733
        transport = _mod_transport.get_transport(url, possible_transports)
 
734
        return klass.open_containing_from_transport(transport)
 
735
 
 
736
    @classmethod
 
737
    def open_containing_from_transport(klass, a_transport):
 
738
        """Open an existing branch which contains a_transport.base.
 
739
 
 
740
        This probes for a branch at a_transport, and searches upwards from there.
 
741
 
 
742
        Basically we keep looking up until we find the control directory or
 
743
        run into the root.  If there isn't one, raises NotBranchError.
 
744
        If there is one and it is either an unrecognised format or an unsupported
 
745
        format, UnknownFormatError or UnsupportedFormatError are raised.
 
746
        If there is one, it is returned, along with the unused portion of url.
 
747
 
 
748
        :return: The ControlDir that contains the path, and a Unicode path
 
749
                for the rest of the URL.
 
750
        """
 
751
        # this gets the normalised url back. I.e. '.' -> the full path.
 
752
        url = a_transport.base
 
753
        while True:
 
754
            try:
 
755
                result = klass.open_from_transport(a_transport)
 
756
                return result, urlutils.unescape(a_transport.relpath(url))
 
757
            except errors.NotBranchError, e:
 
758
                pass
 
759
            except errors.PermissionDenied:
 
760
                pass
 
761
            try:
 
762
                new_t = a_transport.clone('..')
 
763
            except errors.InvalidURLJoin:
 
764
                # reached the root, whatever that may be
 
765
                raise errors.NotBranchError(path=url)
 
766
            if new_t.base == a_transport.base:
 
767
                # reached the root, whatever that may be
 
768
                raise errors.NotBranchError(path=url)
 
769
            a_transport = new_t
 
770
 
 
771
    @classmethod
 
772
    def open_tree_or_branch(klass, location):
 
773
        """Return the branch and working tree at a location.
 
774
 
 
775
        If there is no tree at the location, tree will be None.
 
776
        If there is no branch at the location, an exception will be
 
777
        raised
 
778
        :return: (tree, branch)
 
779
        """
 
780
        controldir = klass.open(location)
 
781
        return controldir._get_tree_branch()
 
782
 
 
783
    @classmethod
 
784
    def open_containing_tree_or_branch(klass, location,
 
785
            possible_transports=None):
 
786
        """Return the branch and working tree contained by a location.
 
787
 
 
788
        Returns (tree, branch, relpath).
 
789
        If there is no tree at containing the location, tree will be None.
 
790
        If there is no branch containing the location, an exception will be
 
791
        raised
 
792
        relpath is the portion of the path that is contained by the branch.
 
793
        """
 
794
        controldir, relpath = klass.open_containing(location,
 
795
            possible_transports=possible_transports)
 
796
        tree, branch = controldir._get_tree_branch()
 
797
        return tree, branch, relpath
 
798
 
 
799
    @classmethod
 
800
    def open_containing_tree_branch_or_repository(klass, location):
 
801
        """Return the working tree, branch and repo contained by a location.
 
802
 
 
803
        Returns (tree, branch, repository, relpath).
 
804
        If there is no tree containing the location, tree will be None.
 
805
        If there is no branch containing the location, branch will be None.
 
806
        If there is no repository containing the location, repository will be
 
807
        None.
 
808
        relpath is the portion of the path that is contained by the innermost
 
809
        ControlDir.
 
810
 
 
811
        If no tree, branch or repository is found, a NotBranchError is raised.
 
812
        """
 
813
        controldir, relpath = klass.open_containing(location)
 
814
        try:
 
815
            tree, branch = controldir._get_tree_branch()
 
816
        except errors.NotBranchError:
 
817
            try:
 
818
                repo = controldir.find_repository()
 
819
                return None, None, repo, relpath
 
820
            except (errors.NoRepositoryPresent):
 
821
                raise errors.NotBranchError(location)
 
822
        return tree, branch, branch.repository, relpath
 
823
 
 
824
    @classmethod
 
825
    def create(klass, base, format=None, possible_transports=None):
 
826
        """Create a new ControlDir at the url 'base'.
 
827
 
 
828
        :param format: If supplied, the format of branch to create.  If not
 
829
            supplied, the default is used.
 
830
        :param possible_transports: If supplied, a list of transports that
 
831
            can be reused to share a remote connection.
 
832
        """
 
833
        if klass is not ControlDir:
 
834
            raise AssertionError("ControlDir.create always creates the"
 
835
                "default format, not one of %r" % klass)
 
836
        t = _mod_transport.get_transport(base, possible_transports)
 
837
        t.ensure_base()
 
838
        if format is None:
 
839
            format = ControlDirFormat.get_default_format()
 
840
        return format.initialize_on_transport(t)
 
841
 
 
842
 
 
843
class ControlDirHooks(hooks.Hooks):
 
844
    """Hooks for ControlDir operations."""
 
845
 
 
846
    def __init__(self):
 
847
        """Create the default hooks."""
 
848
        hooks.Hooks.__init__(self, "bzrlib.controldir", "ControlDir.hooks")
 
849
        self.add_hook('pre_open',
 
850
            "Invoked before attempting to open a ControlDir with the transport "
 
851
            "that the open will use.", (1, 14))
 
852
        self.add_hook('post_repo_init',
 
853
            "Invoked after a repository has been initialized. "
 
854
            "post_repo_init is called with a "
 
855
            "bzrlib.controldir.RepoInitHookParams.",
 
856
            (2, 2))
 
857
 
 
858
# install the default hooks
 
859
ControlDir.hooks = ControlDirHooks()
 
860
 
 
861
 
 
862
class ControlComponentFormat(object):
 
863
    """A component that can live inside of a control directory."""
 
864
 
 
865
    upgrade_recommended = False
 
866
 
 
867
    def get_format_description(self):
 
868
        """Return the short description for this format."""
 
869
        raise NotImplementedError(self.get_format_description)
 
870
 
 
871
    def is_supported(self):
 
872
        """Is this format supported?
 
873
 
 
874
        Supported formats must be initializable and openable.
 
875
        Unsupported formats may not support initialization or committing or
 
876
        some other features depending on the reason for not being supported.
 
877
        """
 
878
        return True
 
879
 
 
880
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
881
        basedir=None):
 
882
        """Give an error or warning on old formats.
 
883
 
 
884
        :param allow_unsupported: If true, allow opening
 
885
            formats that are strongly deprecated, and which may
 
886
            have limited functionality.
 
887
 
 
888
        :param recommend_upgrade: If true (default), warn
 
889
            the user through the ui object that they may wish
 
890
            to upgrade the object.
 
891
        """
 
892
        if not allow_unsupported and not self.is_supported():
 
893
            # see open_downlevel to open legacy branches.
 
894
            raise errors.UnsupportedFormatError(format=self)
 
895
        if recommend_upgrade and self.upgrade_recommended:
 
896
            ui.ui_factory.recommend_upgrade(
 
897
                self.get_format_description(), basedir)
 
898
 
 
899
    @classmethod
 
900
    def get_format_string(cls):
 
901
        raise NotImplementedError(cls.get_format_string)
 
902
 
 
903
 
 
904
class ControlComponentFormatRegistry(registry.FormatRegistry):
 
905
    """A registry for control components (branch, workingtree, repository)."""
 
906
 
 
907
    def __init__(self, other_registry=None):
 
908
        super(ControlComponentFormatRegistry, self).__init__(other_registry)
 
909
        self._extra_formats = []
 
910
 
 
911
    def register(self, format):
 
912
        """Register a new format."""
 
913
        super(ControlComponentFormatRegistry, self).register(
 
914
            format.get_format_string(), format)
 
915
 
 
916
    def remove(self, format):
 
917
        """Remove a registered format."""
 
918
        super(ControlComponentFormatRegistry, self).remove(
 
919
            format.get_format_string())
 
920
 
 
921
    def register_extra(self, format):
 
922
        """Register a format that can not be used in a metadir.
 
923
 
 
924
        This is mainly useful to allow custom repository formats, such as older
 
925
        Bazaar formats and foreign formats, to be tested.
 
926
        """
 
927
        self._extra_formats.append(registry._ObjectGetter(format))
 
928
 
 
929
    def remove_extra(self, format):
 
930
        """Remove an extra format.
 
931
        """
 
932
        self._extra_formats.remove(registry._ObjectGetter(format))
 
933
 
 
934
    def register_extra_lazy(self, module_name, member_name):
 
935
        """Register a format lazily.
 
936
        """
 
937
        self._extra_formats.append(
 
938
            registry._LazyObjectGetter(module_name, member_name))
 
939
 
 
940
    def _get_extra(self):
 
941
        """Return all "extra" formats, not usable in meta directories."""
 
942
        result = []
 
943
        for getter in self._extra_formats:
 
944
            f = getter.get_obj()
 
945
            if callable(f):
 
946
                f = f()
 
947
            result.append(f)
 
948
        return result
 
949
 
 
950
    def _get_all(self):
 
951
        """Return all formats, even those not usable in metadirs.
 
952
        """
 
953
        result = []
 
954
        for name in self.keys():
 
955
            fmt = self.get(name)
 
956
            if callable(fmt):
 
957
                fmt = fmt()
 
958
            result.append(fmt)
 
959
        return result + self._get_extra()
 
960
 
 
961
    def _get_all_modules(self):
 
962
        """Return a set of the modules providing objects."""
 
963
        modules = set()
 
964
        for name in self.keys():
 
965
            modules.add(self._get_module(name))
 
966
        for getter in self._extra_formats:
 
967
            modules.add(getter.get_module())
 
968
        return modules
 
969
 
 
970
 
 
971
class Converter(object):
 
972
    """Converts a disk format object from one format to another."""
 
973
 
 
974
    def convert(self, to_convert, pb):
 
975
        """Perform the conversion of to_convert, giving feedback via pb.
 
976
 
 
977
        :param to_convert: The disk object to convert.
 
978
        :param pb: a progress bar to use for progress information.
 
979
        """
 
980
 
 
981
    def step(self, message):
 
982
        """Update the pb by a step."""
 
983
        self.count +=1
 
984
        self.pb.update(message, self.count, self.total)
546
985
 
547
986
 
548
987
class ControlDirFormat(object):
562
1001
    object will be created every system load.
563
1002
 
564
1003
    :cvar colocated_branches: Whether this formats supports colocated branches.
 
1004
    :cvar supports_workingtrees: This control directory can co-exist with a
 
1005
        working tree.
565
1006
    """
566
1007
 
567
1008
    _default_format = None
568
1009
    """The default format used for new control directories."""
569
1010
 
570
 
    _formats = []
571
 
    """The registered control formats - .bzr, ....
572
 
 
573
 
    This is a list of ControlDirFormat objects.
574
 
    """
575
 
 
576
1011
    _server_probers = []
577
1012
    """The registered server format probers, e.g. RemoteBzrProber.
578
1013
 
589
1024
    """Whether co-located branches are supported for this control dir format.
590
1025
    """
591
1026
 
 
1027
    supports_workingtrees = True
 
1028
    """Whether working trees can exist in control directories of this format.
 
1029
    """
 
1030
 
 
1031
    fixed_components = False
 
1032
    """Whether components can not change format independent of the control dir.
 
1033
    """
 
1034
 
 
1035
    upgrade_recommended = False
 
1036
    """Whether an upgrade from this format is recommended."""
 
1037
 
592
1038
    def get_format_description(self):
593
1039
        """Return the short description for this format."""
594
1040
        raise NotImplementedError(self.get_format_description)
610
1056
    def is_supported(self):
611
1057
        """Is this format supported?
612
1058
 
613
 
        Supported formats must be initializable and openable.
 
1059
        Supported formats must be openable.
614
1060
        Unsupported formats may not support initialization or committing or
615
1061
        some other features depending on the reason for not being supported.
616
1062
        """
617
1063
        return True
618
1064
 
 
1065
    def is_initializable(self):
 
1066
        """Whether new control directories of this format can be initialized.
 
1067
        """
 
1068
        return self.is_supported()
 
1069
 
 
1070
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
1071
        basedir=None):
 
1072
        """Give an error or warning on old formats.
 
1073
 
 
1074
        :param allow_unsupported: If true, allow opening
 
1075
            formats that are strongly deprecated, and which may
 
1076
            have limited functionality.
 
1077
 
 
1078
        :param recommend_upgrade: If true (default), warn
 
1079
            the user through the ui object that they may wish
 
1080
            to upgrade the object.
 
1081
        """
 
1082
        if not allow_unsupported and not self.is_supported():
 
1083
            # see open_downlevel to open legacy branches.
 
1084
            raise errors.UnsupportedFormatError(format=self)
 
1085
        if recommend_upgrade and self.upgrade_recommended:
 
1086
            ui.ui_factory.recommend_upgrade(
 
1087
                self.get_format_description(), basedir)
 
1088
 
619
1089
    def same_model(self, target_format):
620
1090
        return (self.repository_format.rich_root_data ==
621
1091
            target_format.rich_root_data)
625
1095
        """Register a format that does not use '.bzr' for its control dir.
626
1096
 
627
1097
        """
628
 
        klass._formats.append(format)
 
1098
        raise errors.BzrError("ControlDirFormat.register_format() has been "
 
1099
            "removed in Bazaar 2.4. Please upgrade your plugins.")
629
1100
 
630
1101
    @classmethod
631
1102
    def register_prober(klass, prober):
657
1128
        return self.get_format_description().rstrip()
658
1129
 
659
1130
    @classmethod
660
 
    def unregister_format(klass, format):
661
 
        klass._formats.remove(format)
 
1131
    def all_probers(klass):
 
1132
        return klass._server_probers + klass._probers
662
1133
 
663
1134
    @classmethod
664
1135
    def known_formats(klass):
665
1136
        """Return all the known formats.
666
1137
        """
667
 
        return set(klass._formats)
 
1138
        result = set()
 
1139
        for prober_kls in klass.all_probers():
 
1140
            result.update(prober_kls.known_formats())
 
1141
        return result
668
1142
 
669
1143
    @classmethod
670
 
    def find_format(klass, transport, _server_formats=True):
 
1144
    def find_format(klass, transport, probers=None):
671
1145
        """Return the format present at transport."""
672
 
        if _server_formats:
673
 
            _probers = klass._server_probers + klass._probers
674
 
        else:
675
 
            _probers = klass._probers
676
 
        for prober_kls in _probers:
 
1146
        if probers is None:
 
1147
            probers = klass.all_probers()
 
1148
        for prober_kls in probers:
677
1149
            prober = prober_kls()
678
1150
            try:
679
1151
                return prober.probe_transport(transport)
692
1164
        Subclasses should typically override initialize_on_transport
693
1165
        instead of this method.
694
1166
        """
695
 
        return self.initialize_on_transport(get_transport(url,
696
 
                                                          possible_transports))
 
1167
        return self.initialize_on_transport(
 
1168
            _mod_transport.get_transport(url, possible_transports))
 
1169
 
697
1170
    def initialize_on_transport(self, transport):
698
1171
        """Initialize a new controldir in the base directory of a Transport."""
699
1172
        raise NotImplementedError(self.initialize_on_transport)
757
1230
        """Return the current default format."""
758
1231
        return klass._default_format
759
1232
 
 
1233
    def supports_transport(self, transport):
 
1234
        """Check if this format can be opened over a particular transport.
 
1235
        """
 
1236
        raise NotImplementedError(self.supports_transport)
 
1237
 
760
1238
 
761
1239
class Prober(object):
762
 
    """Abstract class that can be used to detect a particular kind of 
 
1240
    """Abstract class that can be used to detect a particular kind of
763
1241
    control directory.
764
1242
 
765
 
    At the moment this just contains a single method to probe a particular 
766
 
    transport, but it may be extended in the future to e.g. avoid 
 
1243
    At the moment this just contains a single method to probe a particular
 
1244
    transport, but it may be extended in the future to e.g. avoid
767
1245
    multiple levels of probing for Subversion repositories.
 
1246
 
 
1247
    See BzrProber and RemoteBzrProber in bzrlib.bzrdir for the
 
1248
    probers that detect .bzr/ directories and Bazaar smart servers,
 
1249
    respectively.
 
1250
 
 
1251
    Probers should be registered using the register_server_prober or
 
1252
    register_prober methods on ControlDirFormat.
768
1253
    """
769
1254
 
770
1255
    def probe_transport(self, transport):
777
1262
        """
778
1263
        raise NotImplementedError(self.probe_transport)
779
1264
 
 
1265
    @classmethod
 
1266
    def known_formats(klass):
 
1267
        """Return the control dir formats known by this prober.
 
1268
 
 
1269
        Multiple probers can return the same formats, so this should
 
1270
        return a set.
 
1271
 
 
1272
        :return: A set of known formats.
 
1273
        """
 
1274
        raise NotImplementedError(klass.known_formats)
 
1275
 
780
1276
 
781
1277
class ControlDirFormatInfo(object):
782
1278
 
791
1287
    """Registry of user-selectable ControlDir subformats.
792
1288
 
793
1289
    Differs from ControlDirFormat._formats in that it provides sub-formats,
794
 
    e.g. ControlDirMeta1 with weave repository.  Also, it's more user-oriented.
 
1290
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
795
1291
    """
796
1292
 
797
1293
    def __init__(self):
914
1410
            return output
915
1411
 
916
1412
 
 
1413
class RepoInitHookParams(object):
 
1414
    """Object holding parameters passed to `*_repo_init` hooks.
 
1415
 
 
1416
    There are 4 fields that hooks may wish to access:
 
1417
 
 
1418
    :ivar repository: Repository created
 
1419
    :ivar format: Repository format
 
1420
    :ivar bzrdir: The controldir for the repository
 
1421
    :ivar shared: The repository is shared
 
1422
    """
 
1423
 
 
1424
    def __init__(self, repository, format, controldir, shared):
 
1425
        """Create a group of RepoInitHook parameters.
 
1426
 
 
1427
        :param repository: Repository created
 
1428
        :param format: Repository format
 
1429
        :param controldir: The controldir for the repository
 
1430
        :param shared: The repository is shared
 
1431
        """
 
1432
        self.repository = repository
 
1433
        self.format = format
 
1434
        self.bzrdir = controldir
 
1435
        self.shared = shared
 
1436
 
 
1437
    def __eq__(self, other):
 
1438
        return self.__dict__ == other.__dict__
 
1439
 
 
1440
    def __repr__(self):
 
1441
        if self.repository:
 
1442
            return "<%s for %s>" % (self.__class__.__name__,
 
1443
                self.repository)
 
1444
        else:
 
1445
            return "<%s for %s>" % (self.__class__.__name__,
 
1446
                self.bzrdir)
 
1447
 
 
1448
 
917
1449
# Please register new formats after old formats so that formats
918
1450
# appear in chronological order and format descriptions can build
919
1451
# on previous ones.
920
1452
format_registry = ControlDirFormatRegistry()
 
1453
 
 
1454
network_format_registry = registry.FormatRegistry()
 
1455
"""Registry of formats indexed by their network name.
 
1456
 
 
1457
The network name for a ControlDirFormat is an identifier that can be used when
 
1458
referring to formats with smart server operations. See
 
1459
ControlDirFormat.network_name() for more detail.
 
1460
"""