/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
23
23
    revision,
24
24
    trace,
25
25
    )
 
26
from bzrlib.symbol_versioning import deprecated_function, deprecated_in
26
27
 
27
28
STEP_UNIQUE_SEARCHER_EVERY = 5
28
29
 
63
64
        ancestry = self.ancestry
64
65
        return dict((k, ancestry[k]) for k in keys if k in ancestry)
65
66
 
 
67
@deprecated_function(deprecated_in((1, 16, 0)))
 
68
def _StackedParentsProvider(*args, **kwargs):
 
69
    return StackedParentsProvider(*args, **kwargs)
66
70
 
67
71
class StackedParentsProvider(object):
68
72
    """A parents provider which stacks (or unions) multiple providers.
179
183
            self.missing_keys.add(key)
180
184
 
181
185
 
182
 
class CallableToParentsProviderAdapter(object):
183
 
    """A parents provider that adapts any callable to the parents provider API.
184
 
 
185
 
    i.e. it accepts calls to self.get_parent_map and relays them to the
186
 
    callable it was constructed with.
187
 
    """
188
 
 
189
 
    def __init__(self, a_callable):
190
 
        self.callable = a_callable
191
 
 
192
 
    def __repr__(self):
193
 
        return "%s(%r)" % (self.__class__.__name__, self.callable)
194
 
 
195
 
    def get_parent_map(self, keys):
196
 
        return self.callable(keys)
197
 
 
198
 
 
199
186
class Graph(object):
200
187
    """Provide incremental access to revision graphs.
201
188
 
271
258
        right = searchers[1].seen
272
259
        return (left.difference(right), right.difference(left))
273
260
 
274
 
    def find_descendants(self, old_key, new_key):
275
 
        """Find descendants of old_key that are ancestors of new_key."""
276
 
        child_map = self.get_child_map(self._find_descendant_ancestors(
277
 
            old_key, new_key))
278
 
        graph = Graph(DictParentsProvider(child_map))
279
 
        searcher = graph._make_breadth_first_searcher([old_key])
280
 
        list(searcher)
281
 
        return searcher.seen
282
 
 
283
 
    def _find_descendant_ancestors(self, old_key, new_key):
284
 
        """Find ancestors of new_key that may be descendants of old_key."""
285
 
        stop = self._make_breadth_first_searcher([old_key])
286
 
        descendants = self._make_breadth_first_searcher([new_key])
287
 
        for revisions in descendants:
288
 
            old_stop = stop.seen.intersection(revisions)
289
 
            descendants.stop_searching_any(old_stop)
290
 
            seen_stop = descendants.find_seen_ancestors(stop.step())
291
 
            descendants.stop_searching_any(seen_stop)
292
 
        return descendants.seen.difference(stop.seen)
293
 
 
294
 
    def get_child_map(self, keys):
295
 
        """Get a mapping from parents to children of the specified keys.
296
 
 
297
 
        This is simply the inversion of get_parent_map.  Only supplied keys
298
 
        will be discovered as children.
299
 
        :return: a dict of key:child_list for keys.
300
 
        """
301
 
        parent_map = self._parents_provider.get_parent_map(keys)
302
 
        parent_child = {}
303
 
        for child, parents in sorted(parent_map.items()):
304
 
            for parent in parents:
305
 
                parent_child.setdefault(parent, []).append(child)
306
 
        return parent_child
307
 
 
308
261
    def find_distance_to_null(self, target_revision_id, known_revision_ids):
309
262
        """Find the left-hand distance to the NULL_REVISION.
310
263
 
909
862
                stop.add(parent_id)
910
863
        return found
911
864
 
912
 
    def find_lefthand_merger(self, merged_key, tip_key):
913
 
        """Find the first lefthand ancestor of tip_key that merged merged_key.
914
 
 
915
 
        We do this by first finding the descendants of merged_key, then
916
 
        walking through the lefthand ancestry of tip_key until we find a key
917
 
        that doesn't descend from merged_key.  Its child is the key that
918
 
        merged merged_key.
919
 
 
920
 
        :return: The first lefthand ancestor of tip_key to merge merged_key.
921
 
            merged_key if it is a lefthand ancestor of tip_key.
922
 
            None if no ancestor of tip_key merged merged_key.
923
 
        """
924
 
        descendants = self.find_descendants(merged_key, tip_key)
925
 
        candidate_iterator = self.iter_lefthand_ancestry(tip_key)
926
 
        last_candidate = None
927
 
        for candidate in candidate_iterator:
928
 
            if candidate not in descendants:
929
 
                return last_candidate
930
 
            last_candidate = candidate
931
 
 
932
865
    def find_unique_lca(self, left_revision, right_revision,
933
866
                        count_steps=False):
934
867
        """Find a unique LCA.
986
919
                yield (ghost, None)
987
920
            pending = next_pending
988
921
 
989
 
    def iter_lefthand_ancestry(self, start_key, stop_keys=None):
990
 
        if stop_keys is None:
991
 
            stop_keys = ()
992
 
        next_key = start_key
993
 
        def get_parents(key):
994
 
            try:
995
 
                return self._parents_provider.get_parent_map([key])[key]
996
 
            except KeyError:
997
 
                raise errors.RevisionNotPresent(next_key, self)
998
 
        while True:
999
 
            if next_key in stop_keys:
1000
 
                return
1001
 
            parents = get_parents(next_key)
1002
 
            yield next_key
1003
 
            if len(parents) == 0:
1004
 
                return
1005
 
            else:
1006
 
                next_key = parents[0]
1007
 
 
1008
922
    def iter_topo_order(self, revisions):
1009
923
        """Iterate through the input revisions in topological order.
1010
924
 
1549
1463
            return revs, ghosts
1550
1464
 
1551
1465
 
1552
 
class AbstractSearchResult(object):
1553
 
    """The result of a search, describing a set of keys.
1554
 
    
1555
 
    Search results are typically used as the 'fetch_spec' parameter when
1556
 
    fetching revisions.
1557
 
 
1558
 
    :seealso: AbstractSearch
1559
 
    """
1560
 
 
1561
 
    def get_recipe(self):
1562
 
        """Return a recipe that can be used to replay this search.
1563
 
 
1564
 
        The recipe allows reconstruction of the same results at a later date.
1565
 
 
1566
 
        :return: A tuple of (search_kind_str, *details).  The details vary by
1567
 
            kind of search result.
1568
 
        """
1569
 
        raise NotImplementedError(self.get_recipe)
1570
 
 
1571
 
    def get_network_struct(self):
1572
 
        """Return a tuple that can be transmitted via the HPSS protocol."""
1573
 
        raise NotImplementedError(self.get_network_struct)
1574
 
 
1575
 
    def get_keys(self):
1576
 
        """Return the keys found in this search.
1577
 
 
1578
 
        :return: A set of keys.
1579
 
        """
1580
 
        raise NotImplementedError(self.get_keys)
1581
 
 
1582
 
    def is_empty(self):
1583
 
        """Return false if the search lists 1 or more revisions."""
1584
 
        raise NotImplementedError(self.is_empty)
1585
 
 
1586
 
    def refine(self, seen, referenced):
1587
 
        """Create a new search by refining this search.
1588
 
 
1589
 
        :param seen: Revisions that have been satisfied.
1590
 
        :param referenced: Revision references observed while satisfying some
1591
 
            of this search.
1592
 
        :return: A search result.
1593
 
        """
1594
 
        raise NotImplementedError(self.refine)
1595
 
 
1596
 
 
1597
 
class AbstractSearch(object):
1598
 
    """A search that can be executed, producing a search result.
1599
 
 
1600
 
    :seealso: AbstractSearchResult
1601
 
    """
1602
 
 
1603
 
    def execute(self):
1604
 
        """Construct a network-ready search result from this search description.
1605
 
 
1606
 
        This may take some time to search repositories, etc.
1607
 
 
1608
 
        :return: A search result (an object that implements
1609
 
            AbstractSearchResult's API).
1610
 
        """
1611
 
        raise NotImplementedError(self.execute)
1612
 
 
1613
 
 
1614
 
class SearchResult(AbstractSearchResult):
 
1466
class SearchResult(object):
1615
1467
    """The result of a breadth first search.
1616
1468
 
1617
1469
    A SearchResult provides the ability to reconstruct the search or access a
1632
1484
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1633
1485
        self._keys = frozenset(keys)
1634
1486
 
1635
 
    def __repr__(self):
1636
 
        kind, start_keys, exclude_keys, key_count = self._recipe
1637
 
        if len(start_keys) > 5:
1638
 
            start_keys_repr = repr(list(start_keys)[:5])[:-1] + ', ...]'
1639
 
        else:
1640
 
            start_keys_repr = repr(start_keys)
1641
 
        if len(exclude_keys) > 5:
1642
 
            exclude_keys_repr = repr(list(exclude_keys)[:5])[:-1] + ', ...]'
1643
 
        else:
1644
 
            exclude_keys_repr = repr(exclude_keys)
1645
 
        return '<%s %s:(%s, %s, %d)>' % (self.__class__.__name__,
1646
 
            kind, start_keys_repr, exclude_keys_repr, key_count)
1647
 
 
1648
1487
    def get_recipe(self):
1649
1488
        """Return a recipe that can be used to replay this search.
1650
1489
 
1668
1507
        """
1669
1508
        return self._recipe
1670
1509
 
1671
 
    def get_network_struct(self):
1672
 
        start_keys = ' '.join(self._recipe[1])
1673
 
        stop_keys = ' '.join(self._recipe[2])
1674
 
        count = str(self._recipe[3])
1675
 
        return (self._recipe[0], '\n'.join((start_keys, stop_keys, count)))
1676
 
 
1677
1510
    def get_keys(self):
1678
1511
        """Return the keys found in this search.
1679
1512
 
1711
1544
        return SearchResult(pending_refs, exclude, count, keys)
1712
1545
 
1713
1546
 
1714
 
class PendingAncestryResult(AbstractSearchResult):
 
1547
class PendingAncestryResult(object):
1715
1548
    """A search result that will reconstruct the ancestry for some graph heads.
1716
1549
 
1717
1550
    Unlike SearchResult, this doesn't hold the complete search result in
1728
1561
        self.heads = frozenset(heads)
1729
1562
        self.repo = repo
1730
1563
 
1731
 
    def __repr__(self):
1732
 
        if len(self.heads) > 5:
1733
 
            heads_repr = repr(list(self.heads)[:5])[:-1]
1734
 
            heads_repr += ', <%d more>...]' % (len(self.heads) - 5,)
1735
 
        else:
1736
 
            heads_repr = repr(self.heads)
1737
 
        return '<%s heads:%s repo:%r>' % (
1738
 
            self.__class__.__name__, heads_repr, self.repo)
1739
 
 
1740
1564
    def get_recipe(self):
1741
1565
        """Return a recipe that can be used to replay this search.
1742
1566
 
1750
1574
        """
1751
1575
        return ('proxy-search', self.heads, set(), -1)
1752
1576
 
1753
 
    def get_network_struct(self):
1754
 
        parts = ['ancestry-of']
1755
 
        parts.extend(self.heads)
1756
 
        return parts
1757
 
 
1758
1577
    def get_keys(self):
1759
1578
        """See SearchResult.get_keys.
1760
1579
 
1787
1606
        return PendingAncestryResult(referenced - seen, self.repo)
1788
1607
 
1789
1608
 
1790
 
class EmptySearchResult(AbstractSearchResult):
1791
 
    """An empty search result."""
1792
 
 
1793
 
    def is_empty(self):
1794
 
        return True
1795
 
    
1796
 
 
1797
 
class EverythingResult(AbstractSearchResult):
1798
 
    """A search result that simply requests everything in the repository."""
1799
 
 
1800
 
    def __init__(self, repo):
1801
 
        self._repo = repo
1802
 
 
1803
 
    def __repr__(self):
1804
 
        return '%s(%r)' % (self.__class__.__name__, self._repo)
1805
 
 
1806
 
    def get_recipe(self):
1807
 
        raise NotImplementedError(self.get_recipe)
1808
 
 
1809
 
    def get_network_struct(self):
1810
 
        return ('everything',)
1811
 
 
1812
 
    def get_keys(self):
1813
 
        if 'evil' in debug.debug_flags:
1814
 
            from bzrlib import remote
1815
 
            if isinstance(self._repo, remote.RemoteRepository):
1816
 
                # warn developers (not users) not to do this
1817
 
                trace.mutter_callsite(
1818
 
                    2, "EverythingResult(RemoteRepository).get_keys() is slow.")
1819
 
        return self._repo.all_revision_ids()
1820
 
 
1821
 
    def is_empty(self):
1822
 
        # It's ok for this to wrongly return False: the worst that can happen
1823
 
        # is that RemoteStreamSource will initiate a get_stream on an empty
1824
 
        # repository.  And almost all repositories are non-empty.
1825
 
        return False
1826
 
 
1827
 
    def refine(self, seen, referenced):
1828
 
        heads = set(self._repo.all_revision_ids())
1829
 
        heads.difference_update(seen)
1830
 
        heads.update(referenced)
1831
 
        return PendingAncestryResult(heads, self._repo)
1832
 
 
1833
 
 
1834
 
class EverythingNotInOther(AbstractSearch):
1835
 
    """Find all revisions in that are in one repo but not the other."""
1836
 
 
1837
 
    def __init__(self, to_repo, from_repo, find_ghosts=False):
1838
 
        self.to_repo = to_repo
1839
 
        self.from_repo = from_repo
1840
 
        self.find_ghosts = find_ghosts
1841
 
 
1842
 
    def execute(self):
1843
 
        return self.to_repo.search_missing_revision_ids(
1844
 
            self.from_repo, find_ghosts=self.find_ghosts)
1845
 
 
1846
 
 
1847
 
class NotInOtherForRevs(AbstractSearch):
1848
 
    """Find all revisions missing in one repo for a some specific heads."""
1849
 
 
1850
 
    def __init__(self, to_repo, from_repo, required_ids, if_present_ids=None,
1851
 
            find_ghosts=False):
1852
 
        """Constructor.
1853
 
 
1854
 
        :param required_ids: revision IDs of heads that must be found, or else
1855
 
            the search will fail with NoSuchRevision.  All revisions in their
1856
 
            ancestry not already in the other repository will be included in
1857
 
            the search result.
1858
 
        :param if_present_ids: revision IDs of heads that may be absent in the
1859
 
            source repository.  If present, then their ancestry not already
1860
 
            found in other will be included in the search result.
1861
 
        """
1862
 
        self.to_repo = to_repo
1863
 
        self.from_repo = from_repo
1864
 
        self.find_ghosts = find_ghosts
1865
 
        self.required_ids = required_ids
1866
 
        self.if_present_ids = if_present_ids
1867
 
 
1868
 
    def __repr__(self):
1869
 
        if len(self.required_ids) > 5:
1870
 
            reqd_revs_repr = repr(list(self.required_ids)[:5])[:-1] + ', ...]'
1871
 
        else:
1872
 
            reqd_revs_repr = repr(self.required_ids)
1873
 
        if self.if_present_ids and len(self.if_present_ids) > 5:
1874
 
            ifp_revs_repr = repr(list(self.if_present_ids)[:5])[:-1] + ', ...]'
1875
 
        else:
1876
 
            ifp_revs_repr = repr(self.if_present_ids)
1877
 
 
1878
 
        return "<%s from:%r to:%r find_ghosts:%r req'd:%r if-present:%r>" % (
1879
 
            self.__class__.__name__, self.from_repo, self.to_repo,
1880
 
            self.find_ghosts, reqd_revs_repr, ifp_revs_repr)
1881
 
 
1882
 
    def execute(self):
1883
 
        return self.to_repo.search_missing_revision_ids(
1884
 
            self.from_repo, revision_ids=self.required_ids,
1885
 
            if_present_ids=self.if_present_ids, find_ghosts=self.find_ghosts)
1886
 
 
1887
 
 
1888
1609
def collapse_linear_regions(parent_map):
1889
1610
    """Collapse regions of the graph that are 'linear'.
1890
1611
 
1976
1697
    def merge_sort(self, tip_revision):
1977
1698
        return self._graph.merge_sort((tip_revision,))
1978
1699
 
1979
 
    def add_node(self, revision, parents):
1980
 
        self._graph.add_node((revision,), [(p,) for p in parents])
1981
 
 
1982
1700
 
1983
1701
_counters = [0,0,0,0,0,0,0]
1984
1702
try: