~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Robert Collins
  • Date: 2010-04-08 04:34:03 UTC
  • mfrom: (5138 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5139.
  • Revision ID: robertc@robertcollins.net-20100408043403-56z0d07vdqrx7f3t
Update bugfix for 528114 to trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
167
167
        """
168
168
        control = bzrdir.BzrDir.open(base, _unsupported,
169
169
                                     possible_transports=possible_transports)
170
 
        return control.open_branch(_unsupported)
 
170
        return control.open_branch(unsupported=_unsupported)
171
171
 
172
172
    @staticmethod
173
 
    def open_from_transport(transport, _unsupported=False):
 
173
    def open_from_transport(transport, name=None, _unsupported=False):
174
174
        """Open the branch rooted at transport"""
175
175
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
176
 
        return control.open_branch(_unsupported)
 
176
        return control.open_branch(name=name, unsupported=_unsupported)
177
177
 
178
178
    @staticmethod
179
179
    def open_containing(url, possible_transports=None):
217
217
    def _get_fallback_repository(self, url):
218
218
        """Get the repository we fallback to at url."""
219
219
        url = urlutils.join(self.base, url)
220
 
        a_bzrdir = bzrdir.BzrDir.open(url,
 
220
        a_branch = Branch.open(url,
221
221
            possible_transports=[self.bzrdir.root_transport])
222
 
        return a_bzrdir.open_branch().repository
 
222
        return a_branch.repository
223
223
 
224
224
    def _get_tags_bytes(self):
225
225
        """Get the bytes of a serialised tags dict.
455
455
        filtered = self._filter_merge_sorted_revisions(
456
456
            self._merge_sorted_revisions_cache, start_revision_id,
457
457
            stop_revision_id, stop_rule)
 
458
        # Make sure we don't return revisions that are not part of the
 
459
        # start_revision_id ancestry.
 
460
        filtered = self._filter_non_ancestors(filtered)
458
461
        if direction == 'reverse':
459
462
            return filtered
460
463
        if direction == 'forward':
525
528
        else:
526
529
            raise ValueError('invalid stop_rule %r' % stop_rule)
527
530
 
 
531
    def _filter_non_ancestors(self, rev_iter):
 
532
        # If we started from a dotted revno, we want to consider it as a tip
 
533
        # and don't want to yield revisions that are not part of its
 
534
        # ancestry. Given the order guaranteed by the merge sort, we will see
 
535
        # uninteresting descendants of the first parent of our tip before the
 
536
        # tip itself.
 
537
        first = rev_iter.next()
 
538
        (rev_id, merge_depth, revno, end_of_merge) = first
 
539
        yield first
 
540
        if not merge_depth:
 
541
            # We start at a mainline revision so by definition, all others
 
542
            # revisions in rev_iter are ancestors
 
543
            for node in rev_iter:
 
544
                yield node
 
545
 
 
546
        clean = False
 
547
        whitelist = set()
 
548
        pmap = self.repository.get_parent_map([rev_id])
 
549
        parents = pmap.get(rev_id, [])
 
550
        if parents:
 
551
            whitelist.update(parents)
 
552
        else:
 
553
            # If there is no parents, there is nothing of interest left
 
554
 
 
555
            # FIXME: It's hard to test this scenario here as this code is never
 
556
            # called in that case. -- vila 20100322
 
557
            return
 
558
 
 
559
        for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
 
560
            if not clean:
 
561
                if rev_id in whitelist:
 
562
                    pmap = self.repository.get_parent_map([rev_id])
 
563
                    parents = pmap.get(rev_id, [])
 
564
                    whitelist.remove(rev_id)
 
565
                    whitelist.update(parents)
 
566
                    if merge_depth == 0:
 
567
                        # We've reached the mainline, there is nothing left to
 
568
                        # filter
 
569
                        clean = True
 
570
                else:
 
571
                    # A revision that is not part of the ancestry of our
 
572
                    # starting revision.
 
573
                    continue
 
574
            yield (rev_id, merge_depth, revno, end_of_merge)
 
575
 
528
576
    def leave_lock_in_place(self):
529
577
        """Tell this branch object not to release the physical lock when this
530
578
        object is unlocked.
1317
1365
        if lightweight:
1318
1366
            format = self._get_checkout_format()
1319
1367
            checkout = format.initialize_on_transport(t)
1320
 
            from_branch = BranchReferenceFormat().initialize(checkout, self)
 
1368
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
1369
                target_branch=self)
1321
1370
        else:
1322
1371
            format = self._get_checkout_format()
1323
1372
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1365
1414
    def supports_tags(self):
1366
1415
        return self._format.supports_tags()
1367
1416
 
 
1417
    def automatic_tag_name(self, revision_id):
 
1418
        """Try to automatically find the tag name for a revision.
 
1419
 
 
1420
        :param revision_id: Revision id of the revision.
 
1421
        :return: A tag name or None if no tag name could be determined.
 
1422
        """
 
1423
        for hook in Branch.hooks['automatic_tag_name']:
 
1424
            ret = hook(self, revision_id)
 
1425
            if ret is not None:
 
1426
                return ret
 
1427
        return None
 
1428
 
1368
1429
    def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1369
1430
                                         other_branch):
1370
1431
        """Ensure that revision_b is a descendant of revision_a.
1434
1495
        return not (self == other)
1435
1496
 
1436
1497
    @classmethod
1437
 
    def find_format(klass, a_bzrdir):
 
1498
    def find_format(klass, a_bzrdir, name=None):
1438
1499
        """Return the format for the branch object in a_bzrdir."""
1439
1500
        try:
1440
 
            transport = a_bzrdir.get_branch_transport(None)
 
1501
            transport = a_bzrdir.get_branch_transport(None, name=name)
1441
1502
            format_string = transport.get_bytes("format")
1442
1503
            return klass._formats[format_string]
1443
1504
        except errors.NoSuchFile:
1483
1544
        """Return the short format description for this format."""
1484
1545
        raise NotImplementedError(self.get_format_description)
1485
1546
 
1486
 
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1487
 
                           set_format=True):
 
1547
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1548
                           lock_type='metadir', set_format=True):
1488
1549
        """Initialize a branch in a bzrdir, with specified files
1489
1550
 
1490
1551
        :param a_bzrdir: The bzrdir to initialize the branch in
1491
1552
        :param utf8_files: The files to create as a list of
1492
1553
            (filename, content) tuples
 
1554
        :param name: Name of colocated branch to create, if any
1493
1555
        :param set_format: If True, set the format with
1494
1556
            self.get_format_string.  (BzrBranch4 has its format set
1495
1557
            elsewhere)
1496
1558
        :return: a branch in this format
1497
1559
        """
1498
1560
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1499
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
1561
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1500
1562
        lock_map = {
1501
1563
            'metadir': ('lock', lockdir.LockDir),
1502
1564
            'branch4': ('branch-lock', lockable_files.TransportLock),
1523
1585
        finally:
1524
1586
            if lock_taken:
1525
1587
                control_files.unlock()
1526
 
        return self.open(a_bzrdir, _found=True)
 
1588
        return self.open(a_bzrdir, name, _found=True)
1527
1589
 
1528
 
    def initialize(self, a_bzrdir):
1529
 
        """Create a branch of this format in a_bzrdir."""
 
1590
    def initialize(self, a_bzrdir, name=None):
 
1591
        """Create a branch of this format in a_bzrdir.
 
1592
        
 
1593
        :param name: Name of the colocated branch to create.
 
1594
        """
1530
1595
        raise NotImplementedError(self.initialize)
1531
1596
 
1532
1597
    def is_supported(self):
1562
1627
        """
1563
1628
        raise NotImplementedError(self.network_name)
1564
1629
 
1565
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1630
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1566
1631
        """Return the branch object for a_bzrdir
1567
1632
 
1568
1633
        :param a_bzrdir: A BzrDir that contains a branch.
 
1634
        :param name: Name of colocated branch to open
1569
1635
        :param _found: a private parameter, do not use it. It is used to
1570
1636
            indicate if format probing has already be done.
1571
1637
        :param ignore_fallbacks: when set, no fallback branches will be opened
1679
1745
            "multiple hooks installed for transform_fallback_location, "
1680
1746
            "all are called with the url returned from the previous hook."
1681
1747
            "The order is however undefined.", (1, 9), None))
 
1748
        self.create_hook(HookPoint('automatic_tag_name',
 
1749
            "Called to determine an automatic tag name for a revision."
 
1750
            "automatic_tag_name is called with (branch, revision_id) and "
 
1751
            "should return a tag name or None if no tag name could be "
 
1752
            "determined. The first non-None tag name returned will be used.",
 
1753
            (2, 2), None))
 
1754
 
1682
1755
 
1683
1756
 
1684
1757
# install the default hooks into the Branch class.
1735
1808
        """See BranchFormat.get_format_description()."""
1736
1809
        return "Branch format 4"
1737
1810
 
1738
 
    def initialize(self, a_bzrdir):
 
1811
    def initialize(self, a_bzrdir, name=None):
1739
1812
        """Create a branch of this format in a_bzrdir."""
1740
1813
        utf8_files = [('revision-history', ''),
1741
1814
                      ('branch-name', ''),
1742
1815
                      ]
