~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Robert Collins
  • Date: 2007-07-04 08:08:13 UTC
  • mfrom: (2572 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2587.
  • Revision ID: robertc@robertcollins.net-20070704080813-wzebx0r88fvwj5rq
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 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
20
20
directories.
21
21
"""
22
22
 
23
 
# TODO: remove unittest dependency; put that stuff inside the test suite
24
 
 
25
 
# TODO: The Format probe_transport seems a bit redundant with just trying to
26
 
# open the bzrdir. -- mbp
27
 
#
28
23
# TODO: Can we move specific formats into separate modules to make this file
29
24
# smaller?
30
25
 
31
 
from copy import deepcopy
32
26
from cStringIO import StringIO
33
27
import os
 
28
import textwrap
 
29
 
 
30
from bzrlib.lazy_import import lazy_import
 
31
lazy_import(globals(), """
34
32
from stat import S_ISDIR
35
 
from unittest import TestSuite
36
33
 
37
34
import bzrlib
38
 
import bzrlib.errors as errors
39
 
from bzrlib.lockable_files import LockableFiles, TransportLock
40
 
from bzrlib.lockdir import LockDir
 
35
from bzrlib import (
 
36
    errors,
 
37
    lockable_files,
 
38
    lockdir,
 
39
    registry,
 
40
    remote,
 
41
    revision as _mod_revision,
 
42
    symbol_versioning,
 
43
    ui,
 
44
    urlutils,
 
45
    xml4,
 
46
    xml5,
 
47
    workingtree,
 
48
    workingtree_4,
 
49
    )
41
50
from bzrlib.osutils import (
42
 
                            abspath,
43
 
                            pathjoin,
44
 
                            safe_unicode,
45
 
                            sha_strings,
46
 
                            sha_string,
47
 
                            )
48
 
import bzrlib.revision
 
51
    safe_unicode,
 
52
    sha_strings,
 
53
    sha_string,
 
54
    )
 
55
from bzrlib.smart.client import _SmartClient
 
56
from bzrlib.smart import protocol
49
57
from bzrlib.store.revision.text import TextRevisionStore
50
58
from bzrlib.store.text import TextStore
51
59
from bzrlib.store.versioned import WeaveStore
52
 
from bzrlib.trace import mutter
53
60
from bzrlib.transactions import WriteTransaction
54
 
from bzrlib.transport import get_transport
 
61
from bzrlib.transport import (
 
62
    do_catching_redirections,
 
63
    get_transport,
 
64
    )
 
65
from bzrlib.weave import Weave
 
66
""")
 
67
 
 
68
from bzrlib.trace import (
 
69
    mutter,
 
70
    note,
 
71
    )
55
72
from bzrlib.transport.local import LocalTransport
56
 
import bzrlib.urlutils as urlutils
57
 
from bzrlib.weave import Weave
58
 
from bzrlib.xml4 import serializer_v4
59
 
import bzrlib.xml5
60
73
 
61
74
 
62
75
class BzrDir(object):
77
90
        If there is a tree, the tree is opened and break_lock() called.
78
91
        Otherwise, branch is tried, and finally repository.
79
92
        """
 
93
        # XXX: This seems more like a UI function than something that really
 
94
        # belongs in this class.
80
95
        try:
81
96
            thing_to_unlock = self.open_workingtree()
82
97
        except (errors.NotLocalUrl, errors.NoWorkingTree):
99
114
        source_repo_format.check_conversion_target(target_repo_format)
100
115
 
101
116
    @staticmethod
102
 
    def _check_supported(format, allow_unsupported):
103
 
        """Check whether format is a supported format.
104
 
 
105
 
        If allow_unsupported is True, this is a no-op.
 
117
    def _check_supported(format, allow_unsupported,
 
118
        recommend_upgrade=True,
 
119
        basedir=None):
 
120
        """Give an error or warning on old formats.
 
121
 
 
122
        :param format: may be any kind of format - workingtree, branch, 
 
123
        or repository.
 
124
 
 
125
        :param allow_unsupported: If true, allow opening 
 
126
        formats that are strongly deprecated, and which may 
 
127
        have limited functionality.
 
128
 
 
129
        :param recommend_upgrade: If true (default), warn
 
130
        the user through the ui object that they may wish
 
131
        to upgrade the object.
106
132
        """
 
133
        # TODO: perhaps move this into a base Format class; it's not BzrDir
 
134
        # specific. mbp 20070323
107
135
        if not allow_unsupported and not format.is_supported():
108
136
            # see open_downlevel to open legacy branches.
109
137
            raise errors.UnsupportedFormatError(format=format)
 
138
        if recommend_upgrade \
 
139
            and getattr(format, 'upgrade_recommended', False):
 
140
            ui.ui_factory.recommend_upgrade(
 
141
                format.get_format_description(),
 
142
                basedir)
110
143
 
111
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
144
    def clone(self, url, revision_id=None, force_new_repo=False):
112
145
        """Clone this bzrdir and its contents to url verbatim.
113
146
 
114
147
        If urls last component does not exist, it will be created.
118
151
        :param force_new_repo: Do not use a shared repository for the target 
119
152
                               even if one is available.
120
153
        """
121
 
        self._make_tail(url)
122
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
123
 
        result = self._format.initialize(url)
 
154
        return self.clone_on_transport(get_transport(url),
 
155
                                       revision_id=revision_id,
 
156
                                       force_new_repo=force_new_repo)
 
157
 
 
158
    def clone_on_transport(self, transport, revision_id=None,
 
159
                           force_new_repo=False):
 
160
        """Clone this bzrdir and its contents to transport verbatim.
 
161
 
 
162
        If the target directory does not exist, it will be created.
 
163
 
 
164
        if revision_id is not None, then the clone operation may tune
 
165
            itself to download less data.
 
166
        :param force_new_repo: Do not use a shared repository for the target 
 
167
                               even if one is available.
 
168
        """
 
169
        transport.ensure_base()
 
170
        result = self._format.initialize_on_transport(transport)
124
171
        try:
125
172
            local_repo = self.find_repository()
126
173
        except errors.NoRepositoryPresent:
130
177
            if force_new_repo:
131
178
                result_repo = local_repo.clone(
132
179
                    result,
133
 
                    revision_id=revision_id,
134
 
                    basis=basis_repo)
 
180
                    revision_id=revision_id)
135
181
                result_repo.set_make_working_trees(local_repo.make_working_trees())
136
182
            else:
137
183
                try:
138
184
                    result_repo = result.find_repository()
139
185
                    # fetch content this dir needs.
140
 
                    if basis_repo:
141
 
                        # XXX FIXME RBC 20060214 need tests for this when the basis
142
 
                        # is incomplete
143
 
                        result_repo.fetch(basis_repo, revision_id=revision_id)
144
186
                    result_repo.fetch(local_repo, revision_id=revision_id)
145
187
                except errors.NoRepositoryPresent:
146
188
                    # needed to make one anyway.
147
189
                    result_repo = local_repo.clone(
148
190
                        result,
149
 
                        revision_id=revision_id,
150
 
                        basis=basis_repo)
 
191
                        revision_id=revision_id)
151
192
                    result_repo.set_make_working_trees(local_repo.make_working_trees())
152
193
        # 1 if there is a branch present
153
194
        #   make sure its content is available in the target repository
157
198
        except errors.NotBranchError:
158
199
            pass
159
200
        try:
160
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
201
            self.open_workingtree().clone(result)
161
202
        except (errors.NoWorkingTree, errors.NotLocalUrl):
162
203
            pass
163
204
        return result
164
205
 
165
 
    def _get_basis_components(self, basis):
166
 
        """Retrieve the basis components that are available at basis."""
167
 
        if basis is None:
168
 
            return None, None, None
169
 
        try:
170
 
            basis_tree = basis.open_workingtree()
171
 
            basis_branch = basis_tree.branch
172
 
            basis_repo = basis_branch.repository
173
 
        except (errors.NoWorkingTree, errors.NotLocalUrl):
174
 
            basis_tree = None
175
 
            try:
176
 
                basis_branch = basis.open_branch()
177
 
                basis_repo = basis_branch.repository
178
 
            except errors.NotBranchError:
179
 
                basis_branch = None
180
 
                try:
181
 
                    basis_repo = basis.open_repository()
182
 
                except errors.NoRepositoryPresent:
183
 
                    basis_repo = None
184
 
        return basis_repo, basis_branch, basis_tree
185
 
 
186
206
    # TODO: This should be given a Transport, and should chdir up; otherwise
187
207
    # this will open a new connection.
188
208
    def _make_tail(self, url):
189
 
        head, tail = urlutils.split(url)
190
 
        if tail and tail != '.':
191
 
            t = bzrlib.transport.get_transport(head)
192
 
            try:
193
 
                t.mkdir(tail)
194
 
            except errors.FileExists:
195
 
                pass
 
209
        t = get_transport(url)
 
210
        t.ensure_base()
196
211
 
197
212
    # TODO: Should take a Transport
198
213
    @classmethod
199
 
    def create(cls, base):
 
214
    def create(cls, base, format=None):
200
215
        """Create a new BzrDir at the url 'base'.
201
216
        
202
217
        This will call the current default formats initialize with base
203
218
        as the only parameter.
204
219
 
205
 
        If you need a specific format, consider creating an instance
206
 
        of that and calling initialize().
 
220
        :param format: If supplied, the format of branch to create.  If not
 
221
            supplied, the default is used.
207
222
        """
208
223
        if cls is not BzrDir:
209
 
            raise AssertionError("BzrDir.create always creates the default format, "
210
 
                    "not one of %r" % cls)
211
 
        head, tail = urlutils.split(base)
212
 
        if tail and tail != '.':
213
 
            t = bzrlib.transport.get_transport(head)
214
 
            try:
215
 
                t.mkdir(tail)
216
 
            except errors.FileExists:
217
 
                pass
218
 
        return BzrDirFormat.get_default_format().initialize(safe_unicode(base))
 
224
            raise AssertionError("BzrDir.create always creates the default"
 
225
                " format, not one of %r" % cls)
 
226
        t = get_transport(base)
 
227
        t.ensure_base()
 
228
        if format is None:
 
229
            format = BzrDirFormat.get_default_format()
 
230
        return format.initialize(safe_unicode(base))
219
231
 
220
232
    def create_branch(self):
221
233
        """Create a branch in this BzrDir.
226
238
        raise NotImplementedError(self.create_branch)
227
239
 
228
240
    @staticmethod
229
 
    def create_branch_and_repo(base, force_new_repo=False):
 
241
    def create_branch_and_repo(base, force_new_repo=False, format=None):
230
242
        """Create a new BzrDir, Branch and Repository at the url 'base'.
231
243
 
232
244
        This will use the current default BzrDirFormat, and use whatever 
239
251
        :param base: The URL to create the branch at.
240
252
        :param force_new_repo: If True a new repository is always created.
241
253
        """
242
 
        bzrdir = BzrDir.create(base)
 
254
        bzrdir = BzrDir.create(base, format)
243
255
        bzrdir._find_or_create_repository(force_new_repo)
244
256
        return bzrdir.create_branch()
245
257
 
283
295
            t = get_transport(safe_unicode(base))
284
296
            if not isinstance(t, LocalTransport):
285
297
                raise errors.NotLocalUrl(base)
286
 
        if format is None:
287
 
            bzrdir = BzrDir.create(base)
288
 
        else:
289
 
            bzrdir = format.initialize(base)
 
298
        bzrdir = BzrDir.create(base, format)
290
299
        repo = bzrdir._find_or_create_repository(force_new_repo)
291
300
        result = bzrdir.create_branch()
292
301
        if force_new_tree or (repo.make_working_trees() and 
298
307
        return result
299
308
        
300
309
    @staticmethod
301
 
    def create_repository(base, shared=False):
 
310
    def create_repository(base, shared=False, format=None):
302
311
        """Create a new BzrDir and Repository at the url 'base'.
303
312
 
304
 
        This will use the current default BzrDirFormat, and use whatever 
305
 
        repository format that that uses for bzrdirformat.create_repository.
 
313
        If no format is supplied, this will default to the current default
 
314
        BzrDirFormat by default, and use whatever repository format that that
 
315
        uses for bzrdirformat.create_repository.
306
316
 
307
317
        :param shared: Create a shared repository rather than a standalone
308
318
                       repository.
312
322
        it should take no parameters and construct whatever repository format
313
323
        that child class desires.
314
324
        """
315
 
        bzrdir = BzrDir.create(base)
 
325
        bzrdir = BzrDir.create(base, format)
316
326
        return bzrdir.create_repository(shared)
317
327
 
318
328
    @staticmethod
319
 
    def create_standalone_workingtree(base):
 
329
    def create_standalone_workingtree(base, format=None):
320
330
        """Create a new BzrDir, WorkingTree, Branch and Repository at 'base'.
321
331
 
322
332
        'base' must be a local path or a file:// url.
331
341
        if not isinstance(t, LocalTransport):
332
342
            raise errors.NotLocalUrl(base)
333
343
        bzrdir = BzrDir.create_branch_and_repo(safe_unicode(base),
334
 
                                               force_new_repo=True).bzrdir
 
344
                                               force_new_repo=True,
 
345
                                               format=format).bzrdir
335
346
        return bzrdir.create_workingtree()
336
347
 
337
348
    def create_workingtree(self, revision_id=None):
341
352
        """
342
353
        raise NotImplementedError(self.create_workingtree)
343
354
 
 
355
    def retire_bzrdir(self):
 
356
        """Permanently disable the bzrdir.
 
357
 
 
358
        This is done by renaming it to give the user some ability to recover
 
359
        if there was a problem.
 
360
 
 
361
        This will have horrible consequences if anyone has anything locked or
 
362
        in use.
 
363
        """
 
364
        for i in xrange(10000):
 
365
            try:
 
366
                to_path = '.bzr.retired.%d' % i
 
367
                self.root_transport.rename('.bzr', to_path)
 
368
                note("renamed %s to %s"
 
369
                    % (self.root_transport.abspath('.bzr'), to_path))
 
370
                break
 
371
            except (errors.TransportError, IOError, errors.PathError):
 
372
                pass
 
373
 
 
374
    def destroy_workingtree(self):
 
375
        """Destroy the working tree at this BzrDir.
 
376
 
 
377
        Formats that do not support this may raise UnsupportedOperation.
 
378
        """
 
379
        raise NotImplementedError(self.destroy_workingtree)
 
380
 
 
381
    def destroy_workingtree_metadata(self):
 
382
        """Destroy the control files for the working tree at this BzrDir.
 
383
 
 
384
        The contents of working tree files are not affected.
 
385
        Formats that do not support this may raise UnsupportedOperation.
 
386
        """
 
387
        raise NotImplementedError(self.destroy_workingtree_metadata)
 
388
 
344
389
    def find_repository(self):
345
390
        """Find the repository that should be used for a_bzrdir.
346
391
 
371
416
                    break
372
417
                else:
373
418
                    continue
374
 
            if ((found_bzrdir.root_transport.base == 
 
419
            if ((found_bzrdir.root_transport.base ==
375
420
                 self.root_transport.base) or repository.is_shared()):
376
421
                return repository
377
422
            else:
378
423
                raise errors.NoRepositoryPresent(self)
379
424
        raise errors.NoRepositoryPresent(self)
380
425
 
 
426
    def get_branch_reference(self):
 
427
        """Return the referenced URL for the branch in this bzrdir.
 
428
 
 
429
        :raises NotBranchError: If there is no Branch.
 
430
        :return: The URL the branch in this bzrdir references if it is a
 
431
            reference branch, or None for regular branches.
 
432
        """
 
433
        return None
 
434
 
381
435
    def get_branch_transport(self, branch_format):
382
436
        """Get the transport for use by branch format in this BzrDir.
383
437
 
408
462
        """Get the transport for use by workingtree format in this BzrDir.
409
463
 
410
464
        Note that bzr dirs that do not support format strings will raise
411
 
        IncompatibleFormat if the workingtree format they are given has
412
 
        a format string, and vice versa.
 
465
        IncompatibleFormat if the workingtree format they are given has a
 
466
        format string, and vice versa.
413
467
 
414
468
        If workingtree_format is None, the transport is returned with no 
415
469
        checking. if it is not None, then the returned transport is
475
529
        return BzrDir.open_from_transport(t, _unsupported=_unsupported)
476
530
 
477
531
    @staticmethod
478
 
    def open_from_transport(transport, _unsupported=False):
 
532
    def open_from_transport(transport, _unsupported=False,
 
533
                            _server_formats=True):
479
534
        """Open a bzrdir within a particular directory.
480
535
 
481
536
        :param transport: Transport containing the bzrdir.
482
537
        :param _unsupported: private.
483
538
        """
484
 
        format = BzrDirFormat.find_format(transport)
 
539
        base = transport.base
 
540
 
 
541
        def find_format(transport):
 
542
            return transport, BzrDirFormat.find_format(
 
543
                transport, _server_formats=_server_formats)
 
544
 
 
545
        def redirected(transport, e, redirection_notice):
 
546
            qualified_source = e.get_source_url()
 
547
            relpath = transport.relpath(qualified_source)
 
548
            if not e.target.endswith(relpath):
 
549
                # Not redirected to a branch-format, not a branch
 
550
                raise errors.NotBranchError(path=e.target)
 
551
            target = e.target[:-len(relpath)]
 
552
            note('%s is%s redirected to %s',
 
553
                 transport.base, e.permanently, target)
 
554
            # Let's try with a new transport
 
555
            qualified_target = e.get_target_url()[:-len(relpath)]
 
556
            # FIXME: If 'transport' has a qualifier, this should
 
557
            # be applied again to the new transport *iff* the
 
558
            # schemes used are the same. It's a bit tricky to
 
559
            # verify, so I'll punt for now
 
560
            # -- vila20070212
 
561
            return get_transport(target)
 
562
 
 
563
        try:
 
564
            transport, format = do_catching_redirections(find_format,
 
565
                                                         transport,
 
566
                                                         redirected)
 
567
        except errors.TooManyRedirections:
 
568
            raise errors.NotBranchError(base)
 
569
 
485
570
        BzrDir._check_supported(format, _unsupported)
486
571
        return format.open(transport, _found=True)
487
572
 
527
612
                return result, urlutils.unescape(a_transport.relpath(url))
528
613
            except errors.NotBranchError, e:
529
614
                pass
530
 
            new_t = a_transport.clone('..')
 
615
            try:
 
616
                new_t = a_transport.clone('..')
 
617
            except errors.InvalidURLJoin:
 
618
                # reached the root, whatever that may be
 
619
                raise errors.NotBranchError(path=url)
531
620
            if new_t.base == a_transport.base:
532
621
                # reached the root, whatever that may be
533
622
                raise errors.NotBranchError(path=url)
534
623
            a_transport = new_t
535
624
 
 
625
    @classmethod
 
626
    def open_containing_tree_or_branch(klass, location):
 
627
        """Return the branch and working tree contained by a location.
 
628
 
 
629
        Returns (tree, branch, relpath).
 
630
        If there is no tree at containing the location, tree will be None.
 
631
        If there is no branch containing the location, an exception will be
 
632
        raised
 
633
        relpath is the portion of the path that is contained by the branch.
 
634
        """
 
635
        bzrdir, relpath = klass.open_containing(location)
 
636
        try:
 
637
            tree = bzrdir.open_workingtree()
 
638
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
639
            tree = None
 
640
            branch = bzrdir.open_branch()
 
641
        else:
 
642
            branch = tree.branch
 
643
        return tree, branch, relpath
 
644
 
536
645
    def open_repository(self, _unsupported=False):
537
646
        """Open the repository object at this BzrDir if one is present.
538
647
 
545
654
        """
546
655
        raise NotImplementedError(self.open_repository)
547
656
 
548
 
    def open_workingtree(self, _unsupported=False):
 
657
    def open_workingtree(self, _unsupported=False,
 
658
            recommend_upgrade=True):
549
659
        """Open the workingtree object at this BzrDir if one is present.
550
 
        
551
 
        TODO: static convenience version of this?
 
660
 
 
661
        :param recommend_upgrade: Optional keyword parameter, when True (the
 
662
            default), emit through the ui module a recommendation that the user
 
663
            upgrade the working tree when the workingtree being opened is old
 
664
            (but still fully supported).
552
665
        """
553
666
        raise NotImplementedError(self.open_workingtree)
554
667
 
576
689
        workingtree and discards it, and that's somewhat expensive.) 
577
690
        """
578
691
        try:
579
 
            self.open_workingtree()
 
692
            self.open_workingtree(recommend_upgrade=False)
580
693
            return True
581
694
        except errors.NoWorkingTree:
582
695
            return False
583
696
 
584
 
    def cloning_metadir(self, basis=None):
 
697
    def _cloning_metadir(self):
585
698
        """Produce a metadir suitable for cloning with"""
586
 
        def related_repository(bzrdir):
 
699
        result_format = self._format.__class__()
 
700
        try:
587
701
            try:
588
 
                branch = bzrdir.open_branch()
589
 
                return branch.repository
 
702
                branch = self.open_branch()
 
703
                source_repository = branch.repository
590
704
            except errors.NotBranchError:
591
705
                source_branch = None
592
 
                return bzrdir.open_repository()
593
 
        result_format = self._format.__class__()
 
706
                source_repository = self.open_repository()
 
707
        except errors.NoRepositoryPresent:
 
708
            source_repository = None
 
709
        else:
 
710
            # XXX TODO: This isinstance is here because we have not implemented
 
711
            # the fix recommended in bug # 103195 - to delegate this choice the
 
712
            # repository itself.
 
713
            repo_format = source_repository._format
 
714
            if not isinstance(repo_format, remote.RemoteRepositoryFormat):
 
715
                result_format.repository_format = repo_format
594
716
        try:
595
 
            try:
596
 
                source_repository = related_repository(self)
597
 
            except errors.NoRepositoryPresent:
598
 
                if basis is None:
599
 
                    raise
600
 
                source_repository = related_repository(self)
601
 
            result_format.repository_format = source_repository._format
602
 
        except errors.NoRepositoryPresent:
603
 
            pass
604
 
        return result_format
605
 
 
606
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
717
            # TODO: Couldn't we just probe for the format in these cases,
 
718
            # rather than opening the whole tree?  It would be a little
 
719
            # faster. mbp 20070401
 
720
            tree = self.open_workingtree(recommend_upgrade=False)
 
721
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
722
            result_format.workingtree_format = None
 
723
        else:
 
724
            result_format.workingtree_format = tree._format.__class__()
 
725
        return result_format, source_repository
 
726
 
 
727
    def cloning_metadir(self):
 
728
        """Produce a metadir suitable for cloning or sprouting with.
 
729
 
 
730
        These operations may produce workingtrees (yes, even though they're
 
731
        "cloning" something that doesn't have a tree, so a viable workingtree
 
732
        format must be selected.
 
733
        """
 
734
        format, repository = self._cloning_metadir()
 
735
        if format._workingtree_format is None:
 
736
            if repository is None:
 
737
                return format
 
738
            tree_format = repository._format._matchingbzrdir.workingtree_format
 
739
            format.workingtree_format = tree_format.__class__()
 
740
        return format
 
741
 
 
742
    def checkout_metadir(self):
 
743
        return self.cloning_metadir()
 
744
 
 
745
    def sprout(self, url, revision_id=None, force_new_repo=False,
 
746
               recurse='down'):
607
747
        """Create a copy of this bzrdir prepared for use as a new line of
608
748
        development.
609
749
 
617
757
        if revision_id is not None, then the clone operation may tune
618
758
            itself to download less data.
619
759
        """
620
 
        self._make_tail(url)
621
 
        cloning_format = self.cloning_metadir(basis)
622
 
        result = cloning_format.initialize(url)
623
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
 
760
        target_transport = get_transport(url)
 
761
        target_transport.ensure_base()
 
762
        cloning_format = self.cloning_metadir()
 
763
        result = cloning_format.initialize_on_transport(target_transport)
624
764
        try:
625
765
            source_branch = self.open_branch()
626
766
            source_repository = source_branch.repository
629
769
            try:
630
770
                source_repository = self.open_repository()
631
771
            except errors.NoRepositoryPresent:
632
 
                # copy the entire basis one if there is one
633
 
                # but there is no repository.
634
 
                source_repository = basis_repo
 
772
                source_repository = None
635
773
        if force_new_repo:
636
774
            result_repo = None
637
775
        else:
646
784
            result.create_repository()
647
785
        elif source_repository is not None and result_repo is None:
648
786
            # have source, and want to make a new target repo
649
 
            # we don't clone the repo because that preserves attributes
650
 
            # like is_shared(), and we have not yet implemented a 
651
 
            # repository sprout().
652
 
            result_repo = result.create_repository()
653
 
        if result_repo is not None:
 
787
            result_repo = source_repository.sprout(result, revision_id=revision_id)
 
788
        else:
654
789
            # fetch needed content into target.
655
 
            if basis_repo:
656
 
                # XXX FIXME RBC 20060214 need tests for this when the basis
657
 
                # is incomplete
658
 
                result_repo.fetch(basis_repo, revision_id=revision_id)
659
790
            if source_repository is not None:
 
791
                # would rather do 
 
792
                # source_repository.copy_content_into(result_repo, revision_id=revision_id)
 
793
                # so we can override the copy method
660
794
                result_repo.fetch(source_repository, revision_id=revision_id)
661
795
        if source_branch is not None:
662
796
            source_branch.sprout(result, revision_id=revision_id)
665
799
        # TODO: jam 20060426 we probably need a test in here in the
666
800
        #       case that the newly sprouted branch is a remote one
667
801
        if result_repo is None or result_repo.make_working_trees():
668
 
            result.create_workingtree()
 
802
            wt = result.create_workingtree()
 
803
            wt.lock_write()
 
804
            try:
 
805
                if wt.path2id('') is None:
 
806
                    try:
 
807
                        wt.set_root_id(self.open_workingtree.get_root_id())
 
808
                    except errors.NoWorkingTree:
 
809
                        pass
 
810
            finally:
 
811
                wt.unlock()
 
812
        else:
 
813
            wt = None
 
814
        if recurse == 'down':
 
815
            if wt is not None:
 
816
                basis = wt.basis_tree()
 
817
                basis.lock_read()
 
818
                subtrees = basis.iter_references()
 
819
                recurse_branch = wt.branch
 
820
            elif source_branch is not None:
 
821
                basis = source_branch.basis_tree()
 
822
                basis.lock_read()
 
823
                subtrees = basis.iter_references()
 
824
                recurse_branch = source_branch
 
825
            else:
 
826
                subtrees = []
 
827
                basis = None
 
828
            try:
 
829
                for path, file_id in subtrees:
 
830
                    target = urlutils.join(url, urlutils.escape(path))
 
831
                    sublocation = source_branch.reference_parent(file_id, path)
 
832
                    sublocation.bzrdir.sprout(target,
 
833
                        basis.get_reference_revision(file_id, path),
 
834
                        force_new_repo=force_new_repo, recurse=recurse)
 
835
            finally:
 
836
                if basis is not None:
 
837
                    basis.unlock()
669
838
        return result
670
839
 
671
840
 
675
844
    def __init__(self, _transport, _format):
676
845
        """See BzrDir.__init__."""
677
846
        super(BzrDirPreSplitOut, self).__init__(_transport, _format)
678
 
        assert self._format._lock_class == TransportLock
 
847
        assert self._format._lock_class == lockable_files.TransportLock
679
848
        assert self._format._lock_file_name == 'branch-lock'
680
 
        self._control_files = LockableFiles(self.get_branch_transport(None),
 
849
        self._control_files = lockable_files.LockableFiles(
 
850
                                            self.get_branch_transport(None),
681
851
                                            self._format._lock_file_name,
682
852
                                            self._format._lock_class)
683
853
 
685
855
        """Pre-splitout bzrdirs do not suffer from stale locks."""
686
856
        raise NotImplementedError(self.break_lock)
687
857
 
688
 
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
 
858
    def clone(self, url, revision_id=None, force_new_repo=False):
689
859
        """See BzrDir.clone()."""
690
860
        from bzrlib.workingtree import WorkingTreeFormat2
691
861
        self._make_tail(url)
692
862
        result = self._format._initialize_for_clone(url)
693
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
694
 
        self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
863
        self.open_repository().clone(result, revision_id=revision_id)
695
864
        from_branch = self.open_branch()
696
865
        from_branch.clone(result, revision_id=revision_id)
697
866
        try:
698
 
            self.open_workingtree().clone(result, basis=basis_tree)
 
867
            self.open_workingtree().clone(result)
699
868
        except errors.NotLocalUrl:
700
869
            # make a new one, this format always has to have one.
701
870
            try:
719
888
    def create_workingtree(self, revision_id=None):
720
889
        """See BzrDir.create_workingtree."""
721
890
        # this looks buggy but is not -really-
 
891
        # because this format creates the workingtree when the bzrdir is
 
892
        # created
722
893
        # clone and sprout will have set the revision_id
723
894
        # and that will have set it for us, its only
724
895
        # specific uses of create_workingtree in isolation
725
896
        # that can do wonky stuff here, and that only
726
897
        # happens for creating checkouts, which cannot be 
727
898
        # done on this format anyway. So - acceptable wart.
728
 
        result = self.open_workingtree()
 
899
        result = self.open_workingtree(recommend_upgrade=False)
729
900
        if revision_id is not None:
730
 
            if revision_id == bzrlib.revision.NULL_REVISION:
 
901
            if revision_id == _mod_revision.NULL_REVISION:
731
902
                result.set_parent_ids([])
732
903
            else:
733
904
                result.set_parent_ids([revision_id])
734
905
        return result
735
906
 
 
907
    def destroy_workingtree(self):
 
908
        """See BzrDir.destroy_workingtree."""
 
909
        raise errors.UnsupportedOperation(self.destroy_workingtree, self)
 
910
 
 
911
    def destroy_workingtree_metadata(self):
 
912
        """See BzrDir.destroy_workingtree_metadata."""
 
913
        raise errors.UnsupportedOperation(self.destroy_workingtree_metadata, 
 
914
                                          self)
 
915
 
736
916
    def get_branch_transport(self, branch_format):
737
917
        """See BzrDir.get_branch_transport()."""
738
918
        if branch_format is None:
778
958
        self._check_supported(format, unsupported)
779
959
        return format.open(self, _found=True)
780
960
 
781
 
    def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
 
961
    def sprout(self, url, revision_id=None, force_new_repo=False):
782
962
        """See BzrDir.sprout()."""
783
963
        from bzrlib.workingtree import WorkingTreeFormat2
784
964
        self._make_tail(url)
785
965
        result = self._format._initialize_for_clone(url)
786
 
        basis_repo, basis_branch, basis_tree = self._get_basis_components(basis)
787
966
        try:
788
 
            self.open_repository().clone(result, revision_id=revision_id, basis=basis_repo)
 
967
            self.open_repository().clone(result, revision_id=revision_id)
789
968
        except errors.NoRepositoryPresent:
790
969
            pass
791
970
        try:
813
992
 
814
993
    def open_repository(self):
815
994
        """See BzrDir.open_repository."""
816
 
        from bzrlib.repository import RepositoryFormat4
 
995
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
817
996
        return RepositoryFormat4().open(self, _found=True)
818
997
 
819
998
 
825
1004
 
826
1005
    def open_repository(self):
827
1006
        """See BzrDir.open_repository."""
828
 
        from bzrlib.repository import RepositoryFormat5
 
1007
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
829
1008
        return RepositoryFormat5().open(self, _found=True)
830
1009
 
831
 
    def open_workingtree(self, _unsupported=False):
 
1010
    def open_workingtree(self, _unsupported=False,
 
1011
            recommend_upgrade=True):
832
1012
        """See BzrDir.create_workingtree."""
833
1013
        from bzrlib.workingtree import WorkingTreeFormat2
834
 
        return WorkingTreeFormat2().open(self, _found=True)
 
1014
        wt_format = WorkingTreeFormat2()
 
1015
        # we don't warn here about upgrades; that ought to be handled for the
 
1016
        # bzrdir as a whole
 
1017
        return wt_format.open(self, _found=True)
835
1018
 
836
1019
 
837
1020
class BzrDir6(BzrDirPreSplitOut):
842
1025
 
843
1026
    def open_repository(self):
844
1027
        """See BzrDir.open_repository."""
845
 
        from bzrlib.repository import RepositoryFormat6
 
1028
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
846
1029
        return RepositoryFormat6().open(self, _found=True)
847
1030
 
848
 
    def open_workingtree(self, _unsupported=False):
 
1031
    def open_workingtree(self, _unsupported=False,
 
1032
        recommend_upgrade=True):
849
1033
        """See BzrDir.create_workingtree."""
 
1034
        # we don't warn here about upgrades; that ought to be handled for the
 
1035
        # bzrdir as a whole
850
1036
        from bzrlib.workingtree import WorkingTreeFormat2
851
1037
        return WorkingTreeFormat2().open(self, _found=True)
852
1038
 
866
1052
 
867
1053
    def create_branch(self):
868
1054
        """See BzrDir.create_branch."""
869
 
        from bzrlib.branch import BranchFormat
870
 
        return BranchFormat.get_default_format().initialize(self)
 
1055
        return self._format.get_branch_format().initialize(self)
871
1056
 
872
1057
    def create_repository(self, shared=False):
873
1058
        """See BzrDir.create_repository."""
876
1061
    def create_workingtree(self, revision_id=None):
877
1062
        """See BzrDir.create_workingtree."""
878
1063
        from bzrlib.workingtree import WorkingTreeFormat
879
 
        return WorkingTreeFormat.get_default_format().initialize(self, revision_id)
 
1064
        return self._format.workingtree_format.initialize(self, revision_id)
 
1065
 
 
1066
    def destroy_workingtree(self):
 
1067
        """See BzrDir.destroy_workingtree."""
 
1068
        wt = self.open_workingtree(recommend_upgrade=False)
 
1069
        repository = wt.branch.repository
 
1070
        empty = repository.revision_tree(_mod_revision.NULL_REVISION)
 
1071
        wt.revert([], old_tree=empty)
 
1072
        self.destroy_workingtree_metadata()
 
1073
 
 
1074
    def destroy_workingtree_metadata(self):
 
1075
        self.transport.delete_tree('checkout')
 
1076
 
 
1077
    def find_branch_format(self):
 
1078
        """Find the branch 'format' for this bzrdir.
 
1079
 
 
1080
        This might be a synthetic object for e.g. RemoteBranch and SVN.
 
1081
        """
 
1082
        from bzrlib.branch import BranchFormat
 
1083
        return BranchFormat.find_format(self)
880
1084
 
881
1085
    def _get_mkdir_mode(self):
882
1086
        """Figure out the mode to use when creating a bzrdir subdir."""
883
 
        temp_control = LockableFiles(self.transport, '', TransportLock)
 
1087
        temp_control = lockable_files.LockableFiles(self.transport, '',
 
1088
                                     lockable_files.TransportLock)
884
1089
        return temp_control._dir_mode
885
1090
 
 
1091
    def get_branch_reference(self):
 
1092
        """See BzrDir.get_branch_reference()."""
 
1093
        from bzrlib.branch import BranchFormat
 
1094
        format = BranchFormat.find_format(self)
 
1095
        return format.get_reference(self)
 
1096
 
886
1097
    def get_branch_transport(self, branch_format):
887
1098
        """See BzrDir.get_branch_transport()."""
888
1099
        if branch_format is None:
940
1151
                return True
941
1152
        except errors.NoRepositoryPresent:
942
1153
            pass
943
 
        # currently there are no other possible conversions for meta1 formats.
 
1154
        try:
 
1155
            if not isinstance(self.open_branch()._format,
 
1156
                              format.get_branch_format().__class__):
 
1157
                # the branch needs an upgrade.
 
1158
                return True
 
1159
        except errors.NotBranchError:
 
1160
            pass
 
1161
        try:
 
1162
            my_wt = self.open_workingtree(recommend_upgrade=False)
 
1163
            if not isinstance(my_wt._format,
 
1164
                              format.workingtree_format.__class__):
 
1165
                # the workingtree needs an upgrade.
 
1166
                return True
 
1167
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
1168
            pass
944
1169
        return False
945
1170
 
946
1171
    def open_branch(self, unsupported=False):
947
1172
        """See BzrDir.open_branch."""
948
 
        from bzrlib.branch import BranchFormat
949
 
        format = BranchFormat.find_format(self)
 
1173
        format = self.find_branch_format()
950
1174
        self._check_supported(format, unsupported)
951
1175
        return format.open(self, _found=True)
952
1176
 
957
1181
        self._check_supported(format, unsupported)
958
1182
        return format.open(self, _found=True)
959
1183
 
960
 
    def open_workingtree(self, unsupported=False):
 
1184
    def open_workingtree(self, unsupported=False,
 
1185
            recommend_upgrade=True):
961
1186
        """See BzrDir.open_workingtree."""
962
1187
        from bzrlib.workingtree import WorkingTreeFormat
963
1188
        format = WorkingTreeFormat.find_format(self)
964
 
        self._check_supported(format, unsupported)
 
1189
        self._check_supported(format, unsupported,
 
1190
            recommend_upgrade,
 
1191
            basedir=self.root_transport.base)
965
1192
        return format.open(self, _found=True)
966
1193
 
967
1194
 
994
1221
    This is a list of BzrDirFormat objects.
995
1222
    """
996
1223
 
 
1224
    _control_server_formats = []
 
1225
    """The registered control server formats, e.g. RemoteBzrDirs.
 
1226
 
 
1227
    This is a list of BzrDirFormat objects.
 
1228
    """
 
1229
 
997
1230
    _lock_file_name = 'branch-lock'
998
1231
 
999
1232
    # _lock_class must be set in subclasses to the lock type, typ.
1000
1233
    # TransportLock or LockDir
1001
1234
 
1002
1235
    @classmethod
1003
 
    def find_format(klass, transport):
 
1236
    def find_format(klass, transport, _server_formats=True):
1004
1237
        """Return the format present at transport."""
1005
 
        for format in klass._control_formats:
 
1238
        if _server_formats:
 
1239
            formats = klass._control_server_formats + klass._control_formats
 
1240
        else:
 
1241
            formats = klass._control_formats
 
1242
        for format in formats:
1006
1243
            try:
1007
1244
                return format.probe_transport(transport)
1008
1245
            except errors.NotBranchError:
1012
1249
 
1013
1250
    @classmethod
1014
1251
    def probe_transport(klass, transport):
1015
 
        """Return the .bzrdir style transport present at URL."""
 
1252
        """Return the .bzrdir style format present in a directory."""
1016
1253
        try:
1017
1254
            format_string = transport.get(".bzr/branch-format").read()
1018
1255
        except errors.NoSuchFile:
1062
1299
        """Initialize a new bzrdir in the base directory of a Transport."""
1063
1300
        # Since we don't have a .bzr directory, inherit the
1064
1301
        # mode from the root directory
1065
 
        temp_control = LockableFiles(transport, '', TransportLock)
 
1302
        temp_control = lockable_files.LockableFiles(transport,
 
1303
                            '', lockable_files.TransportLock)
1066
1304
        temp_control._transport.mkdir('.bzr',
1067
1305
                                      # FIXME: RBC 20060121 don't peek under
1068
1306
                                      # the covers
1077
1315
                      ('branch-format', self.get_format_string()),
1078
1316
                      ]
1079
1317
        # NB: no need to escape relative paths that are url safe.
1080
 
        control_files = LockableFiles(control, self._lock_file_name, 
1081
 
                                      self._lock_class)
 
1318
        control_files = lockable_files.LockableFiles(control,
 
1319
                            self._lock_file_name, self._lock_class)
1082
1320
        control_files.create_lock()
1083
1321
        control_files.lock_write()
1084
1322
        try:
1126
1364
        _found is a private parameter, do not use it.
1127
1365
        """
1128
1366
        if not _found:
1129
 
            assert isinstance(BzrDirFormat.find_format(transport),
1130
 
                              self.__class__)
 
1367
            found_format = BzrDirFormat.find_format(transport)
 
1368
            if not isinstance(found_format, self.__class__):
 
1369
                raise AssertionError("%s was asked to open %s, but it seems to need "
 
1370
                        "format %s" 
 
1371
                        % (self, transport, found_format))
1131
1372
        return self._open(transport)
1132
1373
 
1133
1374
    def _open(self, transport):
1144
1385
 
1145
1386
    @classmethod
1146
1387
    def register_control_format(klass, format):
1147
 
        """Register a format that does not use '.bzrdir' for its control dir.
 
1388
        """Register a format that does not use '.bzr' for its control dir.
1148
1389
 
1149
1390
        TODO: This should be pulled up into a 'ControlDirFormat' base class
1150
1391
        which BzrDirFormat can inherit from, and renamed to register_format 
1154
1395
        klass._control_formats.append(format)
1155
1396
 
1156
1397
    @classmethod
 
1398
    def register_control_server_format(klass, format):
 
1399
        """Register a control format for client-server environments.
 
1400
 
 
1401
        These formats will be tried before ones registered with
 
1402
        register_control_format.  This gives implementations that decide to the
 
1403
        chance to grab it before anything looks at the contents of the format
 
1404
        file.
 
1405
        """
 
1406
        klass._control_server_formats.append(format)
 
1407
 
 
1408
    @classmethod
 
1409
    @symbol_versioning.deprecated_method(symbol_versioning.zero_fourteen)
1157
1410
    def set_default_format(klass, format):
 
1411
        klass._set_default_format(format)
 
1412
 
 
1413
    @classmethod
 
1414
    def _set_default_format(klass, format):
 
1415
        """Set default format (for testing behavior of defaults only)"""
1158
1416
        klass._default_format = format
1159
1417
 
1160
1418
    def __str__(self):
1170
1428
        klass._control_formats.remove(format)
1171
1429
 
1172
1430
 
1173
 
# register BzrDirFormat as a control format
1174
 
BzrDirFormat.register_control_format(BzrDirFormat)
1175
 
 
1176
 
 
1177
1431
class BzrDirFormat4(BzrDirFormat):
1178
1432
    """Bzr dir format 4.
1179
1433
 
1187
1441
    removed in format 5; write support for this format has been removed.
1188
1442
    """
1189
1443
 
1190
 
    _lock_class = TransportLock
 
1444
    _lock_class = lockable_files.TransportLock
1191
1445
 
1192
1446
    def get_format_string(self):
1193
1447
        """See BzrDirFormat.get_format_string()."""
1221
1475
 
1222
1476
    def __return_repository_format(self):
1223
1477
        """Circular import protection."""
1224
 
        from bzrlib.repository import RepositoryFormat4
 
1478
        from bzrlib.repofmt.weaverepo import RepositoryFormat4
1225
1479
        return RepositoryFormat4()
1226
1480
    repository_format = property(__return_repository_format)
1227
1481
 
1237
1491
       Unhashed stores in the repository.
1238
1492
    """
1239
1493
 
1240
 
    _lock_class = TransportLock
 
1494
    _lock_class = lockable_files.TransportLock
1241
1495
 
1242
1496
    def get_format_string(self):
1243
1497
        """See BzrDirFormat.get_format_string()."""
1261
1515
        Except when they are being cloned.
1262
1516
        """
1263
1517
        from bzrlib.branch import BzrBranchFormat4
1264
 
        from bzrlib.repository import RepositoryFormat5
 
1518
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
1265
1519
        from bzrlib.workingtree import WorkingTreeFormat2
1266
1520
        result = (super(BzrDirFormat5, self).initialize_on_transport(transport))
1267
1521
        RepositoryFormat5().initialize(result, _internal=True)
1281
1535
 
1282
1536
    def __return_repository_format(self):
1283
1537
        """Circular import protection."""
1284
 
        from bzrlib.repository import RepositoryFormat5
 
1538
        from bzrlib.repofmt.weaverepo import RepositoryFormat5
1285
1539
        return RepositoryFormat5()
1286
1540
    repository_format = property(__return_repository_format)
1287
1541
 
1296
1550
     - Format 6 repositories [always]
1297
1551
    """
1298
1552
 
1299
 
    _lock_class = TransportLock
 
1553
    _lock_class = lockable_files.TransportLock
1300
1554
 
1301
1555
    def get_format_string(self):
1302
1556
        """See BzrDirFormat.get_format_string()."""
1320
1574
        Except when they are being cloned.
1321
1575
        """
1322
1576
        from bzrlib.branch import BzrBranchFormat4
1323
 
        from bzrlib.repository import RepositoryFormat6
 
1577
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
1324
1578
        from bzrlib.workingtree import WorkingTreeFormat2
1325
1579
        result = super(BzrDirFormat6, self).initialize_on_transport(transport)
1326
1580
        RepositoryFormat6().initialize(result, _internal=True)
1340
1594
 
1341
1595
    def __return_repository_format(self):
1342
1596
        """Circular import protection."""
1343
 
        from bzrlib.repository import RepositoryFormat6
 
1597
        from bzrlib.repofmt.weaverepo import RepositoryFormat6
1344
1598
        return RepositoryFormat6()
1345
1599
    repository_format = property(__return_repository_format)
1346
1600
 
1356
1610
     - Format 7 repositories [optional]
1357
1611
    """
1358
1612
 
1359
 
    _lock_class = LockDir
 
1613
    _lock_class = lockdir.LockDir
 
1614
 
 
1615
    def __init__(self):
 
1616
        self._workingtree_format = None
 
1617
        self._branch_format = None
 
1618
 
 
1619
    def __eq__(self, other):
 
1620
        if other.__class__ is not self.__class__:
 
1621
            return False
 
1622
        if other.repository_format != self.repository_format:
 
1623
            return False
 
1624
        if other.workingtree_format != self.workingtree_format:
 
1625
            return False
 
1626
        return True
 
1627
 
 
1628
    def __ne__(self, other):
 
1629
        return not self == other
 
1630
 
 
1631
    def get_branch_format(self):
 
1632
        if self._branch_format is None:
 
1633
            from bzrlib.branch import BranchFormat
 
1634
            self._branch_format = BranchFormat.get_default_format()
 
1635
        return self._branch_format
 
1636
 
 
1637
    def set_branch_format(self, format):
 
1638
        self._branch_format = format
1360
1639
 
1361
1640
    def get_converter(self, format=None):
1362
1641
        """See BzrDirFormat.get_converter()."""
1392
1671
 
1393
1672
    repository_format = property(__return_repository_format, __set_repository_format)
1394
1673
 
1395
 
 
 
1674
    def __get_workingtree_format(self):
 
1675
        if self._workingtree_format is None:
 
1676
            from bzrlib.workingtree import WorkingTreeFormat
 
1677
            self._workingtree_format = WorkingTreeFormat.get_default_format()
 
1678
        return self._workingtree_format
 
1679
 
 
1680
    def __set_workingtree_format(self, wt_format):
 
1681
        self._workingtree_format = wt_format
 
1682
 
 
1683
    workingtree_format = property(__get_workingtree_format,
 
1684
                                  __set_workingtree_format)
 
1685
 
 
1686
 
 
1687
# Register bzr control format
 
1688
BzrDirFormat.register_control_format(BzrDirFormat)
 
1689
 
 
1690
# Register bzr formats
1396
1691
BzrDirFormat.register_format(BzrDirFormat4())
1397
1692
BzrDirFormat.register_format(BzrDirFormat5())
1398
1693
BzrDirFormat.register_format(BzrDirFormat6())
1399
1694
__default_format = BzrDirMetaFormat1()
1400
1695
BzrDirFormat.register_format(__default_format)
1401
 
BzrDirFormat.set_default_format(__default_format)
1402
 
 
1403
 
 
1404
 
class BzrDirTestProviderAdapter(object):
1405
 
    """A tool to generate a suite testing multiple bzrdir formats at once.
1406
 
 
1407
 
    This is done by copying the test once for each transport and injecting
1408
 
    the transport_server, transport_readonly_server, and bzrdir_format
1409
 
    classes into each copy. Each copy is also given a new id() to make it
1410
 
    easy to identify.
1411
 
    """
1412
 
 
1413
 
    def __init__(self, transport_server, transport_readonly_server, formats):
1414
 
        self._transport_server = transport_server
1415
 
        self._transport_readonly_server = transport_readonly_server
1416
 
        self._formats = formats
1417
 
    
1418
 
    def adapt(self, test):
1419
 
        result = TestSuite()
1420
 
        for format in self._formats:
1421
 
            new_test = deepcopy(test)
1422
 
            new_test.transport_server = self._transport_server
1423
 
            new_test.transport_readonly_server = self._transport_readonly_server
1424
 
            new_test.bzrdir_format = format
1425
 
            def make_new_test_id():
1426
 
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
1427
 
                return lambda: new_id
1428
 
            new_test.id = make_new_test_id()
1429
 
            result.addTest(new_test)
1430
 
        return result
 
1696
BzrDirFormat._default_format = __default_format
1431
1697
 
1432
1698
 
1433
1699
class Converter(object):
1520
1786
        self.bzrdir.transport.delete_tree('text-store')
1521
1787
 
1522
1788
    def _convert_working_inv(self):
1523
 
        inv = serializer_v4.read_inventory(self.branch.control_files.get('inventory'))
1524
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1789
        inv = xml4.serializer_v4.read_inventory(
 
1790
                    self.branch.control_files.get('inventory'))
 
1791
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1525
1792
        # FIXME inventory is a working tree change.
1526
1793
        self.branch.control_files.put('inventory', StringIO(new_inv_xml))
1527
1794
 
1553
1820
                                                      prefixed=False,
1554
1821
                                                      compressed=True))
1555
1822
        try:
1556
 
            transaction = bzrlib.transactions.WriteTransaction()
 
1823
            transaction = WriteTransaction()
1557
1824
            for i, rev_id in enumerate(self.converted_revs):
1558
1825
                self.pb.update('write revision', i, len(self.converted_revs))
1559
1826
                _revision_store.add_revision(self.revisions[rev_id], transaction)
1585
1852
    def _load_old_inventory(self, rev_id):
1586
1853
        assert rev_id not in self.converted_revs
1587
1854
        old_inv_xml = self.branch.repository.inventory_store.get(rev_id).read()
1588
 
        inv = serializer_v4.read_inventory_from_string(old_inv_xml)
 
1855
        inv = xml4.serializer_v4.read_inventory_from_string(old_inv_xml)
1589
1856
        inv.revision_id = rev_id
1590
1857
        rev = self.revisions[rev_id]
1591
1858
        if rev.inventory_sha1:
1596
1863
    def _load_updated_inventory(self, rev_id):
1597
1864
        assert rev_id in self.converted_revs
1598
1865
        inv_xml = self.inv_weave.get_text(rev_id)
1599
 
        inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(inv_xml)
 
1866
        inv = xml5.serializer_v5.read_inventory_from_string(inv_xml)
1600
1867
        return inv
1601
1868
 
1602
1869
    def _convert_one_rev(self, rev_id):
1618
1885
                assert getattr(ie, 'revision', None) is not None, \
1619
1886
                    'no revision on {%s} in {%s}' % \
1620
1887
                    (file_id, rev.revision_id)
1621
 
        new_inv_xml = bzrlib.xml5.serializer_v5.write_inventory_to_string(inv)
 
1888
        new_inv_xml = xml5.serializer_v5.write_inventory_to_string(inv)
1622
1889
        new_inv_sha1 = sha_string(new_inv_xml)
1623
1890
        self.inv_weave.add_lines(rev.revision_id, 
1624
1891
                                 present_parents,
1657
1924
                                                  entry_vf=w)
1658
1925
        for old_revision in previous_entries:
1659
1926
                # if this fails, its a ghost ?
1660
 
                assert old_revision in self.converted_revs 
 
1927
                assert old_revision in self.converted_revs, \
 
1928
                    "Revision {%s} not in converted_revs" % old_revision
1661
1929
        self.snapshot_ie(previous_entries, ie, w, rev_id)
1662
1930
        del ie.text_id
1663
1931
        assert getattr(ie, 'revision', None) is not None
1750
2018
 
1751
2019
    def convert(self, to_convert, pb):
1752
2020
        """See Converter.convert()."""
 
2021
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
2022
        from bzrlib.branch import BzrBranchFormat5
1753
2023
        self.bzrdir = to_convert
1754
2024
        self.pb = pb
1755
2025
        self.count = 0
1784
2054
        # we hard code the formats here because we are converting into
1785
2055
        # the meta format. The meta format upgrader can take this to a 
1786
2056
        # future format within each component.
1787
 
        self.put_format('repository', bzrlib.repository.RepositoryFormat7())
 
2057
        self.put_format('repository', RepositoryFormat7())
1788
2058
        for entry in repository_names:
1789
2059
            self.move_entry('repository', entry)
1790
2060
 
1791
2061
        self.step('Upgrading branch      ')
1792
2062
        self.bzrdir.transport.mkdir('branch', mode=self.dir_mode)
1793
2063
        self.make_lock('branch')
1794
 
        self.put_format('branch', bzrlib.branch.BzrBranchFormat5())
 
2064
        self.put_format('branch', BzrBranchFormat5())
1795
2065
        branch_files = [('revision-history', True),
1796
2066
                        ('branch-name', True),
1797
2067
                        ('parent', False)]
1817
2087
                if name in bzrcontents:
1818
2088
                    self.bzrdir.transport.delete(name)
1819
2089
        else:
 
2090
            from bzrlib.workingtree import WorkingTreeFormat3
1820
2091
            self.step('Upgrading working tree')
1821
2092
            self.bzrdir.transport.mkdir('checkout', mode=self.dir_mode)
1822
2093
            self.make_lock('checkout')
1823
2094
            self.put_format(
1824
 
                'checkout', bzrlib.workingtree.WorkingTreeFormat3())
 
2095
                'checkout', WorkingTreeFormat3())
1825
2096
            self.bzrdir.transport.delete_multi(
1826
2097
                self.garbage_inventories, self.pb)
1827
2098
            for entry in checkout_files:
1836
2107
    def make_lock(self, name):
1837
2108
        """Make a lock for the new control dir name."""
1838
2109
        self.step('Make %s lock' % name)
1839
 
        ld = LockDir(self.bzrdir.transport, 
1840
 
                     '%s/lock' % name,
1841
 
                     file_modebits=self.file_mode,
1842
 
                     dir_modebits=self.dir_mode)
 
2110
        ld = lockdir.LockDir(self.bzrdir.transport,
 
2111
                             '%s/lock' % name,
 
2112
                             file_modebits=self.file_mode,
 
2113
                             dir_modebits=self.dir_mode)
1843
2114
        ld.create()
1844
2115
 
1845
2116
    def move_entry(self, new_dir, entry):
1884
2155
                self.pb.note('starting repository conversion')
1885
2156
                converter = CopyConverter(self.target_format.repository_format)
1886
2157
                converter.convert(repo, pb)
 
2158
        try:
 
2159
            branch = self.bzrdir.open_branch()
 
2160
        except errors.NotBranchError:
 
2161
            pass
 
2162
        else:
 
2163
            # TODO: conversions of Branch and Tree should be done by
 
2164
            # InterXFormat lookups
 
2165
            # Avoid circular imports
 
2166
            from bzrlib import branch as _mod_branch
 
2167
            if (branch._format.__class__ is _mod_branch.BzrBranchFormat5 and
 
2168
                self.target_format.get_branch_format().__class__ is
 
2169
                _mod_branch.BzrBranchFormat6):
 
2170
                branch_converter = _mod_branch.Converter5to6()
 
2171
                branch_converter.convert(branch)
 
2172
        try:
 
2173
            tree = self.bzrdir.open_workingtree(recommend_upgrade=False)
 
2174
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2175
            pass
 
2176
        else:
 
2177
            # TODO: conversions of Branch and Tree should be done by
 
2178
            # InterXFormat lookups
 
2179
            if (isinstance(tree, workingtree.WorkingTree3) and
 
2180
                not isinstance(tree, workingtree_4.WorkingTree4) and
 
2181
                isinstance(self.target_format.workingtree_format,
 
2182
                    workingtree_4.WorkingTreeFormat4)):
 
2183
                workingtree_4.Converter3to4().convert(tree)
1887
2184
        return to_convert
 
2185
 
 
2186
 
 
2187
# This is not in remote.py because it's small, and needs to be registered.
 
2188
# Putting it in remote.py creates a circular import problem.
 
2189
# we can make it a lazy object if the control formats is turned into something
 
2190
# like a registry.
 
2191
class RemoteBzrDirFormat(BzrDirMetaFormat1):
 
2192
    """Format representing bzrdirs accessed via a smart server"""
 
2193
 
 
2194
    def get_format_description(self):
 
2195
        return 'bzr remote bzrdir'
 
2196
    
 
2197
    @classmethod
 
2198
    def probe_transport(klass, transport):
 
2199
        """Return a RemoteBzrDirFormat object if it looks possible."""
 
2200
        try:
 
2201
            client = transport.get_smart_client()
 
2202
        except (NotImplementedError, AttributeError,
 
2203
                errors.TransportNotPossible):
 
2204
            # no smart server, so not a branch for this format type.
 
2205
            raise errors.NotBranchError(path=transport.base)
 
2206
        else:
 
2207
            # Send a 'hello' request in protocol version one, and decline to
 
2208
            # open it if the server doesn't support our required version (2) so
 
2209
            # that the VFS-based transport will do it.
 
2210
            request = client.get_request()
 
2211
            smart_protocol = protocol.SmartClientRequestProtocolOne(request)
 
2212
            server_version = smart_protocol.query_version()
 
2213
            if server_version != 2:
 
2214
                raise errors.NotBranchError(path=transport.base)
 
2215
            return klass()
 
2216
 
 
2217
    def initialize_on_transport(self, transport):
 
2218
        try:
 
2219
            # hand off the request to the smart server
 
2220
            medium = transport.get_smart_medium()
 
2221
        except errors.NoSmartMedium:
 
2222
            # TODO: lookup the local format from a server hint.
 
2223
            local_dir_format = BzrDirMetaFormat1()
 
2224
            return local_dir_format.initialize_on_transport(transport)
 
2225
        client = _SmartClient(medium)
 
2226
        path = client.remote_path_from_transport(transport)
 
2227
        response = _SmartClient(medium).call('BzrDirFormat.initialize', path)
 
2228
        assert response[0] in ('ok', ), 'unexpected response code %s' % (response,)
 
2229
        return remote.RemoteBzrDir(transport)
 
2230
 
 
2231
    def _open(self, transport):
 
2232
        return remote.RemoteBzrDir(transport)
 
2233
 
 
2234
    def __eq__(self, other):
 
2235
        if not isinstance(other, RemoteBzrDirFormat):
 
2236
            return False
 
2237
        return self.get_format_description() == other.get_format_description()
 
2238
 
 
2239
 
 
2240
BzrDirFormat.register_control_server_format(RemoteBzrDirFormat)
 
2241
 
 
2242
 
 
2243
class BzrDirFormatInfo(object):
 
2244
 
 
2245
    def __init__(self, native, deprecated, hidden):
 
2246
        self.deprecated = deprecated
 
2247
        self.native = native
 
2248
        self.hidden = hidden
 
2249
 
 
2250
 
 
2251
class BzrDirFormatRegistry(registry.Registry):
 
2252
    """Registry of user-selectable BzrDir subformats.
 
2253
    
 
2254
    Differs from BzrDirFormat._control_formats in that it provides sub-formats,
 
2255
    e.g. BzrDirMeta1 with weave repository.  Also, it's more user-oriented.
 
2256
    """
 
2257
 
 
2258
    def register_metadir(self, key,
 
2259
             repository_format, help, native=True, deprecated=False,
 
2260
             branch_format=None,
 
2261
             tree_format=None,
 
2262
             hidden=False):
 
2263
        """Register a metadir subformat.
 
2264
 
 
2265
        These all use a BzrDirMetaFormat1 bzrdir, but can be parameterized
 
2266
        by the Repository format.
 
2267
 
 
2268
        :param repository_format: The fully-qualified repository format class
 
2269
            name as a string.
 
2270
        :param branch_format: Fully-qualified branch format class name as
 
2271
            a string.
 
2272
        :param tree_format: Fully-qualified tree format class name as
 
2273
            a string.
 
2274
        """
 
2275
        # This should be expanded to support setting WorkingTree and Branch
 
2276
        # formats, once BzrDirMetaFormat1 supports that.
 
2277
        def _load(full_name):
 
2278
            mod_name, factory_name = full_name.rsplit('.', 1)
 
2279
            try:
 
2280
                mod = __import__(mod_name, globals(), locals(),
 
2281
                        [factory_name])
 
2282
            except ImportError, e:
 
2283
                raise ImportError('failed to load %s: %s' % (full_name, e))
 
2284
            try:
 
2285
                factory = getattr(mod, factory_name)
 
2286
            except AttributeError:
 
2287
                raise AttributeError('no factory %s in module %r'
 
2288
                    % (full_name, mod))
 
2289
            return factory()
 
2290
 
 
2291
        def helper():
 
2292
            bd = BzrDirMetaFormat1()
 
2293
            if branch_format is not None:
 
2294
                bd.set_branch_format(_load(branch_format))
 
2295
            if tree_format is not None:
 
2296
                bd.workingtree_format = _load(tree_format)
 
2297
            if repository_format is not None:
 
2298
                bd.repository_format = _load(repository_format)
 
2299
            return bd
 
2300
        self.register(key, helper, help, native, deprecated, hidden)
 
2301
 
 
2302
    def register(self, key, factory, help, native=True, deprecated=False,
 
2303
                 hidden=False):
 
2304
        """Register a BzrDirFormat factory.
 
2305
        
 
2306
        The factory must be a callable that takes one parameter: the key.
 
2307
        It must produce an instance of the BzrDirFormat when called.
 
2308
 
 
2309
        This function mainly exists to prevent the info object from being
 
2310
        supplied directly.
 
2311
        """
 
2312
        registry.Registry.register(self, key, factory, help, 
 
2313
            BzrDirFormatInfo(native, deprecated, hidden))
 
2314
 
 
2315
    def register_lazy(self, key, module_name, member_name, help, native=True,
 
2316
                      deprecated=False, hidden=False):
 
2317
        registry.Registry.register_lazy(self, key, module_name, member_name, 
 
2318
            help, BzrDirFormatInfo(native, deprecated, hidden))
 
2319
 
 
2320
    def set_default(self, key):
 
2321
        """Set the 'default' key to be a clone of the supplied key.
 
2322
        
 
2323
        This method must be called once and only once.
 
2324
        """
 
2325
        registry.Registry.register(self, 'default', self.get(key), 
 
2326
            self.get_help(key), info=self.get_info(key))
 
2327
 
 
2328
    def set_default_repository(self, key):
 
2329
        """Set the FormatRegistry default and Repository default.
 
2330
        
 
2331
        This is a transitional method while Repository.set_default_format
 
2332
        is deprecated.
 
2333
        """
 
2334
        if 'default' in self:
 
2335
            self.remove('default')
 
2336
        self.set_default(key)
 
2337
        format = self.get('default')()
 
2338
        assert isinstance(format, BzrDirMetaFormat1)
 
2339
 
 
2340
    def make_bzrdir(self, key):
 
2341
        return self.get(key)()
 
2342
 
 
2343
    def help_topic(self, topic):
 
2344
        output = textwrap.dedent("""\
 
2345
            Bazaar directory formats
 
2346
            ------------------------
 
2347
 
 
2348
            These formats can be used for creating branches, working trees, and
 
2349
            repositories.
 
2350
 
 
2351
            """)
 
2352
        default_help = self.get_help('default')
 
2353
        help_pairs = []
 
2354
        for key in self.keys():
 
2355
            if key == 'default':
 
2356
                continue
 
2357
            help = self.get_help(key)
 
2358
            if help == default_help:
 
2359
                default_realkey = key
 
2360
            else:
 
2361
                help_pairs.append((key, help))
 
2362
 
 
2363
        def wrapped(key, help, info):
 
2364
            if info.native:
 
2365
                help = '(native) ' + help
 
2366
            return '  %s:\n%s\n\n' % (key, 
 
2367
                    textwrap.fill(help, initial_indent='    ', 
 
2368
                    subsequent_indent='    '))
 
2369
        output += wrapped('%s/default' % default_realkey, default_help,
 
2370
                          self.get_info('default'))
 
2371
        deprecated_pairs = []
 
2372
        for key, help in help_pairs:
 
2373
            info = self.get_info(key)
 
2374
            if info.hidden:
 
2375
                continue
 
2376
            elif info.deprecated:
 
2377
                deprecated_pairs.append((key, help))
 
2378
            else:
 
2379
                output += wrapped(key, help, info)
 
2380
        if len(deprecated_pairs) > 0:
 
2381
            output += "Deprecated formats\n------------------\n\n"
 
2382
            for key, help in deprecated_pairs:
 
2383
                info = self.get_info(key)
 
2384
                output += wrapped(key, help, info)
 
2385
 
 
2386
        return output
 
2387
 
 
2388
 
 
2389
format_registry = BzrDirFormatRegistry()
 
2390
format_registry.register('weave', BzrDirFormat6,
 
2391
    'Pre-0.8 format.  Slower than knit and does not'
 
2392
    ' support checkouts or shared repositories.',
 
2393
    deprecated=True)
 
2394
format_registry.register_metadir('knit',
 
2395
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2396
    'Format using knits.  Recommended for interoperation with bzr <= 0.14.',
 
2397
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2398
    tree_format='bzrlib.workingtree.WorkingTreeFormat3')
 
2399
format_registry.register_metadir('metaweave',
 
2400
    'bzrlib.repofmt.weaverepo.RepositoryFormat7',
 
2401
    'Transitional format in 0.8.  Slower than knit.',
 
2402
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2403
    tree_format='bzrlib.workingtree.WorkingTreeFormat3',
 
2404
    deprecated=True)
 
2405
format_registry.register_metadir('dirstate',
 
2406
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2407
    help='New in 0.15: Fast local operations. Compatible with bzr 0.8 and '
 
2408
        'above when accessed over the network.',
 
2409
    branch_format='bzrlib.branch.BzrBranchFormat5',
 
2410
    # this uses bzrlib.workingtree.WorkingTreeFormat4 because importing
 
2411
    # directly from workingtree_4 triggers a circular import.
 
2412
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2413
    )
 
2414
format_registry.register_metadir('dirstate-tags',
 
2415
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
 
2416
    help='New in 0.15: Fast local operations and improved scaling for '
 
2417
        'network operations. Additionally adds support for tags.'
 
2418
        ' Incompatible with bzr < 0.15.',
 
2419
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2420
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2421
    )
 
2422
format_registry.register_metadir('dirstate-with-subtree',
 
2423
    'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
 
2424
    help='New in 0.15: Fast local operations and improved scaling for '
 
2425
        'network operations. Additionally adds support for versioning nested '
 
2426
        'bzr branches. Incompatible with bzr < 0.15.',
 
2427
    branch_format='bzrlib.branch.BzrBranchFormat6',
 
2428
    tree_format='bzrlib.workingtree.WorkingTreeFormat4',
 
2429
    hidden=True,
 
2430
    )
 
2431
format_registry.set_default('dirstate')