~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-03-01 07:30:00 UTC
  • mfrom: (2220.2.49 tags)
  • Revision ID: pqm@pqm.ubuntu.com-20070301073000-0bfe1394fee5e712
(mbp) tags in branch

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.")
1398
1439
        :param _run_hooks: Private parameter - allow disabling of
1399
1440
            hooks, used when pushing to a master branch.
1400
1441
        """
 
1442
        result = PullResult()
 
1443
        result.source_branch = source
 
1444
        result.target_branch = self
1401
1445
        source.lock_read()
1402
1446
        try:
1403
 
            old_count, old_tip = self.last_revision_info()
 
1447
            result.old_revno, result.old_revid = self.last_revision_info()
1404
1448
            try:
1405
1449
                self.update_revisions(source, stop_revision)
1406
1450
            except DivergedBranches:
1410
1454
                if stop_revision is None:
1411
1455
                    stop_revision = source.last_revision()
1412
1456
                self.generate_revision_history(stop_revision)
1413
 
            new_count, new_tip = self.last_revision_info()
 
1457
            result.tag_conflicts = source.tags.merge_to(self.tags)
 
1458
            result.new_revno, result.new_revid = self.last_revision_info()
 
1459
            if _hook_master:
 
1460
                result.master_branch = _hook_master
 
1461
                result.local_branch = self
 
1462
            else:
 
1463
                result.master_branch = self
 
1464
                result.local_branch = None
1414
1465
            if _run_hooks:
1415
 
                if _hook_master:
1416
 
                    _hook_local = self
1417
 
                else:
1418
 
                    _hook_master = self
1419
 
                    _hook_local = None
1420
1466
                for hook in Branch.hooks['post_pull']:
1421
 
                    hook(source, _hook_local, _hook_master, old_count, old_tip,
1422
 
                        new_count, new_tip)
1423
 
            return new_count - old_count
 
1467
                    hook(result)
1424
1468
        finally:
1425
1469
            source.unlock()
 
1470
        return result
1426
1471
 
1427
1472
    def _get_parent_location(self):
1428
1473
        _locs = ['parent', 'pull', 'x-pull']
1443
1488
        :param _run_hooks: Private parameter - allow disabling of
1444
1489
            hooks, used when pushing to a master branch.
1445
1490
        """
 
1491
        result = PushResult()
 
1492
        result.source_branch = self
 
1493
        result.target_branch = target
1446
1494
        target.lock_write()
1447
1495
        try:
1448
 
            old_count, old_tip = target.last_revision_info()
 
1496
            result.old_revno, result.old_revid = target.last_revision_info()
1449
1497
            try:
1450
1498
                target.update_revisions(self, stop_revision)
1451
1499
            except DivergedBranches:
1453
1501
                    raise
1454
1502
            if overwrite:
1455
1503
                target.set_revision_history(self.revision_history())
1456
 
            new_count, new_tip = target.last_revision_info()
 
1504
            result.tag_conflicts = self.tags.merge_to(target.tags)
 
1505
            result.new_revno, result.new_revid = target.last_revision_info()
 
1506
            if _hook_master:
 
1507
                result.master_branch = _hook_master
 
1508
                result.local_branch = target
 
1509
            else:
 
1510
                result.master_branch = target
 
1511
                result.local_branch = None
1457
1512
            if _run_hooks:
1458
 
                if _hook_master:
1459
 
                    _hook_local = target
1460
 
                else:
1461
 
                    _hook_master = target
1462
 
                    _hook_local = None
1463
1513
                for hook in Branch.hooks['post_push']:
1464
 
                    hook(self, _hook_local, _hook_master, old_count, old_tip,
1465
 
                        new_count, new_tip)
1466
 
            return new_count - old_count
 
1514
                    hook(result)
1467
1515
        finally:
1468
1516
            target.unlock()
 
1517
        return result
1469
1518
 
1470
1519
    def get_parent(self):
1471
1520
        """See Branch.get_parent."""
1697
1746
        return None
1698
1747
 
1699
1748
 
 
1749
class BzrBranchExperimental(BzrBranch5):
 
