~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/controldir.py

  • Committer: Martin Pool
  • Date: 2011-01-20 23:07:25 UTC
  • mfrom: (5626 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5630.
  • Revision ID: mbp@canonical.com-20110120230725-12l7ltnko5x3fgnz
merge news

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
import textwrap
28
28
 
29
29
from bzrlib import (
 
30
    cleanup,
30
31
    errors,
 
32
    graph,
31
33
    revision as _mod_revision,
32
 
    transport as _mod_transport,
33
 
    ui,
34
34
    urlutils,
35
35
    )
36
36
from bzrlib.push import (
37
37
    PushResult,
38
38
    )
 
39
from bzrlib.trace import (
 
40
    mutter,
 
41
    )
 
42
from bzrlib.transport import (
 
43
    get_transport,
 
44
    local,
 
45
    )
39
46
 
40
47
""")
41
48
 
133
140
        """
134
141
        raise NotImplementedError(self.needs_format_conversion)
135
142
 
136
 
    def create_repository(self, shared=False):
137
 
        """Create a new repository in this control directory.
138
 
 
139
 
        :param shared: If a shared repository should be created
140
 
        :return: The newly created repository
141
 
        """
142
 
        raise NotImplementedError(self.create_repository)
143
 
 
144
143
    def destroy_repository(self):
145
144
        """Destroy the repository in this ControlDir."""
146
145
        raise NotImplementedError(self.destroy_repository)
147
146
 
148
 
    def create_branch(self, name=None, repository=None,
149
 
                      append_revisions_only=None):
 
147
    def create_branch(self, name=None, repository=None):
150
148
        """Create a branch in this ControlDir.
151
149
 
152
150
        :param name: Name of the colocated branch to create, None for
153
151
            the default branch.
154
 
        :param append_revisions_only: Whether this branch should only allow
155
 
            appending new revisions to its history.
156
152
 
157
153
        The controldirs format will control what branch format is created.
158
154
        For more control see BranchFormatXX.create(a_controldir).
196
192
        """
197
193
        raise NotImplementedError(self.destroy_workingtree_metadata)
198
194
 
199
 
    def find_branch_format(self, name=None):
200
 
        """Find the branch 'format' for this bzrdir.
201
 
 
202
 
        This might be a synthetic object for e.g. RemoteBranch and SVN.
203
 
        """
204
 
        raise NotImplementedError(self.find_branch_format)
205
 
 
206
195
    def get_branch_reference(self, name=None):
207
196
        """Return the referenced URL for the branch in this controldir.
208
197
 
217
206
            raise errors.NoColocatedBranchSupport(self)
218
207
        return None
219
208
 
 
209
    def get_branch_transport(self, branch_format, name=None):
 
210
        """Get the transport for use by branch format in this ControlDir.
 
211
 
 
212
        Note that bzr dirs that do not support format strings will raise
 
213
        IncompatibleFormat if the branch format they are given has
 
214
        a format string, and vice versa.
 
215
 
 
216
        If branch_format is None, the transport is returned with no
 
217
        checking. If it is not None, then the returned transport is
 
218
        guaranteed to point to an existing directory ready for use.
 
219
        """
 
220
        raise NotImplementedError(self.get_branch_transport)
 
221
 
 
222
    def get_repository_transport(self, repository_format):
 
223
        """Get the transport for use by repository format in this ControlDir.
 
224
 
 
225
        Note that bzr dirs that do not support format strings will raise
 
226
        IncompatibleFormat if the repository format they are given has
 
227
        a format string, and vice versa.
 
228
 
 
229
        If repository_format is None, the transport is returned with no
 
230
        checking. If it is not None, then the returned transport is
 
231
        guaranteed to point to an existing directory ready for use.
 
232
        """
 
233
        raise NotImplementedError(self.get_repository_transport)
 
234
 
 
235
    def get_workingtree_transport(self, tree_format):
 
236
        """Get the transport for use by workingtree format in this ControlDir.
 
237
 
 
238
        Note that bzr dirs that do not support format strings will raise
 
239
        IncompatibleFormat if the workingtree format they are given has a
 
240
        format string, and vice versa.
 
241
 
 
242
        If workingtree_format is None, the transport is returned with no
 
243
        checking. If it is not None, then the returned transport is
 
244
        guaranteed to point to an existing directory ready for use.
 
245
        """
 
246
        raise NotImplementedError(self.get_workingtree_transport)
 
247
 
220
248
    def open_branch(self, name=None, unsupported=False,
221
249
                    ignore_fallbacks=False):
222
250
        """Open the branch object at this ControlDir if one is present.
236
264
        get at a repository.
237
265
 
238
266
        :param _unsupported: a private parameter, not part of the api.
239
 
 
240
267
        TODO: static convenience version of this?
241
268
        """
242
269
        raise NotImplementedError(self.open_repository)
276
303
        except errors.NotBranchError:
277
304
            return False
278
305
 
279
 
    def _get_selected_branch(self):
280
 
        """Return the name of the branch selected by the user.
281
 
 
282
 
        :return: Name of the branch selected by the user, or None.
283
 
        """
284
 
        branch = self.root_transport.get_segment_parameters().get("branch")
285
 
        if branch is not None:
286
 
            branch = urlutils.unescape(branch)
287
 
        return branch
288
 
 
289
306
    def has_workingtree(self):
290
307
        """Tell if this controldir contains a working tree.
291
308
 
335
352
        whether one existed before or not; and a local branch is always
336
353
        created.
337
354
 
338
 
        :param revision_id: if revision_id is not None, then the clone
339
 
            operation may tune itself to download less data.
 
355
        if revision_id is not None, then the clone operation may tune
 
356
            itself to download less data.
340
357
        :param accelerator_tree: A tree which can be used for retrieving file
341
358
            contents more quickly than the revision tree, i.e. a workingtree.
342
359
            The revision tree will be used for cases where accelerator_tree's
348
365
        :param create_tree_if_local: If true, a working-tree will be created
349
366
            when working locally.
350
367
        """
351
 
        raise NotImplementedError(self.sprout)
 
368
        operation = cleanup.OperationWithCleanups(self._sprout)
 
369
        return operation.run(url, revision_id=revision_id,
 
370
            force_new_repo=force_new_repo, recurse=recurse,
 
371
            possible_transports=possible_transports,
 
372
            accelerator_tree=accelerator_tree, hardlink=hardlink,
 
373
            stacked=stacked, source_branch=source_branch,
 
374
            create_tree_if_local=create_tree_if_local)
 
375
 
 
376
    def _sprout(self, op, url, revision_id=None, force_new_repo=False,
 
377
               recurse='down', possible_transports=None,
 
378
               accelerator_tree=None, hardlink=False, stacked=False,
 
379
               source_branch=None, create_tree_if_local=True):
 
380
        add_cleanup = op.add_cleanup
 
381
        target_transport = get_transport(url, possible_transports)
 
382
        target_transport.ensure_base()
 
383
        cloning_format = self.cloning_metadir(stacked)
 
384
        # Create/update the result branch
 
385
        result = cloning_format.initialize_on_transport(target_transport)
 
386
        # if a stacked branch wasn't requested, we don't create one
 
387
        # even if the origin was stacked
 
388
        stacked_branch_url = None
 
389
        if source_branch is not None:
 
390
            add_cleanup(source_branch.lock_read().unlock)
 
391
            if stacked:
 
392
                stacked_branch_url = self.root_transport.base
 
393
            source_repository = source_branch.repository
 
394
        else:
 
395
            try:
 
396
                source_branch = self.open_branch()
 
397
                source_repository = source_branch.repository
 
398
                if stacked:
 
399
                    stacked_branch_url = self.root_transport.base
 
400
            except errors.NotBranchError:
 
401
                source_branch = None
 
402
                try:
 
403
                    source_repository = self.open_repository()
 
404
                except errors.NoRepositoryPresent:
 
405
                    source_repository = None
 
406
                else:
 
407
                    add_cleanup(source_repository.lock_read().unlock)
 
408
            else:
 
409
                add_cleanup(source_branch.lock_read().unlock)
 
410
        repository_policy = result.determine_repository_policy(
 
411
            force_new_repo, stacked_branch_url, require_stacking=stacked)
 
412
        result_repo, is_new_repo = repository_policy.acquire_repository()
 
413
        add_cleanup(result_repo.lock_write().unlock)
 
414
        is_stacked = stacked or (len(result_repo._fallback_repositories) != 0)
 
415
        if is_new_repo and revision_id is not None and not is_stacked:
 
416
            fetch_spec = graph.PendingAncestryResult(
 
417
                [revision_id], source_repository)
 
418
        else:
 
419
            fetch_spec = None
 
420
        if source_repository is not None:
 
421
            # Fetch while stacked to prevent unstacked fetch from
 
422
            # Branch.sprout.
 
423
            if fetch_spec is None:
 
424
                result_repo.fetch(source_repository, revision_id=revision_id)
 
425
            else:
 
426
                result_repo.fetch(source_repository, fetch_spec=fetch_spec)
 
427
 
 
428
        if source_branch is None:
 
429
            # this is for sprouting a controldir without a branch; is that
 
430
            # actually useful?
 
431
            # Not especially, but it's part of the contract.
 
432
            result_branch = result.create_branch()
 
433
        else:
 
434
            result_branch = source_branch.sprout(result,
 
435
                revision_id=revision_id, repository_policy=repository_policy,
 
436
                repository=result_repo)
 
437
        mutter("created new branch %r" % (result_branch,))
 
438
 
 
439
        # Create/update the result working tree
 
440
        if (create_tree_if_local and
 
441
            isinstance(target_transport, local.LocalTransport) and
 
442
            (result_repo is None or result_repo.make_working_trees())):
 
443
            wt = result.create_workingtree(accelerator_tree=accelerator_tree,
 
444
                hardlink=hardlink, from_branch=result_branch)
 
445
            wt.lock_write()
 
446
            try:
 
447
                if wt.path2id('') is None:
 
448
                    try:
 
449
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
450
                    except errors.NoWorkingTree:
 
451
                        pass
 
452
            finally:
 
453
                wt.unlock()
 
454
        else:
 
455
            wt = None
 
456
        if recurse == 'down':
 
457
            if wt is not None:
 
458
                basis = wt.basis_tree()
 
459
                basis.lock_read()
 
460
                subtrees = basis.iter_references()
 
461
            elif result_branch is not None:
 
462
                basis = result_branch.basis_tree()
 
463
                basis.lock_read()
 
464
                subtrees = basis.iter_references()
 
465
            elif source_branch is not None:
 
466
                basis = source_branch.basis_tree()
 
467
                basis.lock_read()
 
468
                subtrees = basis.iter_references()
 
469
            else:
 
470
                subtrees = []
 
471
                basis = None
 
472
            try:
 
473
                for path, file_id in subtrees:
 
474
                    target = urlutils.join(url, urlutils.escape(path))
 
475
                    sublocation = source_branch.reference_parent(file_id, path)
 
476
                    sublocation.bzrdir.sprout(target,
 
477
                        basis.get_reference_revision(file_id, path),
 
478
                        force_new_repo=force_new_repo, recurse=recurse,
 
479
                        stacked=stacked)
 
480
            finally:
 
481
                if basis is not None:
 
482
                    basis.unlock()
 
483
        return result
352
484
 
353
485
    def push_branch(self, source, revision_id=None, overwrite=False, 
354
486
        remember=False, create_prefix=False):
370
502
        if br_to is None:
371
503
            # We have a repository but no branch, copy the revisions, and then
372
504
            # create a branch.
373
 
            if revision_id is None:
374
 
                # No revision supplied by the user, default to the branch
375
 
                # revision
376
 
                revision_id = source.last_revision()
377
505
            repository_to.fetch(source.repository, revision_id=revision_id)
378
506
            br_to = source.clone(self, revision_id=revision_id)
379
507
            if source.get_push_location() is None or remember:
457
585
        :param preserve_stacking: When cloning a stacked branch, stack the
458
586
            new branch on top of the other branch's stacked-on branch.
459
587
        """
460
 
        return self.clone_on_transport(_mod_transport.get_transport(url),
 
588
        return self.clone_on_transport(get_transport(url),
461
589
                                       revision_id=revision_id,
462
590
                                       force_new_repo=force_new_repo,
463
591
                                       preserve_stacking=preserve_stacking)
464
592
 
465
593
    def clone_on_transport(self, transport, revision_id=None,
466
594
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
467
 
        create_prefix=False, use_existing_dir=True, no_tree=False):
 
595
        create_prefix=False, use_existing_dir=True):
468
596
        """Clone this bzrdir and its contents to transport verbatim.
469
597
 
470
598
        :param transport: The transport for the location to produce the clone
479
607
        :param create_prefix: Create any missing directories leading up to
480
608
            to_transport.
481
609
        :param use_existing_dir: Use an existing directory if one exists.
482
 
        :param no_tree: If set to true prevents creation of a working tree.
483
610
        """
484
611
        raise NotImplementedError(self.clone_on_transport)
485
612
 
486
613
 
487
 
class ControlComponentFormat(object):
488
 
    """A component that can live inside of a .bzr meta directory."""
489
 
 
490
 
    upgrade_recommended = False
491
 
 
492
 
    def get_format_string(self):
493
 
        """Return the format of this format, if usable in meta directories."""
494
 
        raise NotImplementedError(self.get_format_string)
495
 
 
496
 
    def get_format_description(self):
497
 
        """Return the short description for this format."""
498
 
        raise NotImplementedError(self.get_format_description)
499
 
 
500
 
    def is_supported(self):
501
 
        """Is this format supported?
502
 
 
503
 
        Supported formats must be initializable and openable.
504
 
        Unsupported formats may not support initialization or committing or
505
 
        some other features depending on the reason for not being supported.
506
 
        """
507
 
        return True
508
 
 
509
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
510
 
        basedir=None):
