~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Robert Collins
  • Date: 2007-06-28 02:43:50 UTC
  • mfrom: (2553 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2558.
  • Revision ID: robertc@robertcollins.net-20070628024350-z8bdm0y6yz2uyf4o
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
# TODO: remove unittest dependency; put that stuff inside the test suite
24
24
 
25
 
# TODO: The Format probe_transport seems a bit redundant with just trying to
26
 
# open the bzrdir. -- mbp
27
 
#
28
25
# TODO: Can we move specific formats into separate modules to make this file
29
26
# smaller?
30
27
 
44
41
    lockable_files,
45
42
    lockdir,
46
43
    registry,
 
44
    remote,
47
45
    revision as _mod_revision,
48
46
    symbol_versioning,
 
47
    ui,
49
48
    urlutils,
50
49
    xml4,
51
50
    xml5,
 
51
    workingtree,
 
52
    workingtree_4,
52
53
    )
53
54
from bzrlib.osutils import (
54
55
    safe_unicode,
55
56
    sha_strings,
56
57
    sha_string,
57
58
    )
 
59
from bzrlib.smart.client import _SmartClient
 
60
from bzrlib.smart import protocol
58
61
from bzrlib.store.revision.text import TextRevisionStore
59
62
from bzrlib.store.text import TextStore
60
63
from bzrlib.store.versioned import WeaveStore
61
64
from bzrlib.transactions import WriteTransaction
62
 
from bzrlib.transport import get_transport
 
65
from bzrlib.transport import (
 
66
    do_catching_redirections,
 
67
    get_transport,
 
68
    )
63
69
from bzrlib.weave import Weave
64
70
""")
65
71
 
66
 
from bzrlib.trace import mutter
 
72
from bzrlib.trace import (
 
73
    mutter,
 
74
    note,
 
75
    )
67
76
from bzrlib.transport.local import LocalTransport
68
77
 
69
78
 
85
94
        If there is a tree, the tree is opened and break_lock() called.
86
95
        Otherwise, branch is tried, and finally repository.
87
96
        """
 
97
        # XXX: This seems more like a UI function than something that really
 
98
        # belongs in this class.
88
99
        try:
89
100
            thing_to_unlock = self.open_workingtree()
90
101
        except (errors.NotLocalUrl, errors.NoWorkingTree):
107
118
        source_repo_format.check_conversion_target(target_repo_format)
108
119
 
109
120
    @staticmethod
110
 
    def _check_supported(format, allow_unsupported):
111
 
        """Check whether format is a supported format.
112
 
 
113
 
        If allow_unsupported is True, this is a no-op.
 
121
    def _check_supported(format, allow_unsupported,
 
122
        recommend_upgrade=True,
 
123
        basedir=None):
 
124
        """Give an error or warning on old formats.
 
125
 
 
126
        :param format: may be any kind of format - workingtree, branch, 
 
127
        or repository.
 
128
 
 
129
        :param allow_unsupported: If true, allow opening 
 
130
        formats that are strongly deprecated, and which may 
 
131
        have limited functionality.
 
132
 
 
133
        :param recommend_upgrade: If true (default), warn
 
134
        the user through the ui object that they may wish
 
135
        to upgrade the object.
114
136
        """
 
137
        # TODO: perhaps move this into a base Format class; it's not BzrDir
 
138
        # specific. mbp 20070323
115
139
        if not allow_unsupported and not format.is_supported():
116
140
            # see open_downlevel to open legacy branches.
117
141
            raise errors.UnsupportedFormatError(format=format)
 
142
        if recommend_upgrade \
 
143
            and getattr(format, 'upgrade_recommended', False):
 
144
            ui.ui_factory.recommend_upgrade(
 
145
                format.get_format_description(),
 
146
                basedir)
118
147
 
119
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
148
    def clone(self, url, revision_id=None, force_new_repo=False):
120
149
        """Clone this bzrdir and its contents to url verbatim.
121
150
 
122
151
        If urls last component does not exist, it will be created.
126
155
        :param force_new_repo: Do not use a shared repository for the target 
127
156
                               even if one is available.
128
157
        """
129
 
        self._make_tail(url)
130
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
131
 
        result = self._format.initialize(url)
 
158
        return self.clone_on_transport(get_transport(url),
 
159
                                       revision_id=revision_id,
 
160
                                       force_new_repo=force_new_repo)
 
161
 
 
162
    def clone_on_transport(self, transport, revision_id=None,
 
163
                           force_new_repo=False):
 
164
        """Clone this bzrdir and its contents to transport verbatim.
 
165
 
 
166
        If the target directory does not exist, it will be created.
 
167
 
 
168
        if revision_id is not None, then the clone operation may tune
 
169
            itself to download less data.
 
170
        :param force_new_repo: Do not use a shared repository for the target 
 
171
                               even if one is available.
 
172
        """
 
173
        transport.ensure_base()
 
174
        result = self._format.initialize_on_transport(transport)
132
175
        try:
133
176
            local_repo = self.find_repository()
134
177
        except errors.NoRepositoryPresent:
138
181
            if force_new_repo:
139
182
                result_repo = local_repo.clone(
140
183
                    result,
141
 
                    revision_id=revision_id,
142
 
                    basis=basis_repo)
 
184
                    revision_id=revision_id)
143
185
                result_repo.set_make_working_trees(local_repo.make_working_trees())
144
186
            else:
145
187
                try:
146
188
                    result_repo = result.find_repository()
147
189
                    # fetch content this dir needs.
148
 
                    if basis_repo:
149
 
                        # XXX FIXME RBC 20060214 need tests for this when the basis
150
 
                        # is incomplete
151
 
                        result_repo.fetch(basis_repo, revision_id=revision_id)
152
190
                    result_repo.fetch(local_repo, revision_id=revision_id)
153
191
                except errors.NoRepositoryPresent:
154
192
                    # needed to make one anyway.
155
193
                    result_repo = local_repo.clone(
156
194
                        result,
157
 
                        revision_id=revision_id,
158
 
                        basis=basis_repo)
 
195
                        revision_id=revision_id)
159
196
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
160
197
        # 1 if there is a branch present
161
198
        #   make sure its content is available in the target repository
165
202
        except errors.NotBranchError:
166
203
            pass
167
204
        try:
168
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
205
            self.open_workingtree().clone(result)
169
206
        except (errors.NoWorkingTree, errors.NotLocalUrl):
170
207
            pass
171
208
        return result
172
209
 
173
 
    def _get_basis_components(self, basis):
174
 
        """Retrieve the basis components that are available at basis."""
175
 
        if basis is None:
176
 
            return None, None, None
177
 
        try:
178
 
            basis_tree = basis.open_workingtree()
179
 
            basis_branch = basis_tree.branch
180
 
            basis_repo = basis_branch.repository
181
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
182
 
            basis_tree = None
183
 
            try:
184
 
                basis_branch = basis.open_branch()
185
 
                basis_repo = basis_branch.repository
186
 
            except errors.NotBranchError:
187
 
                basis_branch = None
188
 
                try:
189
 
                    basis_repo = basis.open_repository()
190
 
                except errors.NoRepositoryPresent:
191
 
                    basis_repo = None
192
 
        return basis_repo, basis_branch, basis_tree
193
 
 
194
210
    # TODO: This should be given a Transport, and should chdir up; otherwise
195
211
    # this will open a new connection.
196
212
    def _make_tail(self, url):
197
 
        head, tail = urlutils.split(url)
198
 
        if tail and tail != '.':
199
 
            t = get_transport(head)
200
 
            try:
201
 
                t.mkdir(tail)
202
 
            except errors.FileExists:
203
 
                pass
 
213
        t = get_transport(url)
 
214
        t.ensure_base()
204
215
 
205
216
    # TODO: Should take a Transport
206
217
    @classmethod
216
227
        if cls is not BzrDir:
217
228
            raise AssertionError("BzrDir.create always creates the default"
218
229
                " format, not one of %r" % cls)
219
 
        head, tail = urlutils.split(base)
220
 
        if tail and tail != '.':
221
 
            t = get_transport(head)
222
 
            try:
223
 
                t.mkdir(tail)
224
 
            except errors.FileExists:
225
 
                pass
 
230
        t = get_transport(base)
 
231
        t.ensure_base()
226
232
        if format is None:
227
233
            format = BzrDirFormat.get_default_format()
228
234
        return format.initialize(safe_unicode(base))
350
356
        """
351
357
        raise NotImplementedError(self.create_workingtree)
352
358
 
 
359
    def retire_bzrdir(self):
 
360
        """Permanently disable the bzrdir.
 
361
 
 
362
        This is done by renaming it to give the user some ability to recover
 
363
        if there was a problem.
 
364
 
 
365
        This will have horrible consequences if anyone has anything locked or
 
366
        in use.
 
367
        """
 
368
        for i in xrange(10000):
 
369
            try:
 
370
                to_path = '.bzr.retired.%d' % i
 
371
                self.root_transport.rename('.bzr', to_path)
 
372
                note("renamed %s to %s"
 
373
                    % (self.root_transport.abspath('.bzr'), to_path))
 
374
                break
 
375
            except (errors.TransportError, IOError, errors.PathError):
 
376
                pass
 
377
 
353
378
    def destroy_workingtree(self):
354
379
        """Destroy the working tree at this BzrDir.
355
380
 
395
420
                    break
396
421
                else:
397
422
                    continue
398
 
            if ((found_bzrdir.root_transport.base == 
 
423
            if ((found_bzrdir.root_transport.base ==
399
424
                 self.root_transport.base) or repository.is_shared()):
400
425
                return repository
401
426
            else:
402
427
                raise errors.NoRepositoryPresent(self)
403
428
        raise errors.NoRepositoryPresent(self)
404
429
 
 
430
    def get_branch_reference(self):
 
431
        """Return the referenced URL for the branch in this bzrdir.
 
432
 
 
433
        :raises NotBranchError: If there is no Branch.
 
434
        :return: The URL the branch in this bzrdir references if it is a
 
435
            reference branch, or None for regular branches.
 
436
        """
 
437
        return None
 
438
 
405
439
    def get_branch_transport(self, branch_format):
406
440
        """Get the transport for use by branch format in this BzrDir.
407
441
 
432
466
        """Get the transport for use by workingtree format in this BzrDir.
433
467
 
434
468
        Note that bzr dirs that do not support format strings will raise
435
 
        IncompatibleFormat if the workingtree format they are given has
436
 
        a format string, and vice versa.
 
469
        IncompatibleFormat if the workingtree format they are given has a
 
470
        format string, and vice versa.
437
471
 
438
472
        If workingtree_format is None, the transport is returned with no 
439
473
        checking. if it is not None, then the returned transport is
499
533
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
500
534
 
501
535
    @staticmethod
502
 
    def open_from_transport(transport, _unsupported=False):
 
536
    def open_from_transport(transport, _unsupported=False,
 
537
                            _server_formats=True):
503
538
        """Open a bzrdir within a particular directory.
504
539
 
505
540
        :param transport: Transport containing the bzrdir.
506
541
        :param _unsupported: private.
507
542
        """
508
 
        format = BzrDirFormat.find_format(transport)
 
543
        base = transport.base
 
544
 
 
545
        def find_format(transport):
 
546
            return transport, BzrDirFormat.find_format(
 
547
                transport, _server_formats=_server_formats)
 
548
 
 
549
        def redirected(transport, e, redirection_notice):
 
550
            qualified_source = e.get_source_url()
 
551
            relpath = transport.relpath(qualified_source)
 
552
            if not e.target.endswith(relpath):
 
553
                # Not redirected to a branch-format, not a branch
 
554
                raise errors.NotBranchError(path=e.target)
 
555
            target = e.target[:-len(relpath)]
 
556
            note('%s is%s redirected to %s',
 
557
                 transport.base, e.permanently, target)
 
558
            # Let's try with a new transport
 
559
            qualified_target = e.get_target_url()[:-len(relpath)]
 
560
            # FIXME: If 'transport' has a qualifier, this should
 
561
            # be applied again to the new transport *iff* the
 
562
            # schemes used are the same. It's a bit tricky to
 
563
            # verify, so I'll punt for now
 
564
            # -- vila20070212
 
565
            return get_transport(target)
 
566
 
 
567
        try:
 
568
            transport, format = do_catching_redirections(find_format,
 
569
                                                         transport,
 
570
                                                         redirected)
 
571
        except errors.TooManyRedirections:
 
572
            raise errors.NotBranchError(base)
 
573
 
509
574
        BzrDir._check_supported(format, _unsupported)
510
575
        return format.open(transport, _found=True)
511
576
 
551
616
                return result, urlutils.unescape(a_transport.relpath(url))
552
617
            except errors.NotBranchError, e:
553
618
                pass
554
 
            new_t = a_transport.clone('..')
 
619
            try:
 
620
                new_t = a_transport.clone('..')
 
621
            except errors.InvalidURLJoin:
 
622
                # reached the root, whatever that may be
 
623
                raise errors.NotBranchError(path=url)
555
624
            if new_t.base == a_transport.base:
556
625
                # reached the root, whatever that may be
557
626
                raise errors.NotBranchError(path=url)
589
658
        """
590
659
        raise NotImplementedError(self.open_repository)
591
660
 
592
 
    def open_workingtree(self, _unsupported=False):
 
661
    def open_workingtree(self, _unsupported=False,
 
662
            recommend_upgrade=True):
593
663
        """Open the workingtree object at this BzrDir if one is present.
594
 
        
595
 
        TODO: static convenience version of this?
 
664
 
 
665
        :param recommend_upgrade: Optional keyword parameter, when True (the
 
666
            default), emit through the ui module a recommendation that the user
 
667
            upgrade the working tree when the workingtree being opened is old
 
668
            (but still fully supported).
596
669
        """
597
670
        raise NotImplementedError(self.open_workingtree)
598
671
 
620
693
        workingtree and discards it, and that's somewhat expensive.) 
621
694
        """
622
695
        try:
623
 
            self.open_workingtree()
 
696
            self.open_workingtree(recommend_upgrade=False)
624
697
            return True
625
698
        except errors.NoWorkingTree:
626
699
            return False
627
700
 
628
 
    def cloning_metadir(self, basis=None):
 
701
    def _cloning_metadir(self):
629
702
        """Produce a metadir suitable for cloning with"""
630
 
        def related_repository(bzrdir):
 
703
        result_format = self._format.__class__()
 
704
        try:
631
705
            try:
632
 
                branch = bzrdir.open_branch()
633
 
                return branch.repository
 
706
                branch = self.open_branch()
 
707
                source_repository = branch.repository
634
708
            except errors.NotBranchError:
635
709
                source_branch = None
636
 
                return bzrdir.open_repository()
637
 
        result_format = self._format.__class__()
 
710
                source_repository = self.open_repository()
 
711
        except errors.NoRepositoryPresent:
 
712
            source_repository = None
 
713
        else:
 
714
            # XXX TODO: This isinstance is here because we have not implemented
 
715
            # the fix recommended in bug # 103195 - to delegate this choice the
 
716
            # repository itself.
 
717
            repo_format = source_repository._format
 
718
            if not isinstance(repo_format, remote.RemoteRepositoryFormat):
 
719
                result_format.repository_format = repo_format
638
720
        try:
639
 
            try:
640
 
                source_repository = related_repository(self)
641
 
            except errors.NoRepositoryPresent:
642
 
                if basis is None:
643
 
                    raise
644
 
                source_repository = related_repository(self)
645
 
            result_format.repository_format = source_repository._format
646
 
        except errors.NoRepositoryPresent:
647
 
            pass
648
 
        return result_format
649
 
 
650
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
721
            # TODO: Couldn't we just probe for the format in these cases,
 
722
            # rather than opening the whole tree?  It would be a little
 
723
            # faster. mbp 20070401
 
724
            tree = self.open_workingtree(recommend_upgrade=False)
 
725
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
726
            result_format.workingtree_format = None
 
727
        else:
 
728
            result_format.workingtree_format = tree._format.__class__()
 
729
        return result_format, source_repository
 
730
 
 
731
    def cloning_metadir(self):
 
732
        """Produce a metadir suitable for cloning or sprouting with.
 
