~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: Martin
  • Date: 2011-04-15 21:22:57 UTC
  • mto: This revision was merged to the branch mainline in revision 5797.
  • Revision ID: gzlist@googlemail.com-20110415212257-jgtovwwp4be7egd9
Add release notes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2010 Canonical Ltd
 
1
# Copyright (C) 2007-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
64
64
        ancestry = self.ancestry
65
65
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
66
66
 
67
 
@deprecated_function(deprecated_in((1, 16, 0)))
68
 
def _StackedParentsProvider(*args, **kwargs):
69
 
    return StackedParentsProvider(*args, **kwargs)
70
67
 
71
68
class StackedParentsProvider(object):
72
69
    """A parents provider which stacks (or unions) multiple providers.
1536
1533
            return revs, ghosts
1537
1534
 
1538
1535
 
1539
 
class SearchResult(object):
 
1536
class AbstractSearchResult(object):
 
1537
    """The result of a search, describing a set of keys.
 
1538
    
 
1539
    Search results are typically used as the 'fetch_spec' parameter when
 
1540
    fetching revisions.
 
1541
 
 
1542
    :seealso: AbstractSearch
 
1543
    """
 
1544
 
 
1545
    def get_recipe(self):
 
1546
        """Return a recipe that can be used to replay this search.
 
1547
 
 
1548
        The recipe allows reconstruction of the same results at a later date.
 
1549
 
 
1550
        :return: A tuple of (search_kind_str, *details).  The details vary by
 
1551
            kind of search result.
 
1552
        """
 
1553
        raise NotImplementedError(self.get_recipe)
 
1554
 
 
1555
    def get_network_struct(self):
 
1556
        """Return a tuple that can be transmitted via the HPSS protocol."""
 
1557
        raise NotImplementedError(self.get_network_struct)
 
1558
 
 
1559
    def get_keys(self):
 
1560
        """Return the keys found in this search.
 
1561
 
 
1562
        :return: A set of keys.
 
1563
        """
 
1564
        raise NotImplementedError(self.get_keys)
 
1565
 
 
1566
    def is_empty(self):
 
1567
        """Return false if the search lists 1 or more revisions."""
 
1568
        raise NotImplementedError(self.is_empty)
 
1569
 
 
1570
    def refine(self, seen, referenced):
 
1571
        """Create a new search by refining this search.
 
1572
 
 
1573
        :param seen: Revisions that have been satisfied.
 
1574
        :param referenced: Revision references observed while satisfying some
 
1575
            of this search.
 
1576
        :return: A search result.
 
1577
        """
 
1578
        raise NotImplementedError(self.refine)
 
1579
 
 
1580
 
 
1581
class AbstractSearch(object):
 
1582
    """A search that can be executed, producing a search result.
 
1583
 
 
1584
    :seealso: AbstractSearchResult
 
1585
    """
 
1586
 
 
1587
    def execute(self):
 
1588
        """Construct a network-ready search result from this search description.
 
1589
 
 
1590
        This may take some time to search repositories, etc.
 