511
 
        """Give an error or warning on old formats.
512
 
 
513
 
        :param allow_unsupported: If true, allow opening
514
 
            formats that are strongly deprecated, and which may
515
 
            have limited functionality.
516
 
 
517
 
        :param recommend_upgrade: If true (default), warn
518
 
            the user through the ui object that they may wish
519
 
            to upgrade the object.
520
 
        """
521
 
        if not allow_unsupported and not self.is_supported():
522
 
            # see open_downlevel to open legacy branches.
523
 
            raise errors.UnsupportedFormatError(format=self)
524
 
        if recommend_upgrade and self.upgrade_recommended:
525
 
            ui.ui_factory.recommend_upgrade(
526
 
                self.get_format_description(), basedir)
527
 
 
528
 
 
529
 
class ControlComponentFormatRegistry(registry.FormatRegistry):
530
 
    """A registry for control components (branch, workingtree, repository)."""
531
 
 
532
 
    def __init__(self, other_registry=None):
533
 
        super(ControlComponentFormatRegistry, self).__init__(other_registry)
534
 
        self._extra_formats = []
535
 
 
536
 
    def register(self, format):
537
 
        """Register a new format."""
538
 
        super(ControlComponentFormatRegistry, self).register(
539
 
            format.get_format_string(), format)