733
 
 
734
        These operations may produce workingtrees (yes, even though they're
 
735
        "cloning" something that doesn't have a tree, so a viable workingtree
 
736
        format must be selected.
 
737
        """
 
738
        format, repository = self._cloning_metadir()
 
739
        if format._workingtree_format is None:
 
740
            if repository is None:
 
741
                return format
 
742
            tree_format = repository._format._matchingbzrdir.workingtree_format
 
743
            format.workingtree_format = tree_format.__class__()
 
744
        return format
 
745
 
 
746
    def checkout_metadir(self):
 
747
        return self.cloning_metadir()
 
748
 
 
749
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
750
               recurse='down'):
651
751
        """Create a copy of this bzrdir prepared for use as a new line of
652
752
        development.
653
753
 
661
761
        if revision_id is not None, then the clone operation may tune
662
762
            itself to download less data.
663
763
        """
664
 
        self._make_tail(url)
665
 
        cloning_format = self.cloning_metadir(basis)
666
 
        result = cloning_format.initialize(url)
667
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
764
        target_transport = get_transport(url)
 
765
        target_transport.ensure_base()
 
766
        cloning_format = self.cloning_metadir()
 
767
        result = cloning_format.initialize_on_transport(target_transport)
668
768
        try:
669
769
            source_branch = self.open_branch()
670
770
            source_repository = source_branch.repository
673
773
            try:
674
774
                source_repository = self.open_repository()
675
775
            except errors.NoRepositoryPresent:
676
 
                # copy the entire basis one if there is one
677
 
                # but there is no repository.
678
 
                source_repository = basis_repo
 
776
                source_repository = None
679
777
        if force_new_repo:
680
778
            result_repo = None
681
779
        else:
690
788
            result.create_repository()
691
789
        elif source_repository is not None and result_repo is None:
692
790
            # have source, and want to make a new target repo
693
 
            # we don't clone the repo because that preserves attributes
694
 
            # like is_shared(), and we have not yet implemented a 
695
 
            # repository sprout().
696
 
            result_repo = result.create_repository()
697
 
        if result_repo is not None:
 
791
            result_repo = source_repository.sprout(result, revision_id=revision_id)
 
792
        else:
698
793
            # fetch needed content into target.
699
 
            if basis_repo:
700
 
                # XXX FIXME RBC 20060214 need tests for this when the basis
701
 
                # is incomplete
702
 
                result_repo.fetch(basis_repo, revision_id=revision_id)
703
794
            if source_repository is not None:
 
795
                # would rather do 
 
796
                # source_repository.copy_content_into(result_repo, revision_id=revision_id)
 
797
                # so we can override the copy method
704
798
                result_repo.fetch(source_repository, revision_id=revision_id)
705
799
        if source_branch is not None:
706
800
            source_branch.sprout(result, revision_id=revision_id)
710
804
        #       case that the newly sprouted branch is a remote one
711
805
        if result_repo is None or result_repo.make_working_trees():
712
806
            wt = result.create_workingtree()
713
 
            if wt.inventory.root is None:
714
 
                try:
715
 
                    wt.set_root_id(self.open_workingtree.get_root_id())
716
 
                except errors.NoWorkingTree:
717
 
                    pass
 
807
            wt.lock_write()
 
808
            try:
 
809
                if wt.path2id('') is None:
 
810
                    try:
 
811
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
812
                    except errors.NoWorkingTree:
 
813
                        pass
 
814
            finally:
 
815
                wt.unlock()
 
816
        else:
 
817
            wt = None
 
818
        if recurse == 'down':
 
819
            if wt is not None:
 
820
                basis = wt.basis_tree()
 
821
                basis.lock_read()
 
822
                subtrees = basis.iter_references()
 
823
                recurse_branch = wt.branch
 
824
            elif source_branch is not None:
 
825
                basis = source_branch.basis_tree()
 
826
                basis.lock_read()
 
827
                subtrees = basis.iter_references()
 
828
                recurse_branch = source_branch
 
829
            else:
 
830
                subtrees = []
 
831
                basis = None
 
832
            try:
 
833
                for path, file_id in subtrees:
 
834
                    target = urlutils.join(url, urlutils.escape(path))
 
835
                    sublocation = source_branch.reference_parent(file_id, path)
 
836
                    sublocation.bzrdir.sprout(target,
 
837
                        basis.get_reference_revision(file_id, path),
 
838
                        force_new_repo=force_new_repo, recurse=recurse)
 
839
            finally:
 
840
                if basis is not None:
 
841
                    basis.unlock()
718
842
        return result
719
843
 
720
844
 
735
859
        """Pre-splitout bzrdirs do not suffer from stale locks."""
736
860
        raise NotImplementedError(self.break_lock)
737
861
 
738
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
862
    def clone(self, url, revision_id=None, force_new_repo=False):
739
863
        """See BzrDir.clone()."""
740
864
        from bzrlib.workingtree import WorkingTreeFormat2
741
865
        self._make_tail(url)
742
866
        result = self._format._initialize_for_clone(url)
743
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
744
 
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
867
        self.open_repository().clone(result, revision_id=revision_id)
745
868
        from_branch = self.open_branch()
746
869
        from_branch.clone(result, revision_id=revision_id)
747
870
        try:
748
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
871
            self.open_workingtree().clone(result)
749
872
        except errors.NotLocalUrl:
750
873
            # make a new one, this format always has to have one.
751
874
            try:
769
892
    def create_workingtree(self, revision_id=None):
770
893
        """See BzrDir.create_workingtree."""
771
894
        # this looks buggy but is not -really-
 
895
        # because this format creates the workingtree when the bzrdir is
 
896
        # created
772
897
        # clone and sprout will have set the revision_id
773
898
        # and that will have set it for us, its only
774
899
        # specific uses of create_workingtree in isolation
775
900
        # that can do wonky stuff here, and that only
776
901
        # happens for creating checkouts, which cannot be 
777
902
        # done on this format anyway. So - acceptable wart.
778
 
        result = self.open_workingtree()
 
903
        result = self.open_workingtree(recommend_upgrade=False)
779
904
        if revision_id is not None:
780
905
            if revision_id == _mod_revision.NULL_REVISION:
781
906
                result.set_parent_ids([])
837
962
        self._check_supported(format, unsupported)
838
963
        return format.open(self, _found=True)
839
964
 
840
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
965
    def sprout(self, url, revision_id=None, force_new_repo=False):
841
966
        """See BzrDir.sprout()."""
842
967
        from bzrlib.workingtree import WorkingTreeFormat2
843
968
        self._make_tail(url)
844
969
        result = self._format._initialize_for_clone(url)
845
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
846
970
        try:
847
 
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
971
            self.open_repository().clone(result, revision_id=revision_id)
848
972
        except errors.NoRepositoryPresent:
849
973
            pass
850
974
        try:
887
1011
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
888
1012
        return RepositoryFormat5().open(self, _found=True)
889
1013
 
890
 
    def open_workingtree(self, _unsupported=False):
 
1014
    def open_workingtree(self, _unsupported=False,
 
1015
            recommend_upgrade=True):
891
1016
        """See BzrDir.create_workingtree."""
892
1017
        from bzrlib.workingtree import WorkingTreeFormat2
893
 
        return WorkingTreeFormat2().open(self, _found=True)
 
1018
        wt_format = WorkingTreeFormat2()
 
1019
        # we don't warn here about upgrades; that ought to be handled for the
 
1020
        # bzrdir as a whole
 
1021
        return wt_format.open(self, _found=True)
894
1022
 
895
1023
 
896
1024
class BzrDir6(BzrDirPreSplitOut):
904
1032
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
905
1033
        return RepositoryFormat6().open(self, _found=True)
906
1034
 
907
 
    def open_workingtree(self, _unsupported=False):
 
1035
    def open_workingtree(self, _unsupported=False,
 
1036
        recommend_upgrade=True):
908
1037
        """See BzrDir.create_workingtree."""
 
1038
        # we don't warn here about upgrades; that ought to be handled for the
 
1039
        # bzrdir as a whole
909
1040
        from bzrlib.workingtree import WorkingTreeFormat2
910
1041
        return WorkingTreeFormat2().open(self, _found=True)
911
1042
 
934
1065
    def create_workingtree(self, revision_id=None):
935
1066
        """See BzrDir.create_workingtree."""
936
1067
        from bzrlib.workingtree import WorkingTreeFormat
937
 
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
 
1068
        return self._format.workingtree_format.initialize(self, revision_id)
938
1069
 
939
1070
    def destroy_workingtree(self):
940
1071
        """See BzrDir.destroy_workingtree."""
941
 
        wt = self.open_workingtree()
 
1072
        wt = self.open_workingtree(recommend_upgrade=False)
942
1073
        repository = wt.branch.repository
943
1074
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
944
1075
        wt.revert([], old_tree=empty)
947
1078
    def destroy_workingtree_metadata(self):
948
1079
        self.transport.delete_tree('checkout')
949
1080
 
 
1081
    def find_branch_format(self):
 
1082
        """Find the branch 'format' for this bzrdir.
 
1083
 
 
1084
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
1085
        """
 
1086
        from bzrlib.branch import BranchFormat
 
1087
        return BranchFormat.find_format(self)
 
1088
 
950
1089
    def _get_mkdir_mode(self):
951
1090
        """Figure out the mode to use when creating a bzrdir subdir."""
952
1091
        temp_control = lockable_files.LockableFiles(self.transport, '',
953
1092
                                     lockable_files.TransportLock)
954
1093
        return temp_control._dir_mode
955
1094
 
 
1095
    def get_branch_reference(self):
 
1096
        """See BzrDir.get_branch_reference()."""
 
1097
        from bzrlib.branch import BranchFormat
 
1098
        format = BranchFormat.find_format(self)
 
1099
        return format.get_reference(self)
 
1100
 
956
1101
    def get_branch_transport(self, branch_format):
957
1102
        """See BzrDir.get_branch_transport()."""
958
1103
        if branch_format is None:
1013
1158
        try:
1014
1159
            if not isinstance(self.open_branch()._format,
1015
1160
                              format.get_branch_format().__class__):
1016
 
                # the repository needs an upgrade.
 
1161
                # the branch needs an upgrade.
1017
1162
                return True
1018
1163
        except errors.NotBranchError:
1019
1164
            pass
1020
 
        # currently there are no other possible conversions for meta1 formats.
 
1165
        try:
 
1166
            my_wt = self.open_workingtree(recommend_upgrade=False)
 
1167
            if not isinstance(my_wt._format,
 
1168
                              format.workingtree_format.__class__):
 
1169
                # the workingtree needs an upgrade.
 
1170
                return True
 
1171
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1172
            pass
1021
1173
        return False
1022
1174
 
1023
1175
    def open_branch(self, unsupported=False):
1024
1176
        """See BzrDir.open_branch."""
1025
 
        from bzrlib.branch import BranchFormat
1026
 
        format = BranchFormat.find_format(self)
 
1177
        format = self.find_branch_format()
1027
1178
        self._check_supported(format, unsupported)
1028
1179
        return format.open(self, _found=True)
1029
1180
 
1034
1185
        self._check_supported(format, unsupported)
1035
1186
        return format.open(self, _found=True)
1036
1187
 
1037
 
    def open_workingtree(self, unsupported=False):
 
1188
    def open_workingtree(self, unsupported=False,
 
1189
            recommend_upgrade=True):
1038
1190
        """See BzrDir.open_workingtree."""
1039
1191
        from bzrlib.workingtree import WorkingTreeFormat
1040
1192
        format = WorkingTreeFormat.find_format(self)
1041
 
        self._check_supported(format, unsupported)
 
1193
        self._check_supported(format, unsupported,
 
1194
            recommend_upgrade,
 
1195
            basedir=self.root_transport.base)
1042
1196
        return format.open(self, _found=True)
1043
1197
 
1044
1198
 
1071
1225
    This is a list of BzrDirFormat objects.
1072
1226
    """
1073
1227
 
 
1228
    _control_server_formats = []
 
1229
    """The registered control server formats, e.g. RemoteBzrDirs.
 
1230
 
 
1231
    This is a list of BzrDirFormat objects.
 
1232
    """
 
1233
 
1074
1234
    _lock_file_name = 'branch-lock'
1075
1235
 
1076
1236
    # _lock_class must be set in subclasses to the lock type, typ.
1077
1237
    # TransportLock or LockDir
1078
1238
 
1079
1239
    @classmethod
1080
 
    def find_format(klass, transport):
 
1240
    def find_format(klass, transport, _server_formats=True):
1081
1241
        """Return the format present at transport."""
1082
 
        for format in klass._control_formats:
 
1242
        if _server_formats:
 
1243
            formats = klass._control_server_formats + klass._control_formats
 
1244
        else:
 
1245
            formats = klass._control_formats
 
1246
        for format in formats:
1083
1247
            try:
1084
1248
                return format.probe_transport(transport)
1085
1249
            except errors.NotBranchError:
1089
1253
 
1090
1254
    @classmethod
1091
1255
    def probe_transport(klass, transport):
1092
 
        """Return the .bzrdir style transport present at URL."""
 
1256
        """Return the .bzrdir style format present in a directory."""
1093
1257
        try:
1094
1258
            format_string = transport.get(".bzr/branch-format").read()
1095
1259
        except errors.NoSuchFile:
1225
1389
 
1226
1390
    @classmethod
1227
1391
    def register_control_format(klass, format):
1228
 
        """Register a format that does not use '.bzrdir' for its control dir.
 
1392
        """Register a format that does not use '.bzr' for its control dir.
1229
1393
 
1230
1394
        TODO: This should be pulled up into a 'ControlDirFormat' base class
1231
1395
        which BzrDirFormat can inherit from, and renamed to register_format 
1235
1399
        klass._control_formats.append(format)
1236
1400
 
1237
1401
    @classmethod
 
1402
    def register_control_server_format(klass, format):
 
1403
        """Register a control format for client-server environments.
 
1404
 
 
1405
        These formats will be tried before ones registered with
 
1406
        register_control_format.  This gives implementations that decide to the
 
1407
        chance to grab it before anything looks at the contents of the format
 
1408
        file.
 
1409
        """
 
1410
        klass._control_server_formats.append(format)
 
1411
 
 
1412
    @classmethod
1238
1413
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1239
1414
    def set_default_format(klass, format):
1240
1415
        klass._set_default_format(format)
1257
1432
        klass._control_formats.remove(format)
1258
1433
 
1259
1434
 
1260
 
# register BzrDirFormat as a control format
1261
 
BzrDirFormat.register_control_format(BzrDirFormat)
1262
 
 
1263
 
 
1264
1435
class BzrDirFormat4(BzrDirFormat):
1265
1436
    """Bzr dir format 4.
1266
1437
 
1446
1617
    _lock_class = lockdir.LockDir
1447
1618
 
1448
1619
    def __init__(self):
 
1620
        self._workingtree_format = None
1449
1621
        self._branch_format = None
1450
1622
 
 
1623
    def __eq__(self, other):
 
1624
        if other.__class__ is not self.__class__:
 
1625
            return False
 
1626
        if other.repository_format != self.repository_format:
 
1627
            return False
 
1628
        if other.workingtree_format != self.workingtree_format:
 
1629
            return False
 
1630
        return True
 
1631
 
 
1632
    def __ne__(self, other):
 
1633
        return not self == other
 
1634
 
1451
1635
    def get_branch_format(self):
1452
1636
        if self._branch_format is None:
1453
1637
            from bzrlib.branch import BranchFormat
1491
1675
 
1492
1676
    repository_format = property(__return_repository_format, __set_repository_format)
1493
1677
 
1494
 
 
 
1678
    def __get_workingtree_format(self):
 
1679
        if self._workingtree_format is None:
 
1680
            from bzrlib.workingtree import WorkingTreeFormat
 
1681
            self._workingtree_format = WorkingTreeFormat.get_default_format()
 
1682
        return self._workingtree_format
 
1683
 
 
1684
    def __set_workingtree_format(self, wt_format):
 
1685
        self._workingtree_format = wt_format
 
1686
 
 
1687
    workingtree_format = property(__get_workingtree_format,
 
1688
                                  __set_workingtree_format)
 
1689
 
 
1690
 
 
1691
# Register bzr control format
 
1692
BzrDirFormat.register_control_format(BzrDirFormat)
 
1693
 
 
1694
# Register bzr formats
1495
1695
BzrDirFormat.register_format(BzrDirFormat4())
1496
1696
BzrDirFormat.register_format(BzrDirFormat5())
1497
1697
BzrDirFormat.register_format(BzrDirFormat6())
1509
1709
    easy to identify.
1510
1710
    """
1511
1711
 
1512
 
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1712
    def __init__(self, vfs_factory, transport_server, transport_readonly_server,
 
1713
        formats):
 
1714
        """Create an object to adapt tests.
 
1715
 
 
1716
        :param vfs_server: A factory to create a Transport Server which has
 
1717
            all the VFS methods working, and is writable.
 
1718
        """
 
1719
        self._vfs_factory = vfs_factory
1513
1720
        self._transport_server = transport_server
1514
1721
        self._transport_readonly_server = transport_readonly_server
1515
1722
        self._formats = formats
1518
1725
        result = unittest.TestSuite()
1519
1726
        for format in self._formats:
1520
1727
            new_test = deepcopy(test)
 
1728
            new_test.vfs_transport_factory = self._vfs_factory
1521
1729
            new_test.transport_server = self._transport_server
1522
1730
            new_test.transport_readonly_server = self._transport_readonly_server
1523
1731
            new_test.bzrdir_format = format
1993
2201
        except errors.NotBranchError:
1994
2202
            pass
1995
2203
        else:
 
2204
            # TODO: conversions of Branch and Tree should be done by
 
2205
            # InterXFormat lookups
1996
2206
            # Avoid circular imports
1997
2207
            from bzrlib import branch as _mod_branch
1998
2208
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
2000
2210
                _mod_branch.BzrBranchFormat6):