1750
    """Bzr experimental branch format
 
1751
 
 
1752
    This format has:
 
1753
     - a revision-history file.
 
1754
     - a format string
 
1755
     - a lock dir guarding the branch itself
 
1756
     - all of this stored in a branch/ subdirectory
 
1757
     - works with shared repositories.
 
1758
     - a tag dictionary in the branch
 
1759
 
 
1760
    This format is new in bzr 0.15, but shouldn't be used for real data, 
 
1761
    only for testing.
 
1762
 
 
1763
    This class acts as it's own BranchFormat.
 
1764
    """
 
1765
 
 
1766
    _matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
1767
 
 
1768
    @classmethod
 
1769
    def get_format_string(cls):
 
1770
        """See BranchFormat.get_format_string()."""
 
1771
        return "Bazaar-NG branch format experimental\n"
 
1772
 
 
1773
    @classmethod
 
1774
    def get_format_description(cls):
 
1775
        """See BranchFormat.get_format_description()."""
 
1776
        return "Experimental branch format"
 
1777
 
 
1778
    @classmethod
 
1779
    def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
 
1780
            lock_class):
 
1781
        branch_transport = a_bzrdir.get_branch_transport(cls)
 
1782
        control_files = lockable_files.LockableFiles(branch_transport,
 
1783
            lock_filename, lock_class)
 
1784
        control_files.create_lock()
 
1785
        control_files.lock_write()
 
1786
        try:
 
1787
            for filename, content in utf8_files:
 
1788
                control_files.put_utf8(filename, content)
 
1789
        finally:
 
1790
            control_files.unlock()
 
1791
        
 
1792
    @classmethod
 
1793
    def initialize(cls, a_bzrdir):
 
1794
        """Create a branch of this format in a_bzrdir."""
 
1795
        utf8_files = [('format', cls.get_format_string()),
 
1796
                      ('revision-history', ''),
 
1797
                      ('branch-name', ''),
 
1798
                      ('tags', ''),
 
1799
                      ]
 
1800
        cls._initialize_control_files(a_bzrdir, utf8_files,
 
1801
            'lock', lockdir.LockDir)
 
1802
        return cls.open(a_bzrdir, _found=True)
 
1803
 
 
1804
    @classmethod
 
1805
    def open(cls, a_bzrdir, _found=False):
 
1806
        """Return the branch object for a_bzrdir
 
1807
 
 
1808
        _found is a private parameter, do not use it. It is used to indicate
 
1809
               if format probing has already be done.
 
1810
        """
 
1811
        if not _found:
 
1812
            format = BranchFormat.find_format(a_bzrdir)
 
1813
            assert format.__class__ == cls
 
1814
        transport = a_bzrdir.get_branch_transport(None)
 
1815
        control_files = lockable_files.LockableFiles(transport, 'lock',
 
1816
                                                     lockdir.LockDir)
 
1817
        return cls(_format=cls,
 
1818
            _control_files=control_files,
 
1819
            a_bzrdir=a_bzrdir,
 
1820
            _repository=a_bzrdir.find_repository())
 
1821
 
 
1822
    @classmethod
 
1823
    def is_supported(cls):
 
1824
        return True
 
1825
 
 
1826
    def _make_tags(self):
 
1827
        return BasicTags(self)
 
1828
 
 
1829
    @classmethod
 
1830
    def supports_tags(cls):
 
1831
        return True
 
1832
 
 
1833
 
 
1834
BranchFormat.register_format(BzrBranchExperimental)
 
1835
 
 
1836
 
1700
1837
class BzrBranch6(BzrBranch5):
1701
1838
 
1702
1839
    @needs_read_lock
1885
2022
            revno = self.revision_id_to_revno(revision_id)
1886
2023
        destination.set_last_revision_info(revno, revision_id)
1887
2024
 
 
2025
    def _make_tags(self):
 
2026
        return BasicTags(self)
 
2027
 
1888
2028
 
1889
2029
class BranchTestProviderAdapter(object):
1890
2030
    """A tool to generate a suite testing multiple branch formats at once.
1909
2049
            new_test.bzrdir_format = bzrdir_format
1910
2050
            new_test.branch_format = branch_format
1911
2051
            def make_new_test_id():
1912
 
                new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
 
2052
                # the format can be either a class or an instance
 
2053
                name = getattr(branch_format, '__name__',
 
2054
                        branch_format.__class__.__name__)
 
2055
                new_id = "%s(%s)" % (new_test.id(), name)
1913
2056
                return lambda: new_id
1914
2057
            new_test.id = make_new_test_id()
1915
2058
            result.addTest(new_test)
1916
2059
        return result
1917
2060
 
1918
2061
 
 
2062
######################################################################
 
2063
# results of operations
 
2064
 
 
2065
 
 
2066
class _Result(object):
 
2067
 
 
2068
    def _show_tag_conficts(self, to_file):
 
2069
        if not getattr(self, 'tag_conflicts', None):
 
2070
            return
 
2071
        to_file.write('Conflicting tags:\n')
 
2072
        for name, value1, value2 in self.tag_conflicts:
 
2073
            to_file.write('    %s\n' % (name, ))
 
2074
 
 
2075
 
 
2076
class PullResult(_Result):
 
2077
    """Result of a Branch.pull operation.
 
2078
 
 
2079
    :ivar old_revno: Revision number before pull.
 
2080
    :ivar new_revno: Revision number after pull.
 
2081
    :ivar old_revid: Tip revision id before pull.
 
2082
    :ivar new_revid: Tip revision id after pull.
 
2083
    :ivar source_branch: Source (local) branch object.
 
2084
    :ivar master_branch: Master branch of the target, or None.
 
2085
    :ivar target_branch: Target/destination branch object.
 
2086
    """
 
2087
 
 
2088
    def __int__(self):
 
2089
        # DEPRECATED: pull used to return the change in revno
 
2090
        return self.new_revno - self.old_revno
 
2091
 
 
2092
    def report(self, to_file):
 
2093
        if self.old_revid == self.new_revid:
 
2094
            to_file.write('No revisions to pull.\n')
 
2095
        else:
 
2096
            to_file.write('Now on revision %d.\n' % self.new_revno)
 
2097
        self._show_tag_conficts(to_file)
 
2098
 
 
2099
 
 
2100
class PushResult(_Result):
 
2101
    """Result of a Branch.push operation.
 
2102
 
 
2103
    :ivar old_revno: Revision number before push.
 
2104
    :ivar new_revno: Revision number after push.
 
2105
    :ivar old_revid: Tip revision id before push.
 
2106
    :ivar new_revid: Tip revision id after push.
 
2107
    :ivar source_branch: Source branch object.
 
2108
    :ivar master_branch: Master branch of the target, or None.
 
2109
    :ivar target_branch: Target/destination branch object.
 
2110
    """
 
2111
 
 
2112
    def __int__(self):
 
2113
        # DEPRECATED: push used to return the change in revno
 
2114
        return self.new_revno - self.old_revno
 
2115
 
 
2116
    def report(self, to_file):
 
2117
        """Write a human-readable description of the result."""
 
2118
        if self.old_revid == self.new_revid:
 
2119
            to_file.write('No new revisions to push.\n')
 
2120
        else:
 
2121
            to_file.write('Pushed up to revision %d.\n' % self.new_revno)
 
2122
        self._show_tag_conficts(to_file)
 
2123
 
 
2124
 
1919
2125
class BranchCheckResult(object):
1920
2126
    """Results of checking branch consistency.
1921
2127
 
1936
2142
             self.branch._format)
1937
2143
 
1938
2144
 
1939
 
######################################################################
1940
 
# predicates
1941
 
 
1942
 
 
1943
 
@deprecated_function(zero_eight)
1944
 
def is_control_file(*args, **kwargs):
1945
 
    """See bzrlib.workingtree.is_control_file."""
1946
 
    from bzrlib import workingtree
1947
 
    return workingtree.is_control_file(*args, **kwargs)
1948
 
 
1949
 
 
1950
2145
class Converter5to6(object):
1951
2146
    """Perform an in-place upgrade of format 5 to format 6"""
1952
2147
 
1961
2156
        new_branch.set_bound_location(branch.get_bound_location())
1962
2157
        new_branch.set_push_location(branch.get_push_location())
1963
2158
 
 
2159
        # New branch has no tags by default
 
2160
        new_branch.tags._set_tag_dict({})
 
2161
 
1964
2162
        # Copying done; now update target format
1965
2163
        new_branch.control_files.put_utf8('format',
1966
2164
            format.get_format_string())