~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

merge from integration.

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
 
from cStringIO import StringIO
24
26
 
25
27
 
26
28
import bzrlib
27
 
import bzrlib.inventory as inventory
28
 
from bzrlib.trace import mutter, note
29
 
from bzrlib.osutils import (isdir, quotefn,
30
 
                            rename, splitpath, sha_file,
31
 
                            file_kind, abspath, normpath, pathjoin)
 
29
from bzrlib.config import TreeConfig
 
30
from bzrlib.delta import compare_trees
32
31
import bzrlib.errors as errors
33
32
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
34
33
                           NoSuchRevision, HistoryMissing, NotBranchError,
35
 
                           DivergedBranches, LockError, UnlistableStore,
 
34
                           DivergedBranches, LockError, 
 
35
                           UninitializableFormat,
 
36
                           UnlistableStore,
36
37
                           UnlistableBranch, NoSuchFile, NotVersionedError,
37
38
                           NoWorkingTree)
 
39
import bzrlib.inventory as inventory
 
40
from bzrlib.inventory import Inventory
 
41
from bzrlib.osutils import (isdir, quotefn,
 
42
                            rename, splitpath, sha_file,
 
43
                            file_kind, abspath, normpath, pathjoin,
 
44
                            safe_unicode,
 
45
                            )
38
46
from bzrlib.textui import show_status
 
47
from bzrlib.trace import mutter, note
 
48
from bzrlib.tree import EmptyTree, RevisionTree
39
49
from bzrlib.revision import (Revision, is_ancestor, get_intervening_revisions,
40
50
                             NULL_REVISION)
41
 
 
42
 
from bzrlib.delta import compare_trees
43
 
from bzrlib.tree import EmptyTree, RevisionTree
44
 
from bzrlib.inventory import Inventory
45
51
from bzrlib.store import copy_all
46
52
from bzrlib.store.text import TextStore
47
53
from bzrlib.store.weave import WeaveStore
 
54
from bzrlib.symbol_versioning import deprecated_nonce, deprecated_passed
48
55
from bzrlib.testament import Testament
49
56
import bzrlib.transactions as transactions
50
57
from bzrlib.transport import Transport, get_transport
51
58
import bzrlib.xml5
52
59
import bzrlib.ui
53
 
from config import TreeConfig
54
60
 
55
61
 
56
62
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
100
106
    base
101
107
        Base directory/url of the branch.
102
108
    """
 
109
    # this is really an instance variable - FIXME move it there
 
110
    # - RBC 20060112
103
111
    base = None
104
112
 
 
113
    _default_initializer = None
 
114
    """The default initializer for making new branches."""
 
115
 
105
116
    def __init__(self, *ignored, **ignored_too):
106
117
        raise NotImplementedError('The Branch class is abstract')
107
118
 
108
119
    @staticmethod
109
120
    def open_downlevel(base):
110
 
        """Open a branch which may be of an old format.
111
 
        
112
 
        Only local branches are supported."""
113
 
        return BzrBranch(get_transport(base), relax_version_check=True)
 
121
        """Open a branch which may be of an old format."""
 
122
        return Branch.open(base, _unsupported=True)
114
123
        
115
124
    @staticmethod
116
 
    def open(base):
117
 
        """Open an existing branch, rooted at 'base' (url)"""
 
125
    def open(base, _unsupported=False):
 
126
        """Open an existing branch, rooted at 'base' (url)
 
127
        
 
128
        _unsupported is a private parameter to the Branch class.
 
129
        """
118
130
        t = get_transport(base)
119
131
        mutter("trying to open %r with transport %r", base, t)
120
 
        return BzrBranch(t)
 
132
        format = BzrBranchFormat.find_format(t)
 
133
        if not _unsupported and not format.is_supported():
 
134
            # see open_downlevel to open legacy branches.
 
135
            raise errors.UnsupportedFormatError(
 
136
                    'sorry, branch format %s not supported' % format,
 
137
                    ['use a different bzr version',
 
138
                     'or remove the .bzr directory'
 
139
                     ' and "bzr init" again'])
 
140
        return format.open(t)
121
141
 
122
142
    @staticmethod
123
143
    def open_containing(url):
130
150
        If there is one, it is returned, along with the unused portion of url.
131
151
        """
132
152
        t = get_transport(url)
 
153
        url = t.base
133
154
        while True:
134
155
            try:
135
 
                return BzrBranch(t), t.relpath(url)
 
156
                format = BzrBranchFormat.find_format(t)
 
157
                return format.open(t), t.relpath(url)
 
158
            # TODO FIXME, distinguish between formats that cannot be
 
159
            # identified, and a lack of format.
136
160
            except NotBranchError, e:
137
161
                mutter('not a branch in: %r %s', t.base, e)
138
162
            new_t = t.clone('..')
143
167
 
144
168
    @staticmethod
145
169
    def initialize(base):
146
 
        """Create a new branch, rooted at 'base' (url)"""
147
 
        t = get_transport(base)
148
 
        return BzrBranch(t, init=True)
 
170
        """Create a new branch, rooted at 'base' (url)
 
171
        
 
172
        This will call the current default initializer with base
 
173
        as the only parameter.
 
174
        """
 
175
        return Branch._default_initializer(safe_unicode(base))
 
176
 
 
177
    @staticmethod
 
178
    def get_default_initializer():
 
179
        """Return the initializer being used for new branches."""
 
180
        return Branch._default_initializer
 
181
 
 
182
    @staticmethod
 
183
    def set_default_initializer(initializer):
 
184
        """Set the initializer to be used for new branches."""
 
185
        Branch._default_initializer = staticmethod(initializer)
149
186
 
150
187
    def setup_caching(self, cache_root):
151
188
        """Subclasses that care about caching should override this, and set
155
192
 
156
193
    def _get_nick(self):
157
194
        cfg = self.tree_config()
158
 
        return cfg.get_option(u"nickname", default=self.base.split('/')[-1])
 
195
        return cfg.get_option(u"nickname", default=self.base.split('/')[-2])
159
196
 
160
197
    def _set_nick(self, nick):
161
198
        cfg = self.tree_config()
487
524
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
488
525
        raise NotImplementedError('store_revision_signature is abstract')
489
526
 
 
527
 
 
528
class BzrBranchFormat(object):
 
529
    """An encapsulation of the initialization and open routines for a format.
 
530
 
 
531
    Formats provide three things:
 
532
     * An initialization routine,
 
533
     * a format string,
 
534
     * an open routine.
 
535
 
 
536
    Formats are placed in an dict by their format string for reference 
 
537
    during branch opening. Its not required that these be instances, they
 
538
    can be classes themselves with class methods - it simply depends on 
 
539
    whether state is needed for a given format or not.
 
540
 
 
541
    Once a format is deprecated, just deprecate the initialize and open
 
542
    methods on the format class. Do not deprecate the object, as the 
 
543
    object will be created every time regardless.
 
544
    """
 
545
 
 
546
    _formats = {}
 
547
    """The known formats."""
 
548
 
 
549
    @classmethod
 
550
    def find_format(klass, transport):
 
551
        """Return the format registered for URL."""
 
552
        try:
 
553
            format_string = transport.get(".bzr/branch-format").read()
 
554
            return klass._formats[format_string]
 
555
        except NoSuchFile:
 
556
            raise NotBranchError(path=transport.base)
 
557
        except KeyError:
 
558
            raise errors.UnknownFormatError(format_string)
 
559
 
 
560
    def get_format_string(self):
 
561
        """Return the ASCII format string that identifies this format."""
 
562
        raise NotImplementedError(self.get_format_string)
 
563
 
 
564
    def _find_modes(self, t):
 
565
        """Determine the appropriate modes for files and directories.
 
566
        
 
567
        FIXME: When this merges into, or from storage,
 
568
        this code becomes delgatable to a LockableFiles instance.
 
569
 
 
570
        For now its cribbed and returns (dir_mode, file_mode)
 
571
        """
 
572
        try:
 
573
            st = t.stat('.')
 
574
        except errors.TransportNotPossible:
 
575
            dir_mode = 0755
 
576
            file_mode = 0644
 
577
        else:
 
578
            dir_mode = st.st_mode & 07777
 
579
            # Remove the sticky and execute bits for files
 
580
            file_mode = dir_mode & ~07111
 
581
        if not BzrBranch._set_dir_mode:
 
582
            dir_mode = None
 
583
        if not BzrBranch._set_file_mode:
 
584
            file_mode = None
 
585
        return dir_mode, file_mode
 
586
 
 
587
    def initialize(self, url):
 
588
        """Create a branch of this format at url and return an open branch."""
 
589
        t = get_transport(url)
 
590
        from bzrlib.inventory import Inventory
 
591
        from bzrlib.weavefile import write_weave_v5
 
592
        from bzrlib.weave import Weave
 
593
        
 
594
        # Create an empty inventory
 
595
        sio = StringIO()
 
596
        # if we want per-tree root ids then this is the place to set
 
597
        # them; they're not needed for now and so ommitted for
 
598
        # simplicity.
 
599
        bzrlib.xml5.serializer_v5.write_inventory(Inventory(), sio)
 
600
        empty_inv = sio.getvalue()
 
601
        sio = StringIO()
 
602
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
 
603
        empty_weave = sio.getvalue()
 
604
 
 
605
        # Since we don't have a .bzr directory, inherit the
 
606
        # mode from the root directory
 
607
        dir_mode, file_mode = self._find_modes(t)
 
608
 
 
609
        t.mkdir('.bzr', mode=dir_mode)
 
610
        control = t.clone('.bzr')
 
611
        dirs = ['revision-store', 'weaves']
 
612
        files = [('README', 
 
613
            StringIO("This is a Bazaar-NG control directory.\n"
 
614
            "Do not change any files in this directory.\n")),
 
615
            ('branch-format', StringIO(self.get_format_string())),
 
616
            ('revision-history', StringIO('')),
 
617
            ('branch-name', StringIO('')),
 
618
            ('branch-lock', StringIO('')),
 
619
            ('pending-merges', StringIO('')),
 
620
            ('inventory', StringIO(empty_inv)),
 
621
            ('inventory.weave', StringIO(empty_weave)),
 
622
            ('ancestry.weave', StringIO(empty_weave))
 
623
        ]
 
624
        control.mkdir_multi(dirs, mode=dir_mode)
 
625
        control.put_multi(files, mode=file_mode)
 
626
        mutter('created control directory in ' + t.base)
 
627
        return BzrBranch(t, format=self)
 
628
 
 
629
    def is_supported(self):
 
630
        """Is this format supported?
 
631
 
 
632
        Supported formats can be initialized and opened.
 
633
        Unsupported formats may not support initialization or committing or 
 
634
        some other features depending on the reason for not being supported.
 
635
        """
 
636
        return True
 
637
 
 
638
    def open(self, transport):
 
639
        """Fill out the data in branch for the branch at url."""
 
640
        return BzrBranch(transport, format=self)
 
641
 
 
642
    @classmethod
 
643
    def register_format(klass, format):
 
644
        klass._formats[format.get_format_string()] = format
 
645
 
 
646
    @classmethod
 
647
    def unregister_format(klass, format):
 
648
        assert klass._formats[format.get_format_string()] is format
 
649
        del klass._formats[format.get_format_string()]
 
650
 
 
651
 
 
652
class BzrBranchFormat4(BzrBranchFormat):
 
653
    """Bzr branch format 4.
 
654
 
 
655
    This format has:
 
656
     - flat stores
 
657
     - TextStores for texts, inventories,revisions.
 
658
 
 
659
    This format is deprecated: it indexes texts using a text it which is
 
660
    removed in format 5; write support for this format has been removed.
 
661
    """
 
662
 
 
663
    def get_format_string(self):
 
664
        """See BzrBranchFormat.get_format_string()."""
 
665
        return BZR_BRANCH_FORMAT_4
 
666
 
 
667
    def initialize(self, url):
 
668
        """Format 4 branches cannot be created."""
 
669
        raise UninitializableFormat(self)
 
670
 
 
671
    def is_supported(self):
 
672
        """Format 4 is not supported.
 
673
 
 
674
        It is not supported because the model changed from 4 to 5 and the
 
675
        conversion logic is expensive - so doing it on the fly was not 
 
676
        feasible.
 
677
        """
 
678
        return False
 
679
 
 
680
 
 
681
class BzrBranchFormat5(BzrBranchFormat):
 
682
    """Bzr branch format 5.
 
683
 
 
684
    This format has:
 
685
     - weaves for file texts and inventory
 
686
     - flat stores
 
687
     - TextStores for revisions and signatures.
 
688
    """
 
689
 
 
690
    def get_format_string(self):
 
691
        """See BzrBranchFormat.get_format_string()."""
 
692
        return BZR_BRANCH_FORMAT_5
 
693
 
 
694
 
 
695
class BzrBranchFormat6(BzrBranchFormat):
 
696
    """Bzr branch format 6.
 
697
 
 
698
    This format has:
 
699
     - weaves for file texts and inventory
 
700
     - hash subdirectory based stores.
 
701
     - TextStores for revisions and signatures.
 
702
    """
 
703
 
 
704
    def get_format_string(self):
 
705
        """See BzrBranchFormat.get_format_string()."""
 
706
        return BZR_BRANCH_FORMAT_6
 
707
 
 
708
 
 
709
BzrBranchFormat.register_format(BzrBranchFormat4())
 
710
BzrBranchFormat.register_format(BzrBranchFormat5())
 
711
BzrBranchFormat.register_format(BzrBranchFormat6())
 
712
 
 
713
 
490
714
class BzrBranch(Branch):
491
715
    """A branch stored in the actual filesystem.
492
716
 
524
748
 
525
749
    def push_stores(self, branch_to):
526
750
        """See Branch.push_stores."""
527
 
        if (self._branch_format != branch_to._branch_format
528
 
            or self._branch_format != 4):
 
751
        if (not isinstance(self._branch_format, BzrBranchFormat4) or
 
752
            self._branch_format != branch_to._branch_format):
529
753
            from bzrlib.fetch import greedy_fetch
530
 
            mutter("falling back to fetch logic to push between %s(%s) and %s(%s)",
 
754
            mutter("Using fetch logic to push between %s(%s) and %s(%s)",
531
755
                   self, self._branch_format, branch_to, branch_to._branch_format)
532
756
            greedy_fetch(to_branch=branch_to, from_branch=self,
533
757
                         revision=self.last_revision())
534
758
            return
535
759
 
 
760
        # format 4 to format 4 logic only.
536
761
        store_pairs = ((self.text_store,      branch_to.text_store),
537
762
                       (self.inventory_store, branch_to.inventory_store),
538
763
                       (self.revision_store,  branch_to.revision_store))
542
767
        except UnlistableStore:
543
768
            raise UnlistableBranch(from_store)
544
769
 
545
 
    def __init__(self, transport, init=False,
546
 
                 relax_version_check=False):
 
770
    def __init__(self, transport, init=deprecated_nonce,
 
771
                 relax_version_check=deprecated_nonce, format=None):
547
772
        """Create new branch object at a particular location.
548
773
 
549
774
        transport -- A Transport object, defining how to access files.
563
788
        assert isinstance(transport, Transport), \
564
789
            "%r is not a Transport" % transport
565
790
        self._transport = transport
566
 
        if init:
567
 
            self._make_control()
568
 
        self._check_format(relax_version_check)
 
791
        if deprecated_passed(init):
 
792
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
 
793
                 "deprecated as of bzr 0.8. Please use Branch.initialize().",
 
794
                 DeprecationWarning)
 
795
            if init:
 
796
                # this is slower than before deprecation, oh well never mind.
 
797
                # -> its deprecated.
 
798
                self._initialize(transport.base)
569
799
        self._find_modes()
 
800
        self._check_format(format)
 
801
        if deprecated_passed(relax_version_check):
 
802
            warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
 
803
                 "relax_version_check parameter is deprecated as of bzr 0.8. "
 
804
                 "Please use Branch.open_downlevel, or a BzrBranchFormat's "
 
805
                 "open() method.", DeprecationWarning)
 
806
            if (not relax_version_check
 
807
                and not self._branch_format.is_supported()):
 
808
                raise errors.UnsupportedFormatError(
 
809
                        'sorry, branch format %r not supported' % fmt,
 
810
                        ['use a different bzr version',
 
811
                         'or remove the .bzr directory'
 
812
                         ' and "bzr init" again'])
570
813
 
571
814
        def get_store(name, compressed=True, prefixed=False):
572
 
            relpath = self._rel_controlfilename(unicode(name))
 
815
            relpath = self._rel_controlfilename(safe_unicode(name))
573
816
            store = TextStore(self._transport.clone(relpath),
574
817
                              dir_mode=self._dir_mode,
575
818
                              file_mode=self._file_mode,
587
830
                ws.enable_cache = True
588
831
            return ws
589
832
 
590
 
        if self._branch_format == 4:
 
833
        if isinstance(self._branch_format, BzrBranchFormat4):
591
834
            self.inventory_store = get_store('inventory-store')
592
835
            self.text_store = get_store('text-store')
593
836
            self.revision_store = get_store('revision-store')
594
 
        elif self._branch_format == 5:
 
837
        elif isinstance(self._branch_format, BzrBranchFormat5):
595
838
            self.control_weaves = get_weave(u'')
596
839
            self.weave_store = get_weave(u'weaves')
597
840
            self.revision_store = get_store(u'revision-store', compressed=False)
598
 
        elif self._branch_format == 6:
 
841
        elif isinstance(self._branch_format, BzrBranchFormat6):
599
842
            self.control_weaves = get_weave(u'')
600
843
            self.weave_store = get_weave(u'weaves', prefixed=True)
601
844
            self.revision_store = get_store(u'revision-store', compressed=False,
603
846
        self.revision_store.register_suffix('sig')
604
847
        self._transaction = None
605
848
 
 
849
    @staticmethod
 
850
    def _initialize(base):
 
851
        """Create a bzr branch in the latest format."""
 
852
        return BzrBranchFormat6().initialize(base)
 
853
 
606
854
    def __str__(self):
607
855
        return '%s(%r)' % (self.__class__.__name__, self._transport.base)
608
856
 
775
1023
        if not self._set_file_mode:
776
1024
            self._file_mode = None
777
1025
 
778
 
    def _make_control(self):
779
 
        from bzrlib.inventory import Inventory
780
 
        from bzrlib.weavefile import write_weave_v5
781
 
        from bzrlib.weave import Weave
782
 
        
783
 
        # Create an empty inventory
784
 
        sio = StringIO()
785
 
        # if we want per-tree root ids then this is the place to set
786
 
        # them; they're not needed for now and so ommitted for
787
 
        # simplicity.
788
 
        bzrlib.xml5.serializer_v5.write_inventory(Inventory(), sio)
789
 
        empty_inv = sio.getvalue()
790
 
        sio = StringIO()
791
 
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
792
 
        empty_weave = sio.getvalue()
793
 
 
794
 
        cfn = self._rel_controlfilename
795
 
        # Since we don't have a .bzr directory, inherit the
796
 
        # mode from the root directory
797
 
        self._find_modes(u'.')
798
 
 
799
 
        dirs = ['', 'revision-store', 'weaves']
800
 
        files = [('README', 
801
 
            "This is a Bazaar-NG control directory.\n"
802
 
            "Do not change any files in this directory.\n"),
803
 
            ('branch-format', BZR_BRANCH_FORMAT_6),
804
 
            ('revision-history', ''),
805
 
            ('branch-name', ''),
806
 
            ('branch-lock', ''),
807
 
            ('pending-merges', ''),
808
 
            ('inventory', empty_inv),
809
 
            ('inventory.weave', empty_weave),
810
 
            ('ancestry.weave', empty_weave)
811
 
        ]
812
 
        self._transport.mkdir_multi([cfn(d) for d in dirs], mode=self._dir_mode)
813
 
        self.put_controlfiles(files)
814
 
        mutter('created control directory in ' + self._transport.base)
815
 
 
816
 
    def _check_format(self, relax_version_check):
817
 
        """Check this branch format is supported.
818
 
 
819
 
        The format level is stored, as an integer, in
 
1026
    def _check_format(self, format):
 
1027
        """Identify the branch format if needed.
 
1028
 
 
1029
        The format is stored as a reference to the format object in
820
1030
        self._branch_format for code that needs to check it later.
821
1031
 
822
 
        In the future, we might need different in-memory Branch
823
 
        classes to support downlevel branches.  But not yet.
 
1032
        The format parameter is either None or the branch format class
 
1033
        used to open this branch.
824
1034
        """
825
 
        try:
826
 
            fmt = self.controlfile('branch-format', 'r').read()
827
 
        except NoSuchFile:
828
 
            raise NotBranchError(path=self.base)
829
 
        mutter("got branch format %r", fmt)
830
 
        if fmt == BZR_BRANCH_FORMAT_6:
831
 
            self._branch_format = 6
832
 
        elif fmt == BZR_BRANCH_FORMAT_5:
833
 
            self._branch_format = 5
834
 
        elif fmt == BZR_BRANCH_FORMAT_4:
835
 
            self._branch_format = 4
836
 
 
837
 
        if (not relax_version_check
838
 
            and self._branch_format not in (5, 6)):
839
 
            raise errors.UnsupportedFormatError(
840
 
                           'sorry, branch format %r not supported' % fmt,
841
 
                           ['use a different bzr version',
842
 
                            'or remove the .bzr directory'
843
 
                            ' and "bzr init" again'])
 
1035
        if format is None:
 
1036
            format = BzrBranchFormat.find_format(self._transport)
 
1037
        self._branch_format = format
 
1038
        mutter("got branch format %s", self._branch_format)
844
1039
 
845
1040
    @needs_read_lock
846
1041
    def get_root_id(self):
1116
1311
                                revision_id, "sig")
1117
1312
 
1118
1313
 
 
1314
Branch.set_default_initializer(BzrBranch._initialize)
 
1315
 
 
1316
 
 
1317
class BranchTestProviderAdapter(object):
 
1318
    """A tool to generate a suite testing multiple branch formats at once.
 
1319
 
 
1320
    This is done by copying the test once for each transport and injecting
 
1321
    the transport_server, transport_readonly_server, and branch_format
 
1322
    classes into each copy. Each copy is also given a new id() to make it
 
1323
    easy to identify.
 
1324
    """
 
1325
 
 
1326
    def __init__(self, transport_server, transport_readonly_server, formats):
 
1327
        self._transport_server = transport_server
 
1328
        self._transport_readonly_server = transport_readonly_server
 
1329
        self._formats = formats
 
1330
    
 
1331
    def adapt(self, test):
 
1332
        result = TestSuite()
 
1333
        for format in self._formats:
 
1334
            new_test = deepcopy(test)
 
1335
            new_test.transport_server = self._transport_server
 
1336
            new_test.transport_readonly_server = self._transport_readonly_server
 
1337
            new_test.branch_format = format
 
1338
            def make_new_test_id():
 
1339
                new_id = "%s(%s)" % (new_test.id(), format.__class__.__name__)
 
1340
                return lambda: new_id
 
1341
            new_test.id = make_new_test_id()
 
1342
            result.addTest(new_test)
 
1343
        return result
 
1344
 
 
1345
 
1119
1346
class ScratchBranch(BzrBranch):
1120
1347
    """Special test class: a branch that cleans up after itself.
1121
1348
 
1137
1364
        """
1138
1365
        if transport is None:
1139
1366
            transport = bzrlib.transport.local.ScratchTransport()
1140
 
            super(ScratchBranch, self).__init__(transport, init=True)
 
1367
            Branch.initialize(transport.base)
 
1368
            super(ScratchBranch, self).__init__(transport)
1141
1369
        else:
1142
1370
            super(ScratchBranch, self).__init__(transport)
1143
1371