2001
2211
                branch_converter = _mod_branch.Converter5to6()
2002
2212
                branch_converter.convert(branch)
 
2213
        try:
 
2214
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
 
2215
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2216
            pass
 
2217
        else:
 
2218
            # TODO: conversions of Branch and Tree should be done by
 
2219
            # InterXFormat lookups
 
2220
            if (isinstance(tree, workingtree.WorkingTree3) and
 
2221
                not isinstance(tree, workingtree_4.WorkingTree4) and
 
2222
                isinstance(self.target_format.workingtree_format,
 
2223
                    workingtree_4.WorkingTreeFormat4)):
 
2224
                workingtree_4.Converter3to4().convert(tree)
2003
2225
        return to_convert
2004
2226
 
2005
2227
 
 
2228
# This is not in remote.py because it's small, and needs to be registered.
 
2229
# Putting it in remote.py creates a circular import problem.
 
2230
# we can make it a lazy object if the control formats is turned into something
 
2231
# like a registry.
 
2232
class RemoteBzrDirFormat(BzrDirMetaFormat1):
 
2233
    """Format representing bzrdirs accessed via a smart server"""
 
2234
 
 
2235
    def get_format_description(self):
 
2236
        return 'bzr remote bzrdir'
 
2237
    
 
2238
    @classmethod
 
2239
    def probe_transport(klass, transport):
 
2240
        """Return a RemoteBzrDirFormat object if it looks possible."""
 
2241
        try:
 
2242
            client = transport.get_smart_client()
 
2243
        except (NotImplementedError, AttributeError,
 
2244
                errors.TransportNotPossible):
 
2245
            # no smart server, so not a branch for this format type.
 
2246
            raise errors.NotBranchError(path=transport.base)
 
2247
        else:
 
2248
            # Send a 'hello' request in protocol version one, and decline to
 
2249
            # open it if the server doesn't support our required version (2) so
 
2250
            # that the VFS-based transport will do it.
 
2251
            request = client.get_request()
 
2252
            smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
2253
            server_version = smart_protocol.query_version()
 
2254
            if server_version != 2:
 
2255
                raise errors.NotBranchError(path=transport.base)
 
2256
            return klass()
 
2257
 
 
2258
    def initialize_on_transport(self, transport):
 
2259
        try:
 
2260
            # hand off the request to the smart server
 
2261
            medium = transport.get_smart_medium()
 
2262
        except errors.NoSmartMedium:
 
2263
            # TODO: lookup the local format from a server hint.
 
2264
            local_dir_format = BzrDirMetaFormat1()
 
2265
            return local_dir_format.initialize_on_transport(transport)
 
2266
        client = _SmartClient(medium)
 
2267
        path = client.remote_path_from_transport(transport)
 
2268
        response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
 
2269
        assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
 
2270
        return remote.RemoteBzrDir(transport)
 
2271
 
 
2272
    def _open(self, transport):
 
2273
        return remote.RemoteBzrDir(transport)
 
2274
 
 
2275
    def __eq__(self, other):
 
2276
        if not isinstance(other, RemoteBzrDirFormat):
 
2277
            return False
 
2278
        return self.get_format_description() == other.get_format_description()
 
2279
 
 
2280
 
 
2281
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
 
2282
 
 
2283
 
2006
2284
class BzrDirFormatInfo(object):
2007
2285
 
2008
 
    def __init__(self, native, deprecated):
 
2286
    def __init__(self, native, deprecated, hidden):
2009
2287
        self.deprecated = deprecated
2010
2288
        self.native = native
 
2289
        self.hidden = hidden
2011
2290
 
2012
2291
 
2013
2292
class BzrDirFormatRegistry(registry.Registry):
2017
2296
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
2018
2297
    """
2019
2298
 
2020
 
    def register_metadir(self, key, repo, help, native=True, deprecated=False,
2021
 
                         branch_format=None):
 
2299
    def register_metadir(self, key,
 
2300
             repository_format, help, native=True, deprecated=False,
 
2301
             branch_format=None,
 
2302
             tree_format=None,
 
2303
             hidden=False):
2022
2304
        """Register a metadir subformat.
2023
2305
 
2024
2306
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
2025
2307
        by the Repository format.
2026
2308
 
2027
 
        :param repo: The fully-qualified repository format class name as a
2028
 
        string.
 
2309
        :param repository_format: The fully-qualified repository format class
 
2310
            name as a string.
 
2311
        :param branch_format: Fully-qualified branch format class name as
 
2312
            a string.
 
2313
        :param tree_format: Fully-qualified tree format class name as
 
2314
            a string.
2029
2315
        """
2030
2316
        # This should be expanded to support setting WorkingTree and Branch
2031
2317
        # formats, once BzrDirMetaFormat1 supports that.
2032
 
        def helper():
2033
 
            import bzrlib.branch
2034
 
            mod_name, repo_factory_name = repo.rsplit('.', 1)
 
2318
        def _load(full_name):
 
2319
            mod_name, factory_name = full_name.rsplit('.', 1)
2035
2320
            try:
2036
2321
                mod = __import__(mod_name, globals(), locals(),
2037
 
                        [repo_factory_name])
 
2322
                        [factory_name])
2038
2323
            except ImportError, e:
2039
 
                raise ImportError('failed to load repository %s: %s'
2040
 
                    % (repo, e))
 
2324
                raise ImportError('failed to load %s: %s' % (full_name, e))
2041
2325
            try:
2042
 
                repo_format_class = getattr(mod, repo_factory_name)
 
2326
                factory = getattr(mod, factory_name)
2043
2327
            except AttributeError:
2044
 
                raise AttributeError('no repository format %r in module %r' 
2045
 
                    % (repo, mod))
 
2328
                raise AttributeError('no factory %s in module %r'
 
2329
                    % (full_name, mod))
 
2330
            return factory()
 
2331
 
 
2332
        def helper():
2046
2333
            bd = BzrDirMetaFormat1()
2047
 
            bd.repository_format = repo_format_class()
2048
2334
            if branch_format is not None:
2049
 
                bd.set_branch_format(getattr(bzrlib.branch, branch_format)())
 
2335
                bd.set_branch_format(_load(branch_format))
 
2336
            if tree_format is not None:
 
2337
                bd.workingtree_format = _load(tree_format)
 
2338
            if repository_format is not None:
 
2339
                bd.repository_format = _load(repository_format)
2050
2340
            return bd
2051
 
        self.register(key, helper, help, native, deprecated)
 
2341
        self.register(key, helper, help, native, deprecated, hidden)
2052
2342
 
2053
 
    def register(self, key, factory, help, native=True, deprecated=False):
 
2343
    def register(self, key, factory, help, native=True, deprecated=False,
 
2344
                 hidden=False):
2054
2345
        """Register a BzrDirFormat factory.
2055
2346
        
2056
2347
        The factory must be a callable that takes one parameter: the key.
2060
2351
        supplied directly.
2061
2352
        """
2062
2353
        registry.Registry.register(self, key, factory, help, 
2063
 
            BzrDirFormatInfo(native, deprecated))
 
2354
            BzrDirFormatInfo(native, deprecated, hidden))
2064
2355
 
2065
2356
    def register_lazy(self, key, module_name, member_name, help, native=True,
2066
 
                      deprecated=False):
 
2357
                      deprecated=False, hidden=False):
2067
2358
        registry.Registry.register_lazy(self, key, module_name, member_name, 
2068
 
            help, BzrDirFormatInfo(native, deprecated))
 
2359
            help, BzrDirFormatInfo(native, deprecated, hidden))
2069
2360
 
2070
2361
    def set_default(self, key):
2071
2362
        """Set the 'default' key to be a clone of the supplied key.
2121
2412
        deprecated_pairs = []
2122
2413
        for key, help in help_pairs:
2123
2414
            info = self.get_info(key)
2124
 
            if info.deprecated:
 
2415
            if info.hidden:
 
2416
                continue
 
2417
            elif info.deprecated:
2125
2418
                deprecated_pairs.append((key, help))
2126
2419
            else:
2127
2420
                output += wrapped(key, help, info)
2141
2434
    deprecated=True)
2142
2435
format_registry.register_metadir('knit',
2143
2436
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2144
 
    'Format using knits.  Recommended.',
2145
 
    branch_format='BzrBranchFormat5')
2146
 
format_registry.set_default('knit')
 
2437
    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
 
2438
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2439
    tree_format='bzrlib.workingtree.WorkingTreeFormat3')
2147
2440
format_registry.register_metadir('metaweave',
2148
2441
    'bzrlib.repofmt.weaverepo.RepositoryFormat7',
2149
2442
    'Transitional format in 0.8.  Slower than knit.',
2150
 
    deprecated=True,
2151
 
    )
2152
 
format_registry.register_metadir('experimental-knit2',
2153
 
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit2',
2154
 
    'Experimental successor to knit.  Use at your own risk.',
2155
 
    branch_format='BzrBranchFormat5')
2156
 
format_registry.register_metadir('experimental-branch6',
2157
 
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
2158
 
    'Experimental successor to knit.  Use at your own risk.',
2159
 
    branch_format='BzrBranchFormat6')
 
2443
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2444
    tree_format='bzrlib.workingtree.WorkingTreeFormat3',
 
2445
    deprecated=True)
 
2446
format_registry.register_metadir('dirstate',
 
2447
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2448
    help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
 
2449
        'above when accessed over the network.',
 
2450
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2451
    # this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
 
2452
    # directly from workingtree_4 triggers a circular import.
 
2453
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2454
    )
 
2455
format_registry.register_metadir('dirstate-tags',
 
2456
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2457
    help='New in 0.15: Fast local operations and improved scaling for '
 
2458
        'network operations. Additionally adds support for tags.'
 
2459
        ' Incompatible with bzr < 0.15.',
 
2460
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2461
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2462
    )
 
2463
format_registry.register_metadir('dirstate-with-subtree',
 
2464
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
 
2465
    help='New in 0.15: Fast local operations and improved scaling for '
 
2466
        'network operations. Additionally adds support for versioning nested '
 
2467
        'bzr branches. Incompatible with bzr < 0.15.',
 
2468
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2469
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2470
    hidden=True,
 
2471
    )
 
2472
format_registry.set_default('dirstate')