1743
 
        return self._initialize_helper(a_bzrdir, utf8_files,
 
1816
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1744
1817
                                       lock_type='branch4', set_format=False)
1745
1818
 
1746
1819
    def __init__(self):
1751
1824
        """The network name for this format is the control dirs disk label."""
1752
1825
        return self._matchingbzrdir.get_format_string()
1753
1826
 
1754
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1827
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1755
1828
        """See BranchFormat.open()."""
1756
1829
        if not _found:
1757
1830
            # we are being called directly and must probe.
1759
1832
        return BzrBranch(_format=self,
1760
1833
                         _control_files=a_bzrdir._control_files,
1761
1834
                         a_bzrdir=a_bzrdir,
 
1835
                         name=name,
1762
1836
                         _repository=a_bzrdir.open_repository())
1763
1837
 
1764
1838
    def __str__(self):
1779
1853
        """
1780
1854
        return self.get_format_string()
1781
1855
 
1782
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1856
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1783
1857
        """See BranchFormat.open()."""
1784
1858
        if not _found:
1785
 
            format = BranchFormat.find_format(a_bzrdir)
 
1859
            format = BranchFormat.find_format(a_bzrdir, name=name)
1786
1860
            if format.__class__ != self.__class__:
1787
1861
                raise AssertionError("wrong format %r found for %r" %
1788
1862
                    (format, self))
1789
1863
        try:
1790
 
            transport = a_bzrdir.get_branch_transport(None)
 
1864
            transport = a_bzrdir.get_branch_transport(None, name=name)
1791
1865
            control_files = lockable_files.LockableFiles(transport, 'lock',
1792
1866
                                                         lockdir.LockDir)
1793
1867
            return self._branch_class()(_format=self,
1794
1868
                              _control_files=control_files,
 
1869
                              name=name,
1795
1870
                              a_bzrdir=a_bzrdir,
1796
1871
                              _repository=a_bzrdir.find_repository(),
1797
1872
                              ignore_fallbacks=ignore_fallbacks)
1831
1906
        """See BranchFormat.get_format_description()."""
1832
1907
        return "Branch format 5"
1833
1908
 
1834
 
    def initialize(self, a_bzrdir):
 
1909
    def initialize(self, a_bzrdir, name=None):
1835
1910
        """Create a branch of this format in a_bzrdir."""
1836
1911
        utf8_files = [('revision-history', ''),
1837
1912
                      ('branch-name', ''),
1838
1913
                      ]
1839
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
1914
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1840
1915
 
1841
1916
    def supports_tags(self):
1842
1917
        return False
1864
1939
        """See BranchFormat.get_format_description()."""
1865
1940
        return "Branch format 6"
1866
1941
 
1867
 
    def initialize(self, a_bzrdir):
 
1942
    def initialize(self, a_bzrdir, name=None):
1868
1943
        """Create a branch of this format in a_bzrdir."""
1869
1944
        utf8_files = [('last-revision', '0 null:\n'),
1870
1945
                      ('branch.conf', ''),
1871
1946
                      ('tags', ''),
1872
1947
                      ]
1873
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
1948
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1874
1949
 
1875
1950
    def make_tags(self, branch):
1876
1951
        """See bzrlib.branch.BranchFormat.make_tags()."""
1894
1969
        """See BranchFormat.get_format_description()."""
1895
1970
        return "Branch format 8"
1896
1971
 
1897
 
    def initialize(self, a_bzrdir):
 
1972
    def initialize(self, a_bzrdir, name=None):
1898
1973
        """Create a branch of this format in a_bzrdir."""
1899
1974
        utf8_files = [('last-revision', '0 null:\n'),
1900
1975
                      ('branch.conf', ''),
1901
1976
                      ('tags', ''),
1902
1977
                      ('references', '')
1903
1978
                      ]
1904
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
1979
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1905
1980
 
1906
1981
    def __init__(self):
1907
1982
        super(BzrBranchFormat8, self).__init__()
1930
2005
    This format was introduced in bzr 1.6.
1931
2006
    """
1932
2007
 
1933
 
    def initialize(self, a_bzrdir):
 
2008
    def initialize(self, a_bzrdir, name=None):
1934
2009
        """Create a branch of this format in a_bzrdir."""
1935
2010
        utf8_files = [('last-revision', '0 null:\n'),
1936
2011
                      ('branch.conf', ''),
1937
2012
                      ('tags', ''),
1938
2013
                      ]
1939
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2014
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1940
2015
 
1941
2016
    def _branch_class(self):
1942
2017
        return BzrBranch7
1984
2059
        transport = a_bzrdir.get_branch_transport(None)
1985
2060
        location = transport.put_bytes('location', to_branch.base)
1986
2061
 
1987
 
    def initialize(self, a_bzrdir, target_branch=None):
 
2062
    def initialize(self, a_bzrdir, name=None, target_branch=None):
1988
2063
        """Create a branch of this format in a_bzrdir."""
1989
2064
        if target_branch is None:
1990
2065
            # this format does not implement branch itself, thus the implicit
1991
2066
            # creation contract must see it as uninitializable
1992
2067
            raise errors.UninitializableFormat(self)
1993
2068
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
1994
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
2069
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1995
2070
        branch_transport.put_bytes('location',
1996
2071
            target_branch.bzrdir.root_transport.base)
1997
2072
        branch_transport.put_bytes('format', self.get_format_string())
1998
2073
        return self.open(
1999
 
            a_bzrdir, _found=True,
 
2074
            a_bzrdir, name, _found=True,
2000
2075
            possible_transports=[target_branch.bzrdir.root_transport])
2001
2076
 
2002
2077
    def __init__(self):
2009
2084
        def clone(to_bzrdir, revision_id=None,
2010
2085
            repository_policy=None):
2011
2086
            """See Branch.clone()."""
2012
 
            return format.initialize(to_bzrdir, a_branch)
 
2087
            return format.initialize(to_bzrdir, target_branch=a_branch)
2013
2088
            # cannot obey revision_id limits when cloning a reference ...
2014
2089
            # FIXME RBC 20060210 either nuke revision_id for clone, or
2015
2090
            # emit some sort of warning/error to the caller ?!
2016
2091
        return clone
2017
2092
 
2018
 
    def open(self, a_bzrdir, _found=False, location=None,
 
2093
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2019
2094
             possible_transports=None, ignore_fallbacks=False):
2020
2095
        """Return the branch that the branch reference in a_bzrdir points at.
2021
2096
 
2022
2097
        :param a_bzrdir: A BzrDir that contains a branch.
 
2098
        :param name: Name of colocated branch to open, if any
2023
2099
        :param _found: a private parameter, do not use it. It is used to
2024
2100
            indicate if format probing has already be done.
2025
2101
        :param ignore_fallbacks: when set, no fallback branches will be opened
2030
2106
        :param possible_transports: An optional reusable transports list.
2031
2107
        """
2032
2108
        if not _found:
2033
 
            format = BranchFormat.find_format(a_bzrdir)
 
2109
            format = BranchFormat.find_format(a_bzrdir, name=name)
2034
2110
            if format.__class__ != self.__class__:
2035
2111
                raise AssertionError("wrong format %r found for %r" %
2036
2112
                    (format, self))
2038
2114
            location = self.get_reference(a_bzrdir)
2039
2115
        real_bzrdir = bzrdir.BzrDir.open(
2040
2116
            location, possible_transports=possible_transports)
2041
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
 
2117
        result = real_bzrdir.open_branch(name=name, 
 
2118
            ignore_fallbacks=ignore_fallbacks)
2042
2119
        # this changes the behaviour of result.clone to create a new reference
2043
2120
        # rather than a copy of the content of the branch.
2044
2121
        # I did not use a proxy object because that needs much more extensive
2090
2167
    :ivar repository: Repository for this branch.
2091
2168
    :ivar base: The url of the base directory for this branch; the one
2092
2169
        containing the .bzr directory.
 
2170
    :ivar name: Optional colocated branch name as it exists in the control
 
2171
        directory.
2093
2172
    """
2094
2173
 
2095
2174
    def __init__(self, _format=None,
2096
 
                 _control_files=None, a_bzrdir=None, _repository=None,
2097
 
                 ignore_fallbacks=False):
 
2175
                 _control_files=None, a_bzrdir=None, name=None,
 
2176
                 _repository=None, ignore_fallbacks=False):
2098
2177
        """Create new branch object at a particular location."""
2099
2178
        if a_bzrdir is None:
2100
2179
            raise ValueError('a_bzrdir must be supplied')
2101
2180
        else:
2102
2181
            self.bzrdir = a_bzrdir
2103
2182
        self._base = self.bzrdir.transport.clone('..').base
 
2183
        self.name = name
2104
2184
        # XXX: We should be able to just do
2105
2185
        #   self.base = self.bzrdir.root_transport.base
2106
2186
        # but this does not quite work yet -- mbp 20080522
2113
2193
        Branch.__init__(self)
2114
2194
 
2115
2195
    def __str__(self):
2116
 
        return '%s(%r)' % (self.__class__.__name__, self.base)
 
2196
        if self.name is None:
 
2197
            return '%s(%r)' % (self.__class__.__name__, self.base)
 
2198
        else:
 
2199
            return '%s(%r,%r)' % (self.__class__.__name__, self.base, self.name)
2117
2200
 
2118
2201
    __repr__ = __str__
2119
2202