~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
 
18
from copy import deepcopy
 
19
from cStringIO import StringIO
 
20
import errno
 
21
import os
18
22
import shutil
19
23
import sys
20
 
import os
21
 
import errno
 
24
from unittest import TestSuite
22
25
from warnings import warn
23
26
try:
24
27
    import xml.sax.saxutils
31
34
 
32
35
 
33
36
import bzrlib
34
 
from bzrlib.trace import mutter, note
35
 
from bzrlib.osutils import (isdir, quotefn,
36
 
                            rename, splitpath, sha_file,
37
 
                            file_kind, abspath, normpath, pathjoin)
 
37
from bzrlib.config import TreeConfig
 
38
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
39
from bzrlib.delta import compare_trees
38
40
import bzrlib.errors as errors
39
41
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
40
42
                           NoSuchRevision, HistoryMissing, NotBranchError,
41
 
                           DivergedBranches, LockError, UnlistableStore,
 
43
                           DivergedBranches, LockError,
 
44
                           UninitializableFormat,
 
45
                           UnlistableStore,
42
46
                           UnlistableBranch, NoSuchFile, NotVersionedError,
43
47
                           NoWorkingTree)
44
 
from bzrlib.textui import show_status
45
 
from bzrlib.config import TreeConfig
46
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
47
 
from bzrlib.delta import compare_trees
48
48
import bzrlib.inventory as inventory
49
49
from bzrlib.inventory import Inventory
50
50
from bzrlib.lockable_files import LockableFiles
 
51
from bzrlib.osutils import (isdir, quotefn,
 
52
                            rename, splitpath, sha_file,
 
53
                            file_kind, abspath, normpath, pathjoin,
 
54
                            safe_unicode,
 
55
                            )
 
56
from bzrlib.textui import show_status
 
57
from bzrlib.trace import mutter, note
 
58
from bzrlib.tree import EmptyTree, RevisionTree
 
59
from bzrlib.repository import Repository
51
60
from bzrlib.revision import (Revision, is_ancestor, get_intervening_revisions)
52
 
from bzrlib.repository import Repository
53
61
from bzrlib.store import copy_all
 
62
from bzrlib.symbol_versioning import *
54
63
import bzrlib.transactions as transactions
55
64
from bzrlib.transport import Transport, get_transport
56
65
from bzrlib.tree import EmptyTree, RevisionTree
81
90
    base
82
91
        Base directory/url of the branch.
83
92
    """
 
93
    # this is really an instance variable - FIXME move it there
 
94
    # - RBC 20060112
84
95
    base = None
85
96
 
 
97
    _default_initializer = None
 
98
    """The default initializer for making new branches."""
 
99
 
86
100
    def __init__(self, *ignored, **ignored_too):
87
101
        raise NotImplementedError('The Branch class is abstract')
88
102
 
89
103
    @staticmethod
90
104
    def open_downlevel(base):
91
 
        """Open a branch which may be of an old format.
92
 
        
93
 
        Only local branches are supported."""
94
 
        return BzrBranch(get_transport(base), relax_version_check=True)
 
105
        """Open a branch which may be of an old format."""
 
106
        return Branch.open(base, _unsupported=True)
95
107
        
96
108
    @staticmethod
97
 
    def open(base):
98
 
        """Open an existing branch, rooted at 'base' (url)"""
 
109
    def open(base, _unsupported=False):
 
110
        """Open an existing branch, rooted at 'base' (url)
 
111
        
 
112
        _unsupported is a private parameter to the Branch class.
 
113
        """
99
114
        t = get_transport(base)
100
115
        mutter("trying to open %r with transport %r", base, t)
101
 
        return BzrBranch(t)
 
116
        format = BzrBranchFormat.find_format(t)
 
117
        if not _unsupported and not format.is_supported():
 
118
            # see open_downlevel to open legacy branches.
 
119
            raise errors.UnsupportedFormatError(
 
120
                    'sorry, branch format %s not supported' % format,
 
121
                    ['use a different bzr version',
 
122
                     'or remove the .bzr directory'
 
123
                     ' and "bzr init" again'])
 
124
        return format.open(t)
102
125
 
103
126
    @staticmethod
104
127
    def open_containing(url):
108
131
 
109
132
        Basically we keep looking up until we find the control directory or
110
133
        run into the root.  If there isn't one, raises NotBranchError.
 
134
        If there is one and it is either an unrecognised format or an unsupported 
 
135
        format, UnknownFormatError or UnsupportedFormatError are raised.
111
136
        If there is one, it is returned, along with the unused portion of url.
112
137
        """
113
138
        t = get_transport(url)
 
139
        # this gets the normalised url back. I.e. '.' -> the full path.
 
140
        url = t.base
114
141
        while True:
115
142
            try:
116
 
                return BzrBranch(t), t.relpath(url)
 
143
                format = BzrBranchFormat.find_format(t)
 
144
                return format.open(t), t.relpath(url)
117
145
            except NotBranchError, e:
118
146
                mutter('not a branch in: %r %s', t.base, e)
119
147
            new_t = t.clone('..')
123
151
            t = new_t
124
152
 
125
153
    @staticmethod
 
154
    def create(base):
 
155
        """Create a new Branch at the url 'bzr'.
 
156
        
 
157
        This will call the current default initializer with base
 
158
        as the only parameter.
 
159
        """
 
160
        return Branch._default_initializer(safe_unicode(base))
 
161
 
 
162
    @staticmethod
 
163
    @deprecated_function(zero_eight)
126
164
    def initialize(base):
127
 
        """Create a new branch, rooted at 'base' (url)"""
128
 
        t = get_transport(unicode(base))
129
 
        return BzrBranch(t, init=True)
 
165
        """Create a new working tree and branch, rooted at 'base' (url)
 
166
 
 
167
        NOTE: This will soon be deprecated in favour of creation
 
168
        through a BzrDir.
 
169
        """
 
170
        # imported here to prevent scope creep as this is going.
 
171
        from bzrlib.workingtree import WorkingTree
 
172
        return WorkingTree.create_standalone(safe_unicode(base)).branch
 
173
 
 
174
    @staticmethod
 
175
    def get_default_initializer():
 
176
        """Return the initializer being used for new branches."""
 
177
        return Branch._default_initializer
 
178
 
 
179
    @staticmethod
 
180
    def set_default_initializer(initializer):
 
181
        """Set the initializer to be used for new branches."""
 
182
        Branch._default_initializer = staticmethod(initializer)
130
183
 
131
184
    def setup_caching(self, cache_root):
132
185
        """Subclasses that care about caching should override this, and set
212
265
        If self and other have not diverged, return a list of the revisions
213
266
        present in other, but missing from self.
214
267
 
215
 
        >>> from bzrlib.commit import commit
216
268
        >>> bzrlib.trace.silent = True
217
269
        >>> br1 = ScratchBranch()
218
270
        >>> br2 = ScratchBranch()
219
271
        >>> br1.missing_revisions(br2)
220
272
        []
221
 
        >>> commit(br2, "lala!", rev_id="REVISION-ID-1")
 
273
        >>> br2.working_tree().commit("lala!", rev_id="REVISION-ID-1")
222
274
        >>> br1.missing_revisions(br2)
223
275
        [u'REVISION-ID-1']
224
276
        >>> br2.missing_revisions(br1)
225
277
        []
226
 
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1")
 
278
        >>> br1.working_tree().commit("lala!", rev_id="REVISION-ID-1")
227
279
        >>> br1.missing_revisions(br2)
228
280
        []
229
 
        >>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
 
281
        >>> br2.working_tree().commit("lala!", rev_id="REVISION-ID-2A")
230
282
        >>> br1.missing_revisions(br2)
231
283
        [u'REVISION-ID-2A']
232
 
        >>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
 
284
        >>> br1.working_tree().commit("lala!", rev_id="REVISION-ID-2B")
233
285
        >>> br1.missing_revisions(br2)
234
286
        Traceback (most recent call last):
235
287
        DivergedBranches: These branches have diverged.  Try merge.
351
403
        if revno < 1 or revno > self.revno():
352
404
            raise InvalidRevisionNumber(revno)
353
405
        
354
 
    def sign_revision(self, revision_id, gpg_strategy):
355
 
        raise NotImplementedError('sign_revision is abstract')
356
 
 
357
 
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
358
 
        raise NotImplementedError('store_revision_signature is abstract')
359
 
 
360
406
    def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
361
407
        """Copy this branch into the existing directory to_location.
362
408
 
378
424
        to_branch_type
379
425
            Branch type of destination branch
380
426
        """
381
 
        # circular import protection
382
 
        from bzrlib.merge import build_working_dir
383
 
 
 
427
        from bzrlib.workingtree import WorkingTree
384
428
        assert isinstance(to_location, basestring)
385
429
        if not bzrlib.osutils.lexists(to_location):
386
430
            os.mkdir(to_location)
387
431
        if to_branch_type is None:
388
432
            to_branch_type = BzrBranch
 
433
        print "FIXME use a branch format here"
389
434
        br_to = to_branch_type.initialize(to_location)
390
435
        mutter("copy branch from %s to %s", self, br_to)
391
436
        if basis_branch is not None:
392
437
            basis_branch.push_stores(br_to)
393
 
        br_to.working_tree().set_root_id(self.get_root_id())
394
438
        if revision is None:
395
439
            revision = self.last_revision()
396
440
        br_to.update_revisions(self, stop_revision=revision)
397
441
        br_to.set_parent(self.base)
398
 
        build_working_dir(to_location)
 
442
        WorkingTree.create(br_to, to_location).set_root_id(self.get_root_id())
399
443
        mutter("copied")
400
444
        return br_to
401
445
 
441
485
        """
442
486
        raise NotImplementedError('fileid_involved_by_set is abstract')
443
487
 
 
488
class BzrBranchFormat(object):
 
489
    """An encapsulation of the initialization and open routines for a format.
 
490
 
 
491
    Formats provide three things:
 
492
     * An initialization routine,
 
493
     * a format string,
 
494
     * an open routine.
 
495
 
 
496
    Formats are placed in an dict by their format string for reference 
 
497
    during branch opening. Its not required that these be instances, they
 
498
    can be classes themselves with class methods - it simply depends on 
 
499
    whether state is needed for a given format or not.
 
500
 
 
501
    Once a format is deprecated, just deprecate the initialize and open
 
502
    methods on the format class. Do not deprecate the object, as the 
 
503
    object will be created every time regardless.
 
504
    """
 
505
 
 
506
    _formats = {}
 
507
    """The known formats."""
 
508
 
 
509
    @classmethod
 
510
    def find_format(klass, transport):
 
511
        """Return the format registered for URL."""
 
512
        try:
 
513
            format_string = transport.get(".bzr/branch-format").read()
 
514
            return klass._formats[format_string]
 
515
        except NoSuchFile:
 
516
            raise NotBranchError(path=transport.base)
 
517
        except KeyError:
 
518
            raise errors.UnknownFormatError(format_string)
 
519
 
 
520
    def get_format_string(self):
 
521
        """Return the ASCII format string that identifies this format."""
 
522
        raise NotImplementedError(self.get_format_string)
 
523
 
 
524
    def _find_modes(self, t):
 
525
        """Determine the appropriate modes for files and directories.
 
526
        
 
527
        FIXME: When this merges into, or from storage,
 
528
        this code becomes delgatable to a LockableFiles instance.
 
529
 
 
530
        For now its cribbed and returns (dir_mode, file_mode)
 
531
        """
 
532
        try:
 
533
            st = t.stat('.')
 
534
        except errors.TransportNotPossible:
 
535
            dir_mode = 0755
 
536
            file_mode = 0644
 
537
        else:
 
538
            dir_mode = st.st_mode & 07777
 
539
            # Remove the sticky and execute bits for files
 
540
            file_mode = dir_mode & ~07111
 
541
        if not BzrBranch._set_dir_mode:
 
542
            dir_mode = None
 
543
        if not BzrBranch._set_file_mode:
 
544
            file_mode = None
 
545
        return dir_mode, file_mode
 
546
 
 
547
    def initialize(self, url):
 
548
        """Create a branch of this format at url and return an open branch."""
 
549
        t = get_transport(url)
 
550
        from bzrlib.weavefile import write_weave_v5
 
551
        from bzrlib.weave import Weave
 
552
        
 
553
        # Create an empty weave
 
554
        sio = StringIO()
 
555
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
 
556
        empty_weave = sio.getvalue()
 
557
 
 
558
        # Since we don't have a .bzr directory, inherit the
 
559
        # mode from the root directory
 
560
        temp_control = LockableFiles(t, '')
 
561
        temp_control._transport.mkdir('.bzr',
 
562
                                      mode=temp_control._dir_mode)
 
563
        file_mode = temp_control._file_mode
 
564
        del temp_control
 
565
        mutter('created control directory in ' + t.base)
 
566
        control = t.clone('.bzr')
 
567
        dirs = ['revision-store', 'weaves']
 
568
        lock_file = 'branch-lock'
 
569
        utf8_files = [('README', 
 
570
                       "This is a Bazaar-NG control directory.\n"
 
571
                       "Do not change any files in this directory.\n"),
 
572
                      ('branch-format', self.get_format_string()),
 
573
                      ('revision-history', ''),
 
574
                      ('branch-name', ''),
 
575
                      ]
 
576
        files = [('inventory.weave', StringIO(empty_weave)), 
 
577
                 ]
 
578
        
 
579
        # FIXME: RBC 20060125 dont peek under the covers
 
580
        # NB: no need to escape relative paths that are url safe.
 
581
        control.put(lock_file, StringIO(), mode=file_mode)
 
582
        control_files = LockableFiles(control, lock_file)
 
583
        control_files.lock_write()
 
584
        control_files._transport.mkdir_multi(dirs,
 
585
                mode=control_files._dir_mode)
 
586
        try:
 
587
            for file, content in utf8_files:
 
588
                control_files.put_utf8(file, content)
 
589
            for file, content in files:
 
590
                control_files.put(file, content)
 
591
        finally:
 
592
            control_files.unlock()
 
593
        return BzrBranch(t, _format=self, _control_files=control_files)
 
594
 
 
595
    def is_supported(self):
 
596
        """Is this format supported?
 
597
 
 
598
        Supported formats can be initialized and opened.
 
599
        Unsupported formats may not support initialization or committing or 
 
600
        some other features depending on the reason for not being supported.
 
601
        """
 
602
        return True
 
603
 
 
604
    def open(self, transport):
 
605
        """Fill out the data in branch for the branch at url."""
 
606
        return BzrBranch(transport, _format=self)
 
607
 
 
608
    @classmethod
 
609
    def register_format(klass, format):
 
610
        klass._formats[format.get_format_string()] = format
 
611
 
 
612
    @classmethod
 
613
    def unregister_format(klass, format):
 
614
        assert klass._formats[format.get_format_string()] is format
 
615
        del klass._formats[format.get_format_string()]
 
616
 
 
617
 
 
618
class BzrBranchFormat4(BzrBranchFormat):
 
619
    """Bzr branch format 4.
 
620
 
 
621
    This format has:
 
622
     - flat stores
 
623
     - TextStores for texts, inventories,revisions.
 
624
 
 
625
    This format is deprecated: it indexes texts using a text it which is
 
626
    removed in format 5; write support for this format has been removed.
 
627
    """
 
628
 
 
629
    def get_format_string(self):
 
630
        """See BzrBranchFormat.get_format_string()."""
 
631
        return BZR_BRANCH_FORMAT_4
 
632
 
 
633
    def initialize(self, url):
 
634
        """Format 4 branches cannot be created."""
 
635
        raise UninitializableFormat(self)
 
636
 
 
637
    def is_supported(self):
 
638
        """Format 4 is not supported.
 
639
 
 
640
        It is not supported because the model changed from 4 to 5 and the
 
641
        conversion logic is expensive - so doing it on the fly was not 
 
642
        feasible.
 
643
        """
 
644
        return False
 
645
 
 
646
 
 
647
class BzrBranchFormat5(BzrBranchFormat):
 
648
    """Bzr branch format 5.
 
649
 
 
650
    This format has:
 
651
     - weaves for file texts and inventory
 
652
     - flat stores
 
653
     - TextStores for revisions and signatures.
 
654
    """
 
655
 
 
656
    def get_format_string(self):
 
657
        """See BzrBranchFormat.get_format_string()."""
 
658
        return BZR_BRANCH_FORMAT_5
 
659
 
 
660
 
 
661
class BzrBranchFormat6(BzrBranchFormat):
 
662
    """Bzr branch format 6.
 
663
 
 
664
    This format has:
 
665
     - weaves for file texts and inventory
 
666
     - hash subdirectory based stores.
 
667
     - TextStores for revisions and signatures.
 
668
    """
 
669
 
 
670
    def get_format_string(self):
 
671
        """See BzrBranchFormat.get_format_string()."""
 
672
        return BZR_BRANCH_FORMAT_6
 
673
 
 
674
 
 
675
BzrBranchFormat.register_format(BzrBranchFormat4())
 
676
BzrBranchFormat.register_format(BzrBranchFormat5())
 
677
BzrBranchFormat.register_format(BzrBranchFormat6())
 
678
 
 
679
# TODO: jam 20060108 Create a new branch format, and as part of upgrade
 
680
#       make sure that ancestry.weave is deleted (it is never used, but
 
681
#       used to be created)
 
682
 
444
683
 
445
684
class BzrBranch(Branch):
446
685
    """A branch stored in the actual filesystem.
463
702
 
464
703
    def push_stores(self, branch_to):
465
704
        """See Branch.push_stores."""
466
 
        if (self._branch_format != branch_to._branch_format
467
 
            or self._branch_format != 4):
 
705
        if (not isinstance(self._branch_format, BzrBranchFormat4) or
 
706
            self._branch_format != branch_to._branch_format):
468
707
            from bzrlib.fetch import greedy_fetch
469
 
            mutter("falling back to fetch logic to push between %s(%s) and %s(%s)",
 
708
            mutter("Using fetch logic to push between %s(%s) and %s(%s)",
470
709
                   self, self._branch_format, branch_to, branch_to._branch_format)
471
710
            greedy_fetch(to_branch=branch_to, from_branch=self,
472
711
                         revision=self.last_revision())
473
712
            return
474
713
 
 
714
        # format 4 to format 4 logic only.
475
715
        store_pairs = ((self.text_store,      branch_to.text_store),
476
716
                       (self.inventory_store, branch_to.inventory_store),
477
717
                       (self.revision_store,  branch_to.revision_store))
481
721
        except UnlistableStore:
482
722
            raise UnlistableBranch(from_store)
483
723
 
484
 
    def __init__(self, transport, init=False,
485
 
                 relax_version_check=False):
 
724
    def __init__(self, transport, init=DEPRECATED_PARAMETER,
 
725
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
 
726
                 _control_files=None):
486
727
        """Create new branch object at a particular location.
487
728
 
488
729
        transport -- A Transport object, defining how to access files.
501
742
        """
502
743
        assert isinstance(transport, Transport), \
503
744
            "%r is not a Transport" % transport
504
 
        # TODO: jam 20060103 We create a clone of this transport at .bzr/
505
 
        #       and then we forget about it, should we keep a handle to it?
506
 
        self._base = transport.base
507
 
        self.control_files = LockableFiles(transport.clone(bzrlib.BZRDIR),
 
745
        self._transport = transport
 
746
        self._base = self._transport.base
 
747
        if _control_files is None:
 
748
            _control_files = LockableFiles(self._transport.clone(bzrlib.BZRDIR),
508
749
                                           'branch-lock')
509
 
        if init:
510
 
            self._make_control()
511
 
        self._check_format(relax_version_check)
 
750
        self.control_files = _control_files
 
751
        if deprecated_passed(init):
 
752
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
 
753
                 "deprecated as of bzr 0.8. Please use Branch.create().",
 
754
                 DeprecationWarning,
 
755
                 stacklevel=2)
 
756
            if init:
 
757
                # this is slower than before deprecation, oh well never mind.
 
758
                # -> its deprecated.
 
759
                self._initialize(transport.base)
 
760
        self._check_format(_format)
 
761
        if deprecated_passed(relax_version_check):
 
762
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
 
763
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
 
764
                 "Please use Branch.open_downlevel, or a BzrBranchFormat's "
 
765
                 "open() method.",
 
766
                 DeprecationWarning,
 
767
                 stacklevel=2)
 
768
            if (not relax_version_check
 
769
                and not self._branch_format.is_supported()):
 
770
                raise errors.UnsupportedFormatError(
 
771
                        'sorry, branch format %r not supported' % fmt,
 
772
                        ['use a different bzr version',
 
773
                         'or remove the .bzr directory'
 
774
                         ' and "bzr init" again'])
512
775
        self.repository = Repository(transport, self._branch_format)
513
776
 
 
777
 
 
778
    @staticmethod
 
779
    def _initialize(base):
 
780
        """Create a bzr branch in the latest format."""
 
781
        return BzrBranchFormat6().initialize(base)
 
782
 
514
783
    def __str__(self):
515
784
        return '%s(%r)' % (self.__class__.__name__, self.base)
516
785
 
563
832
        """See Branch.abspath."""
564
833
        return self.control_files._transport.abspath(name)
565
834
 
566
 
    def _make_control(self):
567
 
        from bzrlib.inventory import Inventory
568
 
        from bzrlib.weavefile import write_weave_v5
569
 
        from bzrlib.weave import Weave
570
 
        
571
 
        # Create an empty inventory
572
 
        sio = StringIO()
573
 
        # if we want per-tree root ids then this is the place to set
574
 
        # them; they're not needed for now and so ommitted for
575
 
        # simplicity.
576
 
        bzrlib.xml5.serializer_v5.write_inventory(Inventory(), sio)
577
 
        empty_inv = sio.getvalue()
578
 
        sio = StringIO()
579
 
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
580
 
        empty_weave = sio.getvalue()
581
 
 
582
 
        dirs = ['', 'revision-store', 'weaves']
583
 
        files = [('README', 
584
 
            "This is a Bazaar-NG control directory.\n"
585
 
            "Do not change any files in this directory.\n"),
586
 
            ('branch-format', BZR_BRANCH_FORMAT_6),
587
 
            ('revision-history', ''),
588
 
            ('branch-name', ''),
589
 
            ('branch-lock', ''),
590
 
            ('pending-merges', ''),
591
 
            ('inventory', empty_inv),
592
 
            ('inventory.weave', empty_weave),
593
 
        ]
594
 
        cfe = self.control_files._escape
595
 
        # FIXME: RBC 20060125 dont peek under the covers
596
 
        self.control_files._transport.mkdir_multi([cfe(d) for d in dirs],
597
 
                mode=self.control_files._dir_mode)
598
 
        self.control_files.lock_write()
599
 
        try:
600
 
            for file, content in files:
601
 
                self.control_files.put_utf8(file, content)
602
 
            mutter('created control directory in ' + self.base)
603
 
        finally:
604
 
            self.control_files.unlock()
605
 
 
606
 
    def _check_format(self, relax_version_check):
607
 
        """Check this branch format is supported.
608
 
 
609
 
        The format level is stored, as an integer, in
 
835
    def _check_format(self, format):
 
836
        """Identify the branch format if needed.
 
837
 
 
838
        The format is stored as a reference to the format object in
610
839
        self._branch_format for code that needs to check it later.
611
840
 
612
 
        In the future, we might need different in-memory Branch
613
 
        classes to support downlevel branches.  But not yet.
 
841
        The format parameter is either None or the branch format class
 
842
        used to open this branch.
614
843
        """
615
 
        try:
616
 
            fmt = self.control_files.get_utf8('branch-format').read()
617
 
        except NoSuchFile:
618
 
            raise NotBranchError(path=self.base)
619
 
        mutter("got branch format %r", fmt)
620
 
        if fmt == BZR_BRANCH_FORMAT_6:
621
 
            self._branch_format = 6
622
 
        elif fmt == BZR_BRANCH_FORMAT_5:
623
 
            self._branch_format = 5
624
 
        elif fmt == BZR_BRANCH_FORMAT_4:
625
 
            self._branch_format = 4
626
 
 
627
 
        if (not relax_version_check
628
 
            and self._branch_format not in (5, 6)):
629
 
            raise errors.UnsupportedFormatError(
630
 
                           'sorry, branch format %r not supported' % fmt,
631
 
                           ['use a different bzr version',
632
 
                            'or remove the .bzr directory'
633
 
                            ' and "bzr init" again'])
 
844
        if format is None:
 
845
            format = BzrBranchFormat.find_format(self._transport)
 
846
        self._branch_format = format
 
847
        mutter("got branch format %s", self._branch_format)
634
848
 
635
849
    @needs_read_lock
636
850
    def get_root_id(self):
637
851
        """See Branch.get_root_id."""
638
 
        inv = self.repository.get_inventory(self.last_revision())
639
 
        return inv.root.file_id
 
852
        tree = self.repository.revision_tree(self.last_revision())
 
853
        return tree.inventory.root.file_id
640
854
 
641
855
    def lock_write(self):
642
856
        # TODO: test for failed two phase locks. This is known broken.
676
890
    @needs_write_lock
677
891
    def set_revision_history(self, rev_history):
678
892
        """See Branch.set_revision_history."""
679
 
        old_revision = self.last_revision()
680
 
        new_revision = rev_history[-1]
681
893
        self.control_files.put_utf8(
682
894
            'revision-history', '\n'.join(rev_history))
683
 
        try:
684
 
            # FIXME: RBC 20051207 this smells wrong, last_revision in the 
685
 
            # working tree may be != to last_revision in the branch - so
686
 
            # why is this passing in the branches last_revision ?
687
 
            self.working_tree().set_last_revision(new_revision, old_revision)
688
 
        except NoWorkingTree:
689
 
            mutter('Unable to set_last_revision without a working tree.')
690
895
 
691
896
    def get_revision_delta(self, revno):
692
897
        """Return the delta for one revision.
775
980
    def working_tree(self):
776
981
        """See Branch.working_tree."""
777
982
        from bzrlib.workingtree import WorkingTree
778
 
        if self.base.find('://') != -1:
 
983
        from bzrlib.transport.local import LocalTransport
 
984
        if (self.base.find('://') != -1 or 
 
985
            not isinstance(self._transport, LocalTransport)):
779
986
            raise NoWorkingTree(self.base)
780
987
        return WorkingTree(self.base, branch=self)
781
988
 
844
1051
 
845
1052
    @needs_read_lock
846
1053
    def _clone_weave(self, to_location, revision=None, basis_branch=None):
 
1054
        # prevent leakage
 
1055
        from bzrlib.workingtree import WorkingTree
847
1056
        assert isinstance(to_location, basestring)
848
1057
        if basis_branch is not None:
849
1058
            note("basis_branch is not supported for fast weave copy yet.")
853
1062
            os.mkdir(to_location)
854
1063
        branch_to = Branch.initialize(to_location)
855
1064
        mutter("copy branch from %s to %s", self, branch_to)
856
 
        branch_to.working_tree().set_root_id(self.get_root_id())
857
1065
 
858
1066
        self.repository.copy(branch_to.repository)
859
1067
        
860
1068
        # must be done *after* history is copied across
861
1069
        # FIXME duplicate code with base .clone().
862
 
        # .. would template method be useful here.  RBC 20051207
 
1070
        # .. would template method be useful here?  RBC 20051207
863
1071
        branch_to.set_parent(self.base)
864
1072
        branch_to.append_revision(*history)
865
 
        # circular import protection
866
 
        from bzrlib.merge import build_working_dir
867
 
        build_working_dir(to_location)
 
1073
        # FIXME: this should be in workingtree.clone
 
1074
        WorkingTree.create(branch_to, to_location).set_root_id(self.get_root_id())
868
1075
        mutter("copied")
869
1076
        return branch_to
870
1077
 
871
1078
    def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
 
1079
        print "FIXME: clone via create and fetch is probably faster when versioned file comes in."
872
1080
        if to_branch_type is None:
873
1081
            to_branch_type = BzrBranch
874
1082
 
940
1148
        to have a single line per file/directory, and to have
941
1149
        fileid="" and revision="" on that line.
942
1150
        """
943
 
        assert self._branch_format in (5, 6), \
 
1151
        assert (isinstance(self._branch_format, BzrBranchFormat5) or
 
1152
                isinstance(self._branch_format, BzrBranchFormat6)), \
944
1153
            "fileid_involved only supported for branches which store inventory as xml"
945
1154
 
946
1155
        w = self.repository.get_inventory_weave()
971
1180
        return file_ids
972
1181
 
973
1182
 
 
1183
Branch.set_default_initializer(BzrBranch._initialize)
 
1184
 
 
1185
 
 
1186
class BranchTestProviderAdapter(object):
 
1187
    """A tool to generate a suite testing multiple branch formats at once.
 
1188
 
 
1189
    This is done by copying the test once for each transport and injecting
 
1190
    the transport_server, transport_readonly_server, and branch_format
 
1191
    classes into each copy. Each copy is also given a new id() to make it
 
1192
    easy to identify.
 
1193
    """
 
1194
 
 
1195
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1196
        self._transport_server = transport_server
 
1197
        self._transport_readonly_server = transport_readonly_server
 
1198
        self._formats = formats
 
1199
    
 
1200
    def adapt(self, test):
 
1201
        result = TestSuite()
 
1202
        for format in self._formats:
 
1203
            new_test = deepcopy(test)
 
1204
            new_test.transport_server = self._transport_server
 
1205
            new_test.transport_readonly_server = self._transport_readonly_server
 
1206
            new_test.branch_format = format
 
1207
            def make_new_test_id():
 
1208
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
 
1209
                return lambda: new_id
 
1210
            new_test.id = make_new_test_id()
 
1211
            result.addTest(new_test)
 
1212
        return result
 
1213
 
 
1214
 
974
1215
class ScratchBranch(BzrBranch):
975
1216
    """Special test class: a branch that cleans up after itself.
976
1217
 
992
1233
        """
993
1234
        if transport is None:
994
1235
            transport = bzrlib.transport.local.ScratchTransport()
995
 
            super(ScratchBranch, self).__init__(transport, init=True)
 
1236
            # local import for scope restriction
 
1237
            from bzrlib.workingtree import WorkingTree
 
1238
            WorkingTree.create_standalone(transport.base)
 
1239
            super(ScratchBranch, self).__init__(transport)
996
1240
        else:
997
1241
            super(ScratchBranch, self).__init__(transport)
998
1242