540
 
 
541
 
    def remove(self, format):
542
 
        """Remove a registered format."""
543
 
        super(ControlComponentFormatRegistry, self).remove(
544
 
            format.get_format_string())
545
 
 
546
 
    def register_extra(self, format):
547
 
        """Register a format that can not be used in a metadir.
548
 
 
549
 
        This is mainly useful to allow custom repository formats, such as older
550
 
        Bazaar formats and foreign formats, to be tested.
551
 
        """
552
 
        self._extra_formats.append(registry._ObjectGetter(format))
553
 
 
554
 
    def remove_extra(self, format):
555
 
        """Remove an extra format.
556
 
        """
557
 
        self._extra_formats.remove(registry._ObjectGetter(format))
558
 
 
559
 
    def register_extra_lazy(self, module_name, member_name):
560
 
        """Register a format lazily.
561
 
        """
562
 
        self._extra_formats.append(
563
 
            registry._LazyObjectGetter(module_name, member_name))
564
 
 
565
 
    def _get_extra(self):
566
 
        """Return all "extra" formats, not usable in meta directories."""
567
 
        result = []
568
 
        for getter in self._extra_formats:
569
 
            f = getter.get_obj()
570
 
            if callable(f):
571
 
                f = f()
572
 
            result.append(f)
573
 
        return result
574
 
 
575
 
    def _get_all(self):
576
 
        """Return all formats, even those not usable in metadirs.
577
 
        """
578
 
        result = []
579
 
        for name in self.keys():
580
 
            fmt = self.get(name)
581
 
            if callable(fmt):
582
 
                fmt = fmt()
583
 
            result.append(fmt)
584
 
        return result + self._get_extra()
585
 
 
586
 
    def _get_all_modules(self):
587
 
        """Return a set of the modules providing objects."""
588
 
        modules = set()
589
 
        for name in self.keys():
590
 
            modules.add(self._get_module(name))
591
 
        for getter in self._extra_formats:
592
 
            modules.add(getter.get_module())
593
 
        return modules
594
 
 
595
 
 
596
 
class Converter(object):
597
 
    """Converts a disk format object from one format to another."""
598
 
 
599
 
    def convert(self, to_convert, pb):
600
 
        """Perform the conversion of to_convert, giving feedback via pb.
601
 
 
602
 
        :param to_convert: The disk object to convert.
603
 
        :param pb: a progress bar to use for progress information.
604
 
        """
605
 
 
606
 
    def step(self, message):
607
 
        """Update the pb by a step."""
608
 
        self.count +=1
609
 
        self.pb.update(message, self.count, self.total)
610
 
 
611
 
 
612
614
class ControlDirFormat(object):
613
615
    """An encapsulation of the initialization and open routines for a format.
614
616
 
633
635
    _default_format = None
634
636
    """The default format used for new control directories."""
635
637
 
 
638
    _formats = []
 
639
    """The registered control formats - .bzr, ....
 
640
 
 
641
    This is a list of ControlDirFormat objects.
 
642
    """
 
643
 
636
644
    _server_probers = []
637
645
    """The registered server format probers, e.g. RemoteBzrProber.
638
646
 
650
658
    """
651
659
 
652
660
    supports_workingtrees = True
653
 
    """Whether working trees can exist in control directories of this format.
654
 
    """
655
 
 
656
 
    fixed_components = False
657
 
    """Whether components can not change format independent of the control dir.
658
 
    """
659
 
 
660
 
    upgrade_recommended = False
661
 
    """Whether an upgrade from this format is recommended."""
662
661
 
663
662
    def get_format_description(self):
664
663
        """Return the short description for this format."""
681
680
    def is_supported(self):
682
681
        """Is this format supported?
683
682
 
684
 
        Supported formats must be openable.
 
683
        Supported formats must be initializable and openable.
685
684
        Unsupported formats may not support initialization or committing or
686
685
        some other features depending on the reason for not being supported.
687
686
        """
688
687
        return True
689
688
 
690
 
    def is_initializable(self):
691
 
        """Whether new control directories of this format can be initialized.
692
 
        """
693
 
        return self.is_supported()
694
 
 
695
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
696
 
        basedir=None):
697
 
        """Give an error or warning on old formats.
698
 
 
699
 
        :param allow_unsupported: If true, allow opening
700
 
            formats that are strongly deprecated, and which may
701
 
            have limited functionality.
702
 
 
703
 
        :param recommend_upgrade: If true (default), warn
704
 
            the user through the ui object that they may wish
705
 
            to upgrade the object.
706
 
        """
707
 
        if not allow_unsupported and not self.is_supported():
708
 
            # see open_downlevel to open legacy branches.
709
 
            raise errors.UnsupportedFormatError(format=self)
710
 
        if recommend_upgrade and self.upgrade_recommended:
711
 
            ui.ui_factory.recommend_upgrade(
712
 
                self.get_format_description(), basedir)
713
 
 
714
689
    def same_model(self, target_format):
715
690
        return (self.repository_format.rich_root_data ==
716
691
            target_format.rich_root_data)
720
695
        """Register a format that does not use '.bzr' for its control dir.
721
696
 
722
697
        """
723
 
        raise errors.BzrError("ControlDirFormat.register_format() has been "
724
 
            "removed in Bazaar 2.4. Please upgrade your plugins.")
 
698
        klass._formats.append(format)
725
699
 
726
700
    @classmethod
727
701
    def register_prober(klass, prober):
753
727
        return self.get_format_description().rstrip()
754
728
 
755
729
    @classmethod
 
730
    def unregister_format(klass, format):
 
731
        klass._formats.remove(format)
 
732
 
 
733
    @classmethod
756
734
    def known_formats(klass):
757
735
        """Return all the known formats.
758
736
        """
759
 
        result = set()
760
 
        for prober_kls in klass._probers + klass._server_probers:
761
 
            result.update(prober_kls.known_formats())
762
 
        return result
 
737
        return set(klass._formats)
763
738
 
764
739
    @classmethod
765
740
    def find_format(klass, transport, _server_formats=True):
787
762
        Subclasses should typically override initialize_on_transport
788
763
        instead of this method.
789
764
        """
790
 
        return self.initialize_on_transport(
791
 
            _mod_transport.get_transport(url, possible_transports))
792
 
 
 
765
        return self.initialize_on_transport(get_transport(url,
 
766
                                                          possible_transports))
793
767
    def initialize_on_transport(self, transport):
794
768
        """Initialize a new controldir in the base directory of a Transport."""
795
769
        raise NotImplementedError(self.initialize_on_transport)
855
829
 
856
830
 
857
831
class Prober(object):
858
 
    """Abstract class that can be used to detect a particular kind of
 
832
    """Abstract class that can be used to detect a particular kind of 
859
833
    control directory.
860
834
 
861
 
    At the moment this just contains a single method to probe a particular
862
 
    transport, but it may be extended in the future to e.g. avoid
 
835
    At the moment this just contains a single method to probe a particular 
 
836
    transport, but it may be extended in the future to e.g. avoid 
863
837
    multiple levels of probing for Subversion repositories.
864
 
 
865
 
    See BzrProber and RemoteBzrProber in bzrlib.bzrdir for the
866
 
    probers that detect .bzr/ directories and Bazaar smart servers,
867
 
    respectively.
868
 
 
869
 
    Probers should be registered using the register_server_prober or
870
 
    register_prober methods on ControlDirFormat.
871
838
    """
872
839
 
873
840
    def probe_transport(self, transport):
880
847
        """
881
848
        raise NotImplementedError(self.probe_transport)
882
849
 
883
 
    @classmethod
884
 
    def known_formats(cls):
885
 
        """Return the control dir formats known by this prober.
886
 
 
887
 
        Multiple probers can return the same formats, so this should
888
 
        return a set.
889
 
 
890
 
        :return: A set of known formats.
891
 
        """
892
 
        raise NotImplementedError(cls.known_formats)
893
 
 
894
850
 
895
851
class ControlDirFormatInfo(object):
896
852
 
905
861
    """Registry of user-selectable ControlDir subformats.
906
862
 
907
863
    Differs from ControlDirFormat._formats in that it provides sub-formats,
908
 
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
864
    e.g. ControlDirMeta1 with weave repository.  Also, it's more user-oriented.
909
865
    """
910
866
 
911
867
    def __init__(self):