1591
 
 
1592
        :return: A search result (an object that implements
 
1593
            AbstractSearchResult's API).
 
1594
        """
 
1595
        raise NotImplementedError(self.execute)
 
1596
 
 
1597
 
 
1598
class SearchResult(AbstractSearchResult):
1540
1599
    """The result of a breadth first search.
1541
1600
 
1542
1601
    A SearchResult provides the ability to reconstruct the search or access a
1557
1616
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1558
1617
        self._keys = frozenset(keys)
1559
1618
 
 
1619
    def __repr__(self):
 
1620
        kind, start_keys, exclude_keys, key_count = self._recipe
 
1621
        if len(start_keys) > 5:
 
1622
            start_keys_repr = repr(list(start_keys)[:5])[:-1] + ', ...]'
 
1623
        else:
 
1624
            start_keys_repr = repr(start_keys)
 
1625
        if len(exclude_keys) > 5:
 
1626
            exclude_keys_repr = repr(list(exclude_keys)[:5])[:-1] + ', ...]'
 
1627
        else:
 
1628
            exclude_keys_repr = repr(exclude_keys)
 
1629
        return '<%s %s:(%s, %s, %d)>' % (self.__class__.__name__,
 
1630
            kind, start_keys_repr, exclude_keys_repr, key_count)
 
1631
 
1560
1632
    def get_recipe(self):
1561
1633
        """Return a recipe that can be used to replay this search.
1562
1634
 
1580
1652
        """
1581
1653
        return self._recipe
1582
1654
 
 
1655
    def get_network_struct(self):
 
1656
        start_keys = ' '.join(self._recipe[1])
 
1657
        stop_keys = ' '.join(self._recipe[2])
 
1658
        count = str(self._recipe[3])
 
1659
        return (self._recipe[0], '\n'.join((start_keys, stop_keys, count)))
 
1660
 
1583
1661
    def get_keys(self):
1584
1662
        """Return the keys found in this search.
1585
1663
 
1617
1695
        return SearchResult(pending_refs, exclude, count, keys)
1618
1696
 
1619
1697
 
1620
 
class PendingAncestryResult(object):
 
1698
class PendingAncestryResult(AbstractSearchResult):
1621
1699
    """A search result that will reconstruct the ancestry for some graph heads.
1622
1700
 
1623
1701
    Unlike SearchResult, this doesn't hold the complete search result in
1634
1712
        self.heads = frozenset(heads)
1635
1713
        self.repo = repo
1636
1714
 
 
1715
    def __repr__(self):
 
1716
        if len(self.heads) > 5:
 
1717
            heads_repr = repr(list(self.heads)[:5])[:-1]
 
1718
            heads_repr += ', <%d more>...]' % (len(self.heads) - 5,)
 
1719
        else:
 
1720
            heads_repr = repr(self.heads)
 
1721
        return '<%s heads:%s repo:%r>' % (
 
1722
            self.__class__.__name__, heads_repr, self.repo)
 
1723
 
1637
1724
    def get_recipe(self):
1638
1725
        """Return a recipe that can be used to replay this search.
1639
1726
 
1647
1734
        """
1648
1735
        return ('proxy-search', self.heads, set(), -1)
1649
1736
 
 
1737
    def get_network_struct(self):
 
1738
        parts = ['ancestry-of']
 
1739
        parts.extend(self.heads)
 
1740
        return parts
 
1741
 
1650
1742
    def get_keys(self):
1651
1743
        """See SearchResult.get_keys.
1652
1744
 
1679
1771
        return PendingAncestryResult(referenced - seen, self.repo)
1680
1772
 
1681
1773
 
 
1774
class EmptySearchResult(AbstractSearchResult):
 
1775
    """An empty search result."""
 
1776
 
 
1777
    def is_empty(self):
 
1778
        return True
 
1779
    
 
1780
 
 
1781
class EverythingResult(AbstractSearchResult):
 
1782
    """A search result that simply requests everything in the repository."""
 
1783
 
 
1784
    def __init__(self, repo):
 
1785
        self._repo = repo
 
1786
 
 
1787
    def __repr__(self):
 
1788
        return '%s(%r)' % (self.__class__.__name__, self._repo)
 
1789
 
 
1790
    def get_recipe(self):
 
1791
        raise NotImplementedError(self.get_recipe)
 
1792
 
 
1793
    def get_network_struct(self):
 
1794
        return ('everything',)
 
1795
 
 
1796
    def get_keys(self):
 
1797
        if 'evil' in debug.debug_flags:
 
1798
            from bzrlib import remote
 
1799
            if isinstance(self._repo, remote.RemoteRepository):
 
1800
                # warn developers (not users) not to do this
 
1801
                trace.mutter_callsite(
 
1802
                    2, "EverythingResult(RemoteRepository).get_keys() is slow.")
 
1803
        return self._repo.all_revision_ids()
 
1804
 
 
1805
    def is_empty(self):
 
1806
        # It's ok for this to wrongly return False: the worst that can happen
 
1807
        # is that RemoteStreamSource will initiate a get_stream on an empty
 
1808
        # repository.  And almost all repositories are non-empty.
 
1809
        return False
 
1810
 
 
1811
    def refine(self, seen, referenced):
 
1812
        heads = set(self._repo.all_revision_ids())
 
1813
        heads.difference_update(seen)
 
1814
        heads.update(referenced)
 
1815
        return PendingAncestryResult(heads, self._repo)
 
1816
 
 
1817
 
 
1818
class EverythingNotInOther(AbstractSearch):
 
1819
    """Find all revisions in that are in one repo but not the other."""
 
1820
 
 
1821
    def __init__(self, to_repo, from_repo, find_ghosts=False):
 
1822
        self.to_repo = to_repo
 
1823
        self.from_repo = from_repo
 
1824
        self.find_ghosts = find_ghosts
 
1825
 
 
1826
    def execute(self):
 
1827
        return self.to_repo.search_missing_revision_ids(
 
1828
            self.from_repo, find_ghosts=self.find_ghosts)
 
1829
 
 
1830
 
 
1831
class NotInOtherForRevs(AbstractSearch):
 
1832
    """Find all revisions missing in one repo for a some specific heads."""
 
1833
 
 
1834
    def __init__(self, to_repo, from_repo, required_ids, if_present_ids=None,
 
1835
            find_ghosts=False):
 
1836
        """Constructor.
 
1837
 
 
1838
        :param required_ids: revision IDs of heads that must be found, or else
 
1839
            the search will fail with NoSuchRevision.  All revisions in their
 
1840
            ancestry not already in the other repository will be included in
 
1841
            the search result.
 
1842
        :param if_present_ids: revision IDs of heads that may be absent in the
 
1843
            source repository.  If present, then their ancestry not already
 
1844
            found in other will be included in the search result.
 
1845
        """
 
1846
        self.to_repo = to_repo
 
1847
        self.from_repo = from_repo
 
1848
        self.find_ghosts = find_ghosts
 
1849
        self.required_ids = required_ids
 
1850
        self.if_present_ids = if_present_ids
 
1851
 
 
1852
    def __repr__(self):
 
1853
        if len(self.required_ids) > 5:
 
1854
            reqd_revs_repr = repr(list(self.required_ids)[:5])[:-1] + ', ...]'
 
1855
        else:
 
1856
            reqd_revs_repr = repr(self.required_ids)
 
1857
        if self.if_present_ids and len(self.if_present_ids) > 5:
 
1858
            ifp_revs_repr = repr(list(self.if_present_ids)[:5])[:-1] + ', ...]'
 
1859
        else:
 
1860
            ifp_revs_repr = repr(self.if_present_ids)
 
1861
 
 
1862
        return "<%s from:%r to:%r find_ghosts:%r req'd:%r if-present:%r>" % (
 
1863
            self.__class__.__name__, self.from_repo, self.to_repo,
 
1864
            self.find_ghosts, reqd_revs_repr, ifp_revs_repr)
 
1865
 
 
1866
    def execute(self):
 
1867
        return self.to_repo.search_missing_revision_ids(
 
1868
            self.from_repo, revision_ids=self.required_ids,
 
1869
            if_present_ids=self.if_present_ids, find_ghosts=self.find_ghosts)
 
1870
 
 
1871
 
1682
1872
def collapse_linear_regions(parent_map):
1683
1873
    """Collapse regions of the graph that are 'linear'.
1684
1874
 
1770
1960
    def merge_sort(self, tip_revision):
1771
1961
        return self._graph.merge_sort((tip_revision,))
1772
1962
 
 
1963
    def add_node(self, revision, parents):
 
1964
        self._graph.add_node((revision,), [(p,) for p in parents])
 
1965
 
1773
1966
 
1774
1967
_counters = [0,0,0,0,0,0,0]
1775
1968
try: