/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2007-2010 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)
67
70
 
68
71
class StackedParentsProvider(object):
69
72
    """A parents provider which stacks (or unions) multiple providers.
255
258
        right = searchers[1].seen
256
259
        return (left.difference(right), right.difference(left))
257
260
 
258
 
    def find_descendants(self, old_key, new_key):
259
 
        """Find descendants of old_key that are ancestors of new_key."""
260
 
        child_map = self.get_child_map(self._find_descendant_ancestors(
261
 
            old_key, new_key))
262
 
        graph = Graph(DictParentsProvider(child_map))
263
 
        searcher = graph._make_breadth_first_searcher([old_key])
264
 
        list(searcher)
265
 
        return searcher.seen
266
 
 
267
 
    def _find_descendant_ancestors(self, old_key, new_key):
268
 
        """Find ancestors of new_key that may be descendants of old_key."""
269
 
        stop = self._make_breadth_first_searcher([old_key])
270
 
        descendants = self._make_breadth_first_searcher([new_key])
271
 
        for revisions in descendants:
272
 
            old_stop = stop.seen.intersection(revisions)
273
 
            descendants.stop_searching_any(old_stop)
274
 
            seen_stop = descendants.find_seen_ancestors(stop.step())
275
 
            descendants.stop_searching_any(seen_stop)
276
 
        return descendants.seen.difference(stop.seen)
277
 
 
278
 
    def get_child_map(self, keys):
279
 
        """Get a mapping from parents to children of the specified keys.
280
 
 
281
 
        This is simply the inversion of get_parent_map.  Only supplied keys
282
 
        will be discovered as children.
283
 
        :return: a dict of key:child_list for keys.
284
 
        """
285
 
        parent_map = self._parents_provider.get_parent_map(keys)
286
 
        parent_child = {}
287
 
        for child, parents in sorted(parent_map.items()):
288
 
            for parent in parents:
289
 
                parent_child.setdefault(parent, []).append(child)
290
 
        return parent_child
291
 
 
292
261
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
293
262
        """Find the left-hand distance to the NULL_REVISION.
294
263
 
893
862
                stop.add(parent_id)
894
863
        return found
895
864
 
896
 
    def find_lefthand_merger(self, merged_key, tip_key):
897
 
        """Find the first lefthand ancestor of tip_key that merged merged_key.
898
 
 
899
 
        We do this by first finding the descendants of merged_key, then
900
 
        walking through the lefthand ancestry of tip_key until we find a key
901
 
        that doesn't descend from merged_key.  Its child is the key that
902
 
        merged merged_key.
903
 
 
904
 
        :return: The first lefthand ancestor of tip_key to merge merged_key.
905
 
            merged_key if it is a lefthand ancestor of tip_key.
906
 
            None if no ancestor of tip_key merged merged_key.
907
 
        """
908
 
        descendants = self.find_descendants(merged_key, tip_key)
909
 
        candidate_iterator = self.iter_lefthand_ancestry(tip_key)
910
 
        last_candidate = None
911
 
        for candidate in candidate_iterator:
912
 
            if candidate not in descendants:
913
 
                return last_candidate
914
 
            last_candidate = candidate
915
 
 
916
865
    def find_unique_lca(self, left_revision, right_revision,
917
866
                        count_steps=False):
918
867
        """Find a unique LCA.
970
919
                yield (ghost, None)
971
920
            pending = next_pending
972
921
 
973
 
    def iter_lefthand_ancestry(self, start_key, stop_keys=None):
974
 
        if stop_keys is None:
975
 
            stop_keys = ()
976
 
        next_key = start_key
977
 
        def get_parents(key):
978
 
            try:
979
 
                return self._parents_provider.get_parent_map([key])[key]
980
 
            except KeyError:
981
 
                raise errors.RevisionNotPresent(next_key, self)
982
 
        while True:
983
 
            if next_key in stop_keys:
984
 
                return
985
 
            parents = get_parents(next_key)
986
 
            yield next_key
987
 
            if len(parents) == 0:
988
 
                return
989
 
            else:
990
 
                next_key = parents[0]
991
 
 
992
922
    def iter_topo_order(self, revisions):
993
923
        """Iterate through the input revisions in topological order.
994
924
 
1533
1463
            return revs, ghosts
1534
1464
 
1535
1465
 
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):
 
1466
class SearchResult(object):
1599
1467
    """The result of a breadth first search.
1600
1468
 
1601
1469
    A SearchResult provides the ability to reconstruct the search or access a
1616
1484
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1617
1485
        self._keys = frozenset(keys)
1618
1486
 
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
 
 
1632
1487
    def get_recipe(self):
1633
1488
        """Return a recipe that can be used to replay this search.
1634
1489
 
1652
1507
        """
1653
1508
        return self._recipe
1654
1509
 
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
 
 
1661
1510
    def get_keys(self):
1662
1511
        """Return the keys found in this search.
1663
1512
 
1695
1544
        return SearchResult(pending_refs, exclude, count, keys)
1696
1545
 
1697
1546
 
1698
 
class PendingAncestryResult(AbstractSearchResult):
 
1547
class PendingAncestryResult(object):
1699
1548
    """A search result that will reconstruct the ancestry for some graph heads.
1700
1549
 
1701
1550
    Unlike SearchResult, this doesn't hold the complete search result in
1712
1561
        self.heads = frozenset(heads)
1713
1562
        self.repo = repo
1714
1563
 
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
 
 
1724
1564
    def get_recipe(self):
1725
1565
        """Return a recipe that can be used to replay this search.
1726
1566
 
1734
1574
        """
1735
1575
        return ('proxy-search', self.heads, set(), -1)
1736
1576
 
1737
 
    def get_network_struct(self):
1738
 
        parts = ['ancestry-of']
1739
 
        parts.extend(self.heads)
1740
 
        return parts
1741
 
 
1742
1577
    def get_keys(self):
1743
1578
        """See SearchResult.get_keys.
1744
1579
 
1771
1606
        return PendingAncestryResult(referenced - seen, self.repo)
1772
1607
 
1773
1608
 
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
 
 
1872
1609
def collapse_linear_regions(parent_map):
1873
1610
    """Collapse regions of the graph that are 'linear'.
1874
1611
 
1960
1697
    def merge_sort(self, tip_revision):
1961
1698
        return self._graph.merge_sort((tip_revision,))
1962
1699
 
1963
 
    def add_node(self, revision, parents):
1964
 
        self._graph.add_node((revision,), [(p,) for p in parents])
1965
 
 
1966
1700
 
1967
1701
_counters = [0,0,0,0,0,0,0]
1968
1702
try: