~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Robert Collins
  • Date: 2007-03-05 03:43:56 UTC
  • mfrom: (2312 +trunk)
  • mto: (2255.11.6 dirstate)
  • mto: This revision was merged to the branch mainline in revision 2322.
  • Revision ID: robertc@robertcollins.net-20070305034356-og43j35eg62m952f
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
40
40
        )
41
41
from bzrlib.config import BranchConfig, TreeConfig
42
42
from bzrlib.lockable_files import LockableFiles, TransportLock
 
43
from bzrlib.tag import (
 
44
    BasicTags,
 
45
    DisabledTags,
 
46
    )
43
47
""")
44
48
 
45
49
from bzrlib.decorators import needs_read_lock, needs_write_lock
88
92
    # - RBC 20060112
89
93
    base = None
90
94
 
 
95
    # override this to set the strategy for storing tags
 
96
    def _make_tags(self):
 
97
        return DisabledTags(self)
 
98
 
91
99
    def __init__(self, *ignored, **ignored_too):
92
 
        raise NotImplementedError('The Branch class is abstract')
 
100
        self.tags = self._make_tags()
93
101
 
94
102
    def break_lock(self):
95
103
        """Break a lock if one is present from another instance.
399
407
        """Mirror source into this branch.
400
408
 
401
409
        This branch is considered to be 'local', having low latency.
 
410
 
 
411
        :returns: PullResult instance
402
412
        """
403
413
        raise NotImplementedError(self.pull)
404
414
 
677
687
            checkout_branch.pull(self, stop_revision=revision_id)
678
688
        return checkout.create_workingtree(revision_id)
679
689
 
 
690
    def supports_tags(self):
 
691
        return self._format.supports_tags()
 
692
 
680
693
 
681
694
class BranchFormat(object):
682
695
    """An encapsulation of the initialization and open routines for a format.
796
809
    def __str__(self):
797
810
        return self.get_format_string().rstrip()
798
811
 
 
812
    def supports_tags(self):
 
813
        """True if this format supports tags stored in the branch"""
 
814
        return False  # by default
 
815
 
 
816
    # XXX: Probably doesn't really belong here -- mbp 20070212
 
817
    def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
 
818
            lock_class):
 
819
        branch_transport = a_bzrdir.get_branch_transport(self)
 
820
        control_files = lockable_files.LockableFiles(branch_transport,
 
821
            lock_filename, lock_class)
 
822
        control_files.create_lock()
 
823
        control_files.lock_write()
 
824
        try:
 
825
            for filename, content in utf8_files:
 
826
                control_files.put_utf8(filename, content)
 
827
        finally:
 
828
            control_files.unlock()
 
829
 
799
830
 
800
831
class BranchHooks(dict):
801
832
    """A dictionary mapping hook name to a list of callables for branch hooks.
819
850
        self['set_rh'] = []
820
851
        # invoked after a push operation completes.
821
852
        # the api signature is
 
853
        # (push_result)
 
854
        # containing the members
822
855
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
823
856
        # where local is the local branch or None, master is the target 
824
857
        # master branch, and the rest should be self explanatory. The source
827
860
        self['post_push'] = []
828
861
        # invoked after a pull operation completes.
829
862
        # the api signature is
 
863
        # (pull_result)
 
864
        # containing the members
830
865
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
831
866
        # where local is the local branch or None, master is the target 
832
867
        # master branch, and the rest should be self explanatory. The source
955
990
                          a_bzrdir=a_bzrdir,
956
991
                          _repository=a_bzrdir.find_repository())
957
992
 
958
 
    def __str__(self):
959
 
        return "Bazaar-NG Metadir branch format 5"
960
 
 
961
993
 
962
994
class BzrBranchFormat6(BzrBranchFormat5):
963
995
    """Branch format with last-revision
981
1013
        """Create a branch of this format in a_bzrdir."""
982
1014
        utf8_files = [('last-revision', '0 null:\n'),
983
1015
                      ('branch-name', ''),
984
 
                      ('branch.conf', '')
 
1016
                      ('branch.conf', ''),
 
1017
                      ('tags', ''),
985
1018
                      ]
986
1019
        return self._initialize_helper(a_bzrdir, utf8_files)
987
1020
 
1002
1035
                          a_bzrdir=a_bzrdir,
1003
1036
                          _repository=a_bzrdir.find_repository())
1004
1037
 
 
1038
    def supports_tags(self):
 
1039
        return True
 
1040
 
1005
1041
 
1006
1042
class BranchReferenceFormat(BranchFormat):
1007
1043
    """Bzr branch reference format.
1107
1143
            upgrade/recovery type use; it's not guaranteed that
1108
1144
            all operations will work on old format branches.
1109
1145
        """
 
1146
        Branch.__init__(self)
1110
1147
        if a_bzrdir is None:
1111
1148
            self.bzrdir = bzrdir.BzrDir.open(transport.base)
1112
1149
        else:
1113
1150
            self.bzrdir = a_bzrdir
1114
 
        self._transport = self.bzrdir.transport.clone('..')
1115
 
        self._base = self._transport.base
 
1151
        # self._transport used to point to the directory containing the
 
1152
        # control directory, but was not used - now it's just the transport
 
1153
        # for the branch control files.  mbp 20070212
 
1154
        self._base = self.bzrdir.transport.clone('..').base
1116
1155
        self._format = _format
1117
1156
        if _control_files is None:
1118
1157
            raise ValueError('BzrBranch _control_files is None')
1119
1158
        self.control_files = _control_files
 
1159
        self._transport = _control_files._transport
1120
1160
        if deprecated_passed(init):
1121
1161
            warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
1122
1162
                 "deprecated as of bzr 0.8. Please use Branch.create().",
1151
1191
    __repr__ = __str__
1152
1192
 
1153
1193
    def _get_base(self):
 
1194
        """Returns the directory containing the control directory."""
1154
1195
        return self._base
1155
1196
 
1156
1197
    base = property(_get_base, doc="The URL for the root of this branch.")
1397
1438
        :param _run_hooks: Private parameter - allow disabling of
1398
1439
            hooks, used when pushing to a master branch.
1399
1440
        """
 
1441
        result = PullResult()
 
1442
        result.source_branch = source
 
1443
        result.target_branch = self
1400
1444
        source.lock_read()
1401
1445
        try:
1402
 
            old_count, old_tip = self.last_revision_info()
 
1446
            result.old_revno, result.old_revid = self.last_revision_info()
1403
1447
            try:
1404
1448
                self.update_revisions(source, stop_revision)
1405
1449
            except DivergedBranches:
1406
1450
                if not overwrite:
1407
1451
                    raise
1408
1452
            if overwrite:
1409
 
                self.set_revision_history(source.revision_history())
1410
 
            new_count, new_tip = self.last_revision_info()
 
1453
                if stop_revision is None:
 
1454
                    stop_revision = source.last_revision()
 
1455
                self.generate_revision_history(stop_revision)
 
1456
            result.tag_conflicts = source.tags.merge_to(self.tags)
 
1457
            result.new_revno, result.new_revid = self.last_revision_info()
 
1458
            if _hook_master:
 
1459
                result.master_branch = _hook_master
 
1460
                result.local_branch = self
 
1461
            else:
 
1462
                result.master_branch = self
 
1463
                result.local_branch = None
1411
1464
            if _run_hooks:
1412
 
                if _hook_master:
1413
 
                    _hook_local = self
1414
 
                else:
1415
 
                    _hook_master = self
1416
 
                    _hook_local = None
1417
1465
                for hook in Branch.hooks['post_pull']:
1418
 
                    hook(source, _hook_local, _hook_master, old_count, old_tip,
1419
 
                        new_count, new_tip)
1420
 
            return new_count - old_count
 
1466
                    hook(result)
1421
1467
        finally:
1422
1468
            source.unlock()
 
1469
        return result
1423
1470
 
1424
1471
    def _get_parent_location(self):
1425
1472
        _locs = ['parent', 'pull', 'x-pull']
1440
1487
        :param _run_hooks: Private parameter - allow disabling of
1441
1488
            hooks, used when pushing to a master branch.
1442
1489
        """
 
1490
        result = PushResult()
 
1491
        result.source_branch = self
 
1492
        result.target_branch = target
1443
1493
        target.lock_write()
1444
1494
        try:
1445
 
            old_count, old_tip = target.last_revision_info()
 
1495
            result.old_revno, result.old_revid = target.last_revision_info()
1446
1496
            try:
1447
1497
                target.update_revisions(self, stop_revision)
1448
1498
            except DivergedBranches:
1450
1500
                    raise
1451
1501
            if overwrite:
1452
1502
                target.set_revision_history(self.revision_history())
1453
 
            new_count, new_tip = target.last_revision_info()
 
1503
            result.tag_conflicts = self.tags.merge_to(target.tags)
 
1504
            result.new_revno, result.new_revid = target.last_revision_info()
 
1505
            if _hook_master:
 
1506
                result.master_branch = _hook_master
 
1507
                result.local_branch = target
 
1508
            else:
 
1509
                result.master_branch = target
 
1510
                result.local_branch = None
1454
1511
            if _run_hooks:
1455
 
                if _hook_master:
1456
 
                    _hook_local = target
1457
 
                else:
1458
 
                    _hook_master = target
1459
 
                    _hook_local = None
1460
1512
                for hook in Branch.hooks['post_push']:
1461
 
                    hook(self, _hook_local, _hook_master, old_count, old_tip,
1462
 
                        new_count, new_tip)
1463
 
            return new_count - old_count
 
1513
                    hook(result)
1464
1514
        finally:
1465
1515
            target.unlock()
 
1516
        return result
1466
1517
 
1467
1518
    def get_parent(self):
1468
1519
        """See Branch.get_parent."""
1694
1745
        return None
1695
1746
 
1696
1747
 
 
1748
class BzrBranchExperimental(BzrBranch5):
 
1749
    """Bzr experimental branch format
 
1750
 
 
1751
    This format has:
 
1752
     - a revision-history file.
 
1753
     - a format string
 
1754
     - a lock dir guarding the branch itself
 
1755
     - all of this stored in a branch/ subdirectory
 
1756
     - works with shared repositories.
 
1757
     - a tag dictionary in the branch
 
1758
 
 
1759
    This format is new in bzr 0.15, but shouldn't be used for real data, 
 
1760
    only for testing.
 
1761
 
 
1762
    This class acts as it's own BranchFormat.
 
1763
    """
 
1764
 
 
1765
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
1766
 
 
1767
    @classmethod
 
1768
    def get_format_string(cls):
 
1769
        """See BranchFormat.get_format_string()."""
 
1770
        return "Bazaar-NG branch format experimental\n"
 
1771
 
 
1772
    @classmethod
 
1773
    def get_format_description(cls):
 
1774
        """See BranchFormat.get_format_description()."""
 
1775
        return "Experimental branch format"
 
1776
 
 
1777
    @classmethod
 
1778
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
 
1779
            lock_class):
 
1780
        branch_transport = a_bzrdir.get_branch_transport(cls)
 
1781
        control_files = lockable_files.LockableFiles(branch_transport,
 
1782
            lock_filename, lock_class)
 
1783
        control_files.create_lock()
 
1784
        control_files.lock_write()
 
1785
        try:
 
1786
            for filename, content in utf8_files:
 
1787
                control_files.put_utf8(filename, content)
 
1788
        finally:
 
1789
            control_files.unlock()
 
1790
        
 
1791
    @classmethod
 
1792
    def initialize(cls, a_bzrdir):
 
1793
        """Create a branch of this format in a_bzrdir."""
 
1794
        utf8_files = [('format', cls.get_format_string()),
 
1795
                      ('revision-history', ''),
 
1796
                      ('branch-name', ''),
 
1797
                      ('tags', ''),
 
1798
                      ]
 
1799
        cls._initialize_control_files(a_bzrdir, utf8_files,
 
1800
            'lock', lockdir.LockDir)
 
1801
        return cls.open(a_bzrdir, _found=True)
 
1802
 
 
1803
    @classmethod
 
1804
    def open(cls, a_bzrdir, _found=False):
 
1805
        """Return the branch object for a_bzrdir
 
1806
 
 
1807
        _found is a private parameter, do not use it. It is used to indicate
 
1808
               if format probing has already be done.
 
1809
        """
 
1810
        if not _found:
 
1811
            format = BranchFormat.find_format(a_bzrdir)
 
1812
            assert format.__class__ == cls
 
1813
        transport = a_bzrdir.get_branch_transport(None)
 
1814
        control_files = lockable_files.LockableFiles(transport, 'lock',
 
1815
                                                     lockdir.LockDir)
 
1816
        return cls(_format=cls,
 
1817
            _control_files=control_files,
 
1818
            a_bzrdir=a_bzrdir,
 
1819
            _repository=a_bzrdir.find_repository())
 
1820
 
 
1821
    @classmethod
 
1822
    def is_supported(cls):
 
1823
        return True
 
1824
 
 
1825
    def _make_tags(self):
 
1826
        return BasicTags(self)
 
1827
 
 
1828
    @classmethod
 
1829
    def supports_tags(cls):
 
1830
        return True
 
1831
 
 
1832
 
 
1833
BranchFormat.register_format(BzrBranchExperimental)
 
1834
 
 
1835
 
1697
1836
class BzrBranch6(BzrBranch5):
1698
1837
 
1699
1838
    @needs_read_lock
1882
2021
            revno = self.revision_id_to_revno(revision_id)
1883
2022
        destination.set_last_revision_info(revno, revision_id)
1884
2023
 
 
2024
    def _make_tags(self):
 
2025
        return BasicTags(self)
 
2026
 
1885
2027
 
1886
2028
class BranchTestProviderAdapter(object):
1887
2029
    """A tool to generate a suite testing multiple branch formats at once.
