~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2006-02-01 12:24:35 UTC
  • mfrom: (1534.4.32 branch-formats)
  • mto: This revision was merged to the branch mainline in revision 1553.
  • Revision ID: mbp@sourcefrog.net-20060201122435-53f3efb1b5749fe1
[merge] branch-formats branch, and reconcile changes

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
        # imported here to prevent scope creep as this is going.
 
168
        from bzrlib.workingtree import WorkingTree
 
169
        return WorkingTree.create_standalone(safe_unicode(base)).branch
 
170
 
 
171
    @staticmethod
 
172
    def get_default_initializer():
 
173
        """Return the initializer being used for new branches."""
 
174
        return Branch._default_initializer
 
175
 
 
176
    @staticmethod
 
177
    def set_default_initializer(initializer):
 
178
        """Set the initializer to be used for new branches."""
 
179
        Branch._default_initializer = staticmethod(initializer)
130
180
 
131
181
    def setup_caching(self, cache_root):
132
182
        """Subclasses that care about caching should override this, and set
212
262
        If self and other have not diverged, return a list of the revisions
213
263
        present in other, but missing from self.
214
264
 
215
 
        >>> from bzrlib.commit import commit
216
265
        >>> bzrlib.trace.silent = True
217
266
        >>> br1 = ScratchBranch()
218
267
        >>> br2 = ScratchBranch()
219
268
        >>> br1.missing_revisions(br2)
220
269
        []
221
 
        >>> commit(br2, "lala!", rev_id="REVISION-ID-1")
 
270
        >>> br2.working_tree().commit("lala!", rev_id="REVISION-ID-1")
222
271
        >>> br1.missing_revisions(br2)
223
272
        [u'REVISION-ID-1']
224
273
        >>> br2.missing_revisions(br1)
225
274
        []
226
 
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1")
 
275
        >>> br1.working_tree().commit("lala!", rev_id="REVISION-ID-1")
227
276
        >>> br1.missing_revisions(br2)
228
277
        []
229
 
        >>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
 
278
        >>> br2.working_tree().commit("lala!", rev_id="REVISION-ID-2A")
230
279
        >>> br1.missing_revisions(br2)
231
280
        [u'REVISION-ID-2A']
232
 
        >>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
 
281
        >>> br1.working_tree().commit("lala!", rev_id="REVISION-ID-2B")
233
282
        >>> br1.missing_revisions(br2)
234
283
        Traceback (most recent call last):
235
284
        DivergedBranches: These branches have diverged.  Try merge.
351
400
        if revno < 1 or revno > self.revno():
352
401
            raise InvalidRevisionNumber(revno)
353
402
        
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
403
    def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
361
404
        """Copy this branch into the existing directory to_location.
362
405
 
378
421
        to_branch_type
379
422
            Branch type of destination branch
380
423
        """
381
 
        # circular import protection
382
 
        from bzrlib.merge import build_working_dir
383
 
 
 
424
        from bzrlib.workingtree import WorkingTree
384
425
        assert isinstance(to_location, basestring)
385
426
        if not bzrlib.osutils.lexists(to_location):
386
427
            os.mkdir(to_location)
387
428
        if to_branch_type is None:
388
429
            to_branch_type = BzrBranch
 
430
        print "FIXME use a branch format here"
389
431
        br_to = to_branch_type.initialize(to_location)
390
432
        mutter("copy branch from %s to %s", self, br_to)
391
433
        if basis_branch is not None:
392
434
            basis_branch.push_stores(br_to)
393
 
        br_to.working_tree().set_root_id(self.get_root_id())
394
435
        if revision is None:
395
436
            revision = self.last_revision()
396
437
        br_to.update_revisions(self, stop_revision=revision)
397
438
        br_to.set_parent(self.base)
398
 
        build_working_dir(to_location)
 
439
        WorkingTree.create(br_to, to_location).set_root_id(self.get_root_id())
399
440
        mutter("copied")
400
441
        return br_to
401
442
 
441
482
        """
442
483
        raise NotImplementedError('fileid_involved_by_set is abstract')
443
484
 
 
485
class BzrBranchFormat(object):
 
486
    """An encapsulation of the initialization and open routines for a format.
 
487
 
 
488
    Formats provide three things:
 
489
     * An initialization routine,
 
490
     * a format string,
 
491
     * an open routine.
 
492
 
 
493
    Formats are placed in an dict by their format string for reference 
 
494
    during branch opening. Its not required that these be instances, they
 
495
    can be classes themselves with class methods - it simply depends on 
 
496
    whether state is needed for a given format or not.
 
497
 
 
498
    Once a format is deprecated, just deprecate the initialize and open
 
499
    methods on the format class. Do not deprecate the object, as the 
 
500
    object will be created every time regardless.
 
501
    """
 
502
 
 
503
    _formats = {}
 
504
    """The known formats."""
 
505
 
 
506
    @classmethod
 
507
    def find_format(klass, transport):
 
508
        """Return the format registered for URL."""
 
509
        try:
 
510
            format_string = transport.get(".bzr/branch-format").read()
 
511
            return klass._formats[format_string]
 
512
        except NoSuchFile:
 
513
            raise NotBranchError(path=transport.base)
 
514
        except KeyError:
 
515
            raise errors.UnknownFormatError(format_string)
 
516
 
 
517
    def get_format_string(self):
 
518
        """Return the ASCII format string that identifies this format."""
 
519
        raise NotImplementedError(self.get_format_string)
 
520
 
 
521
    def _find_modes(self, t):
 
522
        """Determine the appropriate modes for files and directories.
 
523
        
 
524
        FIXME: When this merges into, or from storage,
 
525
        this code becomes delgatable to a LockableFiles instance.
 
526
 
 
527
        For now its cribbed and returns (dir_mode, file_mode)
 
528
        """
 
529
        try:
 
530
            st = t.stat('.')
 
531
        except errors.TransportNotPossible:
 
532
            dir_mode = 0755
 
533
            file_mode = 0644
 
534
        else:
 
535
            dir_mode = st.st_mode & 07777
 
536
            # Remove the sticky and execute bits for files
 
537
            file_mode = dir_mode & ~07111
 
538
        if not BzrBranch._set_dir_mode:
 
539
            dir_mode = None
 
540
        if not BzrBranch._set_file_mode:
 
541
            file_mode = None
 
542
        return dir_mode, file_mode
 
543
 
 
544
    def initialize(self, url):
 
545
        """Create a branch of this format at url and return an open branch."""
 
546
        t = get_transport(url)
 
547
        from bzrlib.weavefile import write_weave_v5
 
548
        from bzrlib.weave import Weave
 
549
        
 
550
        # Create an empty weave
 
551
        sio = StringIO()
 
552
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
 
553
        empty_weave = sio.getvalue()
 
554
 
 
555
        # Since we don't have a .bzr directory, inherit the
 
556
        # mode from the root directory
 
557
        temp_control = LockableFiles(t, '')
 
558
        temp_control._transport.mkdir('.bzr',
 
559
                                      mode=temp_control._dir_mode)
 
560
        file_mode = temp_control._file_mode
 
561
        del temp_control
 
562
        mutter('created control directory in ' + t.base)
 
563
        control = t.clone('.bzr')
 
564
        dirs = ['revision-store', 'weaves']
 
565
        lock_file = 'branch-lock'
 
566
        utf8_files = [('README', 
 
567
                       "This is a Bazaar-NG control directory.\n"
 
568
                       "Do not change any files in this directory.\n"),
 
569
                      ('branch-format', self.get_format_string()),
 
570
                      ('revision-history', ''),
 
571
                      ('branch-name', ''),
 
572
                      ]
 
573
        files = [('inventory.weave', StringIO(empty_weave)), 
 
574
                 ]
 
575
        
 
576
        # FIXME: RBC 20060125 dont peek under the covers
 
577
        # NB: no need to escape relative paths that are url safe.
 
578
        control.put(lock_file, StringIO(), mode=file_mode)
 
579
        control_files = LockableFiles(control, lock_file)
 
580
        control_files.lock_write()
 
581
        control_files._transport.mkdir_multi(dirs,
 
582
                mode=control_files._dir_mode)
 
583
        try:
 
584
            for file, content in utf8_files:
 
585
                control_files.put_utf8(file, content)
 
586
            for file, content in files:
 
587
                control_files.put(file, content)
 
588
        finally:
 
589
            control_files.unlock()
 
590
        return BzrBranch(t, _format=self, _control_files=control_files)
 
591
 
 
592
    def is_supported(self):
 
593
        """Is this format supported?
 
594
 
 
595
        Supported formats can be initialized and opened.
 
596
        Unsupported formats may not support initialization or committing or 
 
597
        some other features depending on the reason for not being supported.
 
598
        """
 
599
        return True
 
600
 
 
601
    def open(self, transport):
 
602
        """Fill out the data in branch for the branch at url."""
 
603
        return BzrBranch(transport, _format=self)
 
604
 
 
605
    @classmethod
 
606
    def register_format(klass, format):
 
607
        klass._formats[format.get_format_string()] = format
 
608
 
 
609
    @classmethod
 
610
    def unregister_format(klass, format):
 
611
        assert klass._formats[format.get_format_string()] is format
 
612
        del klass._formats[format.get_format_string()]
 
613
 
 
614
 
 
615
class BzrBranchFormat4(BzrBranchFormat):
 
616
    """Bzr branch format 4.
 
617
 
 
618
    This format has:
 
619
     - flat stores
 
620
     - TextStores for texts, inventories,revisions.
 
621
 
 
622
    This format is deprecated: it indexes texts using a text it which is
 
623
    removed in format 5; write support for this format has been removed.
 
624
    """
 
625
 
 
626
    def get_format_string(self):
 
627
        """See BzrBranchFormat.get_format_string()."""
 
628
        return BZR_BRANCH_FORMAT_4
 
629
 
 
630
    def initialize(self, url):
 
631
        """Format 4 branches cannot be created."""
 
632
        raise UninitializableFormat(self)
 
633
 
 
634
    def is_supported(self):
 
635
        """Format 4 is not supported.
 
636
 
 
637
        It is not supported because the model changed from 4 to 5 and the
 
638
        conversion logic is expensive - so doing it on the fly was not 
 
639
        feasible.
 
640
        """
 
641
        return False
 
642
 
 
643
 
 
644
class BzrBranchFormat5(BzrBranchFormat):
 
645
    """Bzr branch format 5.
 
646
 
 
647
    This format has:
 
648
     - weaves for file texts and inventory
 
649
     - flat stores
 
650
     - TextStores for revisions and signatures.
 
651
    """
 
652
 
 
653
    def get_format_string(self):
 
654
        """See BzrBranchFormat.get_format_string()."""
 
655
        return BZR_BRANCH_FORMAT_5
 
656
 
 
657
 
 
658
class BzrBranchFormat6(BzrBranchFormat):
 
659
    """Bzr branch format 6.
 
660
 
 
661
    This format has:
 
662
     - weaves for file texts and inventory
 
663
     - hash subdirectory based stores.
 
664
     - TextStores for revisions and signatures.
 
665
    """
 
666
 
 
667
    def get_format_string(self):
 
668
        """See BzrBranchFormat.get_format_string()."""
 
669
        return BZR_BRANCH_FORMAT_6
 
670
 
 
671
 
 
672
BzrBranchFormat.register_format(BzrBranchFormat4())
 
673
BzrBranchFormat.register_format(BzrBranchFormat5())
 
674
BzrBranchFormat.register_format(BzrBranchFormat6())
 
675
 
 
676
# TODO: jam 20060108 Create a new branch format, and as part of upgrade
 
677
#       make sure that ancestry.weave is deleted (it is never used, but
 
678
#       used to be created)
 
679
 
444
680
 
445
681
class BzrBranch(Branch):
446
682
    """A branch stored in the actual filesystem.
463
699
 
464
700
    def push_stores(self, branch_to):
465
701
        """See Branch.push_stores."""
466
 
        if (self._branch_format != branch_to._branch_format
467
 
            or self._branch_format != 4):
 
702
        if (not isinstance(self._branch_format, BzrBranchFormat4) or
 
703
            self._branch_format != branch_to._branch_format):
468
704
            from bzrlib.fetch import greedy_fetch
469
 
            mutter("falling back to fetch logic to push between %s(%s) and %s(%s)",
 
705
            mutter("Using fetch logic to push between %s(%s) and %s(%s)",
470
706
                   self, self._branch_format, branch_to, branch_to._branch_format)
471
707
            greedy_fetch(to_branch=branch_to, from_branch=self,
472
708
                         revision=self.last_revision())
473
709
            return
474
710
 
 
711
        # format 4 to format 4 logic only.
475
712
        store_pairs = ((self.text_store,      branch_to.text_store),
476
713
                       (self.inventory_store, branch_to.inventory_store),
477
714
                       (self.revision_store,  branch_to.revision_store))
481
718
        except UnlistableStore:
482
719
            raise UnlistableBranch(from_store)
483
720
 
484
 
    def __init__(self, transport, init=False,
485
 
                 relax_version_check=False):
 
721
    def __init__(self, transport, init=DEPRECATED_PARAMETER,
 
722
                 relax_version_check=DEPRECATED_PARAMETER, _format=None,
 
723
                 _control_files=None):
486
724
        """Create new branch object at a particular location.
487
725
 
488
726
        transport -- A Transport object, defining how to access files.
501
739
        """
502
740
        assert isinstance(transport, Transport), \
503
741
            "%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),
 
742
        self._transport = transport
 
743
        self._base = self._transport.base
 
744
        if _control_files is None:
 
745
            _control_files = LockableFiles(self._transport.clone(bzrlib.BZRDIR),
508
746
                                           'branch-lock')
509
 
        if init:
510
 
            self._make_control()
511
 
        self._check_format(relax_version_check)
 
747
        self.control_files = _control_files
 
748
        if deprecated_passed(init):
 
749
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
 
750
                 "deprecated as of bzr 0.8. Please use Branch.create().",
 
751
                 DeprecationWarning,
 
752
                 stacklevel=2)
 
753
            if init:
 
754
                # this is slower than before deprecation, oh well never mind.
 
755
                # -> its deprecated.
 
756
                self._initialize(transport.base)
 
757
        self._check_format(_format)
 
758
        if deprecated_passed(relax_version_check):
 
759
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
 
760
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
 
761
                 "Please use Branch.open_downlevel, or a BzrBranchFormat's "
 
762
                 "open() method.",
 
763
                 DeprecationWarning,
 
764
                 stacklevel=2)
 
765
            if (not relax_version_check
 
766
                and not self._branch_format.is_supported()):
 
767
                raise errors.UnsupportedFormatError(
 
768
                        'sorry, branch format %r not supported' % fmt,
 
769
                        ['use a different bzr version',
 
770
                         'or remove the .bzr directory'
 
771
                         ' and "bzr init" again'])
512
772
        self.repository = Repository(transport, self._branch_format)
513
773
 
 
774
 
 
775
    @staticmethod
 
776
    def _initialize(base):
 
777
        """Create a bzr branch in the latest format."""
 
778
        return BzrBranchFormat6().initialize(base)
 
779
 
514
780
    def __str__(self):
515
781
        return '%s(%r)' % (self.__class__.__name__, self.base)
516
782
 
563
829
        """See Branch.abspath."""
564
830
        return self.control_files._transport.abspath(name)
565
831
 
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
 
832
    def _check_format(self, format):
 
833
        """Identify the branch format if needed.
 
834
 
 
835
        The format is stored as a reference to the format object in
610
836
        self._branch_format for code that needs to check it later.
611
837
 
612
 
        In the future, we might need different in-memory Branch
613
 
        classes to support downlevel branches.  But not yet.
 
838
        The format parameter is either None or the branch format class
 
839
        used to open this branch.
614
840
        """
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'])
 
841
        if format is None:
 
842
            format = BzrBranchFormat.find_format(self._transport)
 
843
        self._branch_format = format
 
844
        mutter("got branch format %s", self._branch_format)
634
845
 
635
846
    @needs_read_lock
636
847
    def get_root_id(self):
637
848
        """See Branch.get_root_id."""
638
 
        inv = self.repository.get_inventory(self.last_revision())
639
 
        return inv.root.file_id
 
849
        tree = self.repository.revision_tree(self.last_revision())
 
850
        return tree.inventory.root.file_id
640
851
 
641
852
    def lock_write(self):
642
853
        # TODO: test for failed two phase locks. This is known broken.
676
887
    @needs_write_lock
677
888
    def set_revision_history(self, rev_history):
678
889
        """See Branch.set_revision_history."""
679
 
        old_revision = self.last_revision()
680
 
        new_revision = rev_history[-1]
681
890
        self.control_files.put_utf8(
682
891
            '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
892
 
691
893
    def get_revision_delta(self, revno):
692
894
        """Return the delta for one revision.
775
977
    def working_tree(self):
776
978
        """See Branch.working_tree."""
777
979
        from bzrlib.workingtree import WorkingTree
778
 
        if self.base.find('://') != -1:
 
980
        from bzrlib.transport.local import LocalTransport
 
981
        if (self.base.find('://') != -1 or 
 
982
            not isinstance(self._transport, LocalTransport)):
779
983
            raise NoWorkingTree(self.base)
780
984
        return WorkingTree(self.base, branch=self)
781
985
 
844
1048
 
845
1049
    @needs_read_lock
846
1050
    def _clone_weave(self, to_location, revision=None, basis_branch=None):
 
1051
        # prevent leakage
 
1052
        from bzrlib.workingtree import WorkingTree
847
1053
        assert isinstance(to_location, basestring)
848
1054
        if basis_branch is not None:
849
1055
            note("basis_branch is not supported for fast weave copy yet.")
853
1059
            os.mkdir(to_location)
854
1060
        branch_to = Branch.initialize(to_location)
855
1061
        mutter("copy branch from %s to %s", self, branch_to)
856
 
        branch_to.working_tree().set_root_id(self.get_root_id())
857
1062
 
858
1063
        self.repository.copy(branch_to.repository)
859
1064
        
860
1065
        # must be done *after* history is copied across
861
1066
        # FIXME duplicate code with base .clone().
862
 
        # .. would template method be useful here.  RBC 20051207
 
1067
        # .. would template method be useful here?  RBC 20051207
863
1068
        branch_to.set_parent(self.base)
864
1069
        branch_to.append_revision(*history)
865
 
        # circular import protection
866
 
        from bzrlib.merge import build_working_dir
867
 
        build_working_dir(to_location)
 
1070
        # FIXME: this should be in workingtree.clone
 
1071
        WorkingTree.create(branch_to, to_location).set_root_id(self.get_root_id())
868
1072
        mutter("copied")
869
1073
        return branch_to
870
1074
 
871
1075
    def clone(self, to_location, revision=None, basis_branch=None, to_branch_type=None):
 
1076
        print "FIXME: clone via create and fetch is probably faster when versioned file comes in."
872
1077
        if to_branch_type is None:
873
1078
            to_branch_type = BzrBranch
874
1079
 
940
1145
        to have a single line per file/directory, and to have
941
1146
        fileid="" and revision="" on that line.
942
1147
        """
943
 
        assert self._branch_format in (5, 6), \
 
1148
        assert (isinstance(self._branch_format, BzrBranchFormat5) or
 
1149
                isinstance(self._branch_format, BzrBranchFormat6)), \
944
1150
            "fileid_involved only supported for branches which store inventory as xml"
945
1151
 
946
1152
        w = self.repository.get_inventory_weave()
971
1177
        return file_ids
972
1178
 
973
1179
 
 
1180
Branch.set_default_initializer(BzrBranch._initialize)
 
1181
 
 
1182
 
 
1183
class BranchTestProviderAdapter(object):
 
1184
    """A tool to generate a suite testing multiple branch formats at once.
 
1185
 
 
1186
    This is done by copying the test once for each transport and injecting
 
1187
    the transport_server, transport_readonly_server, and branch_format
 
1188
    classes into each copy. Each copy is also given a new id() to make it
 
1189
    easy to identify.
 
1190
    """
 
1191
 
 
1192
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1193
        self._transport_server = transport_server
 
1194
        self._transport_readonly_server = transport_readonly_server
 
1195
        self._formats = formats
 
1196
    
 
1197
    def adapt(self, test):
 
1198
        result = TestSuite()
 
1199
        for format in self._formats:
 
1200
            new_test = deepcopy(test)
 
1201
            new_test.transport_server = self._transport_server
 
1202
            new_test.transport_readonly_server = self._transport_readonly_server
 
1203
            new_test.branch_format = format
 
1204
            def make_new_test_id():
 
1205
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
 
1206
                return lambda: new_id
 
1207
            new_test.id = make_new_test_id()
 
1208
            result.addTest(new_test)
 
1209
        return result
 
1210
 
 
1211
 
974
1212
class ScratchBranch(BzrBranch):
975
1213
    """Special test class: a branch that cleans up after itself.
976
1214
 
992
1230
        """
993
1231
        if transport is None:
994
1232
            transport = bzrlib.transport.local.ScratchTransport()
995
 
            super(ScratchBranch, self).__init__(transport, init=True)
 
1233
            # local import for scope restriction
 
1234
            from bzrlib.workingtree import WorkingTree
 
1235
            WorkingTree.create_standalone(transport.base)
 
1236
            super(ScratchBranch, self).__init__(transport)
996
1237
        else:
997
1238
            super(ScratchBranch, self).__init__(transport)
998
1239