1906
2048
            new_test.bzrdir_format = bzrdir_format
1907
2049
            new_test.branch_format = branch_format
1908
2050
            def make_new_test_id():
1909
 
                new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
 
2051
                # the format can be either a class or an instance
 
2052
                name = getattr(branch_format, '__name__',
 
2053
                        branch_format.__class__.__name__)
 
2054
                new_id = "%s(%s)" % (new_test.id(), name)
1910
2055
                return lambda: new_id
1911
2056
            new_test.id = make_new_test_id()
1912
2057
            result.addTest(new_test)
1913
2058
        return result
1914
2059
 
1915
2060
 
 
2061
######################################################################
 
2062
# results of operations
 
2063
 
 
2064
 
 
2065
class _Result(object):
 
2066
 
 
2067
    def _show_tag_conficts(self, to_file):
 
2068
        if not getattr(self, 'tag_conflicts', None):
 
2069
            return
 
2070
        to_file.write('Conflicting tags:\n')
 
2071
        for name, value1, value2 in self.tag_conflicts:
 
2072
            to_file.write('    %s\n' % (name, ))
 
2073
 
 
2074
 
 
2075
class PullResult(_Result):
 
2076
    """Result of a Branch.pull operation.
 
2077
 
 
2078
    :ivar old_revno: Revision number before pull.
 
2079
    :ivar new_revno: Revision number after pull.
 
2080
    :ivar old_revid: Tip revision id before pull.
 
2081
    :ivar new_revid: Tip revision id after pull.
 
2082
    :ivar source_branch: Source (local) branch object.
 
2083
    :ivar master_branch: Master branch of the target, or None.
 
2084
    :ivar target_branch: Target/destination branch object.
 
2085
    """
 
2086
 
 
2087
    def __int__(self):
 
2088
        # DEPRECATED: pull used to return the change in revno
 
2089
        return self.new_revno - self.old_revno
 
2090
 
 
2091
    def report(self, to_file):
 
2092
        if self.old_revid == self.new_revid:
 
2093
            to_file.write('No revisions to pull.\n')
 
2094
        else:
 
2095
            to_file.write('Now on revision %d.\n' % self.new_revno)
 
2096
        self._show_tag_conficts(to_file)
 
2097
 
 
2098
 
 
2099
class PushResult(_Result):
 
2100
    """Result of a Branch.push operation.
 
2101
 
 
2102
    :ivar old_revno: Revision number before push.
 
2103
    :ivar new_revno: Revision number after push.
 
2104
    :ivar old_revid: Tip revision id before push.
 
2105
    :ivar new_revid: Tip revision id after push.
 
2106
    :ivar source_branch: Source branch object.
 
2107
    :ivar master_branch: Master branch of the target, or None.
 
2108
    :ivar target_branch: Target/destination branch object.
 
2109
    """
 
2110
 
 
2111
    def __int__(self):
 
2112
        # DEPRECATED: push used to return the change in revno
 
2113
        return self.new_revno - self.old_revno
 
2114
 
 
2115
    def report(self, to_file):
 
2116
        """Write a human-readable description of the result."""
 
2117
        if self.old_revid == self.new_revid:
 
2118
            to_file.write('No new revisions to push.\n')
 
2119
        else:
 
2120
            to_file.write('Pushed up to revision %d.\n' % self.new_revno)
 
2121
        self._show_tag_conficts(to_file)
 
2122
 
 
2123
 
1916
2124
class BranchCheckResult(object):
1917
2125
    """Results of checking branch consistency.
1918
2126
 
1933
2141
             self.branch._format)
1934
2142
 
1935
2143
 
1936
 
######################################################################
1937
 
# predicates
1938
 
 
1939
 
 
1940
 
@deprecated_function(zero_eight)
1941
 
def is_control_file(*args, **kwargs):
1942
 
    """See bzrlib.workingtree.is_control_file."""
1943
 
    from bzrlib import workingtree
1944
 
    return workingtree.is_control_file(*args, **kwargs)
1945
 
 
1946
 
 
1947
2144
class Converter5to6(object):
1948
2145
    """Perform an in-place upgrade of format 5 to format 6"""
1949
2146
 
1958
2155
        new_branch.set_bound_location(branch.get_bound_location())
1959
2156
        new_branch.set_push_location(branch.get_push_location())
1960
2157
 
 
2158
        # New branch has no tags by default
 
2159
        new_branch.tags._set_tag_dict({})
 
2160
 
1961
2161
        # Copying done; now update target format
1962
2162
        new_branch.control_files.put_utf8('format',
1963
2163
            format.get_format_string())