/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: Daniel Watkins
  • Date: 2007-11-06 09:33:05 UTC
  • mfrom: (2967 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2993.
  • Revision ID: d.m.watkins@warwick.ac.uk-20071106093305-zfef3c0jbcvunnuz
Merged bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
91
91
        specialized implementations for particular repository types.  See
92
92
        Repository.get_graph()
93
93
 
94
 
        :param parents_func: an object providing a get_parents call
 
94
        :param parents_provider: An object providing a get_parents call
95
95
            conforming to the behavior of StackedParentsProvider.get_parents
96
96
        """
97
97
        self.get_parents = parents_provider.get_parents
231
231
            order if they need it.
232
232
        """
233
233
        candidate_heads = set(keys)
 
234
        if len(candidate_heads) < 2:
 
235
            return candidate_heads
234
236
        searchers = dict((c, self._make_breadth_first_searcher([c]))
235
237
                          for c in candidate_heads)
236
238
        active_searchers = dict(searchers)
237
239
        # skip over the actual candidate for each searcher
238
240
        for searcher in active_searchers.itervalues():
239
241
            searcher.next()
 
242
        # The common walker finds nodes that are common to two or more of the
 
243
        # input keys, so that we don't access all history when a currently
 
244
        # uncommon search point actually meets up with something behind a
 
245
        # common search point. Common search points do not keep searches
 
246
        # active; they just allow us to make searches inactive without
 
247
        # accessing all history.
 
248
        common_walker = self._make_breadth_first_searcher([])
240
249
        while len(active_searchers) > 0:
 
250
            ancestors = set()
 
251
            # advance searches
 
252
            try:
 
253
                common_walker.next()
 
254
            except StopIteration:
 
255
                # No common points being searched at this time.
 
256
                pass
241
257
            for candidate in active_searchers.keys():
242
258
                try:
243
259
                    searcher = active_searchers[candidate]
247
263
                    # a descendant of another candidate.
248
264
                    continue
249
265
                try:
250
 
                    ancestors = searcher.next()
 
266
                    ancestors.update(searcher.next())
251
267
                except StopIteration:
252
268
                    del active_searchers[candidate]
253
269
                    continue
254
 
                for ancestor in ancestors:
255
 
                    if ancestor in candidate_heads:
256
 
                        candidate_heads.remove(ancestor)
257
 
                        del searchers[ancestor]
258
 
                        if ancestor in active_searchers:
259
 
                            del active_searchers[ancestor]
 
270
            # process found nodes
 
271
            new_common = set()
 
272
            for ancestor in ancestors:
 
273
                if ancestor in candidate_heads:
 
274
                    candidate_heads.remove(ancestor)
 
275
                    del searchers[ancestor]
 
276
                    if ancestor in active_searchers:
 
277
                        del active_searchers[ancestor]
 
278
                # it may meet up with a known common node
 
279
                if ancestor in common_walker.seen:
 
280
                    # some searcher has encountered our known common nodes:
 
281
                    # just stop it
 
282
                    ancestor_set = set([ancestor])
 
283
                    for searcher in searchers.itervalues():
 
284
                        searcher.stop_searching_any(ancestor_set)
 
285
                else:
 
286
                    # or it may have been just reached by all the searchers:
260
287
                    for searcher in searchers.itervalues():
261
288
                        if ancestor not in searcher.seen:
262
289
                            break
263
290
                    else:
264
 
                        # if this revision was seen by all searchers, then it
265
 
                        # is a descendant of all candidates, so we can stop
266
 
                        # searching it, and any seen ancestors
 
291
                        # The final active searcher has just reached this node,
 
292
                        # making it be known as a descendant of all candidates,
 
293
                        # so we can stop searching it, and any seen ancestors
 
294
                        new_common.add(ancestor)
267
295
                        for searcher in searchers.itervalues():
268
296
                            seen_ancestors =\
269
297
                                searcher.find_seen_ancestors(ancestor)
270
298
                            searcher.stop_searching_any(seen_ancestors)
 
299
            common_walker.start_searching(new_common)
271
300
        return candidate_heads
272
301
 
273
302
    def find_unique_lca(self, left_revision, right_revision):
304
333
    def is_ancestor(self, candidate_ancestor, candidate_descendant):
305
334
        """Determine whether a revision is an ancestor of another.
306
335
 
307
 
        There are two possible outcomes: True and False, but there are three
308
 
        possible relationships:
309
 
 
310
 
        a) candidate_ancestor is an ancestor of candidate_descendant
311
 
        b) candidate_ancestor is an descendant of candidate_descendant
312
 
        c) candidate_ancestor is an sibling of candidate_descendant
313
 
 
314
 
        To check for a, we walk from candidate_descendant, looking for
315
 
        candidate_ancestor.
316
 
 
317
 
        To check for b, we walk from candidate_ancestor, looking for
318
 
        candidate_descendant.
319
 
 
320
 
        To make a and b more efficient, we can stop any searches that hit
321
 
        common ancestors.
322
 
 
323
 
        If we exhaust our searches, but neither a or b is true, then c is true.
324
 
 
325
 
        In order to find c efficiently, we must avoid searching from
326
 
        candidate_descendant or candidate_ancestor into common ancestors.  But
327
 
        if we don't search common ancestors at all, we won't know if we hit
328
 
        common ancestors.  So we have a walker for common ancestors.  Note that
329
 
        its searches are not required to terminate in order to determine c to
330
 
        be true.
331
 
        """
332
 
        ancestor_walker = self._make_breadth_first_searcher(
333
 
            [candidate_ancestor])
334
 
        descendant_walker = self._make_breadth_first_searcher(
335
 
            [candidate_descendant])
336
 
        common_walker = self._make_breadth_first_searcher([])
337
 
        active_ancestor = True
338
 
        active_descendant = True
339
 
        while (active_ancestor or active_descendant):
340
 
            new_common = set()
341
 
            if active_descendant:
342
 
                try:
343
 
                    nodes = descendant_walker.next()
344
 
                except StopIteration:
345
 
                    active_descendant = False
346
 
                else:
347
 
                    if candidate_ancestor in nodes:
348
 
                        return True
349
 
                    new_common.update(nodes.intersection(ancestor_walker.seen))
350
 
            if active_ancestor:
351
 
                try:
352
 
                    nodes = ancestor_walker.next()
353
 
                except StopIteration:
354
 
                    active_ancestor = False
355
 
                else:
356
 
                    if candidate_descendant in nodes:
357
 
                        return False
358
 
                    new_common.update(nodes.intersection(
359
 
                        descendant_walker.seen))
360
 
            try:
361
 
                new_common.update(common_walker.next())
362
 
            except StopIteration:
363
 
                pass
364
 
            for walker in (ancestor_walker, descendant_walker):
365
 
                for node in new_common:
366
 
                    c_ancestors = walker.find_seen_ancestors(node)
367
 
                    walker.stop_searching_any(c_ancestors)
368
 
                common_walker.start_searching(new_common)
369
 
        return False
 
336
        We answer this using heads() as heads() has the logic to perform the
 
337
        smallest number of parent looksup to determine the ancestral
 
338
        relationship between N revisions.
 
339
        """
 
340
        return set([candidate_descendant]) == self.heads(
 
341
            [candidate_ancestor, candidate_descendant])
 
342
 
 
343
 
 
344
class HeadsCache(object):
 
345
    """A cache of results for graph heads calls."""
 
346
 
 
347
    def __init__(self, graph):
 
348
        self.graph = graph
 
349
        self._heads = {}
 
350
 
 
351
    def heads(self, keys):
 
352
        """Return the heads of keys.
 
353
 
 
354
        This matches the API of Graph.heads(), specifically the return value is
 
355
        a set which can be mutated, and ordering of the input is not preserved
 
356
        in the output.
 
357
 
 
358
        :see also: Graph.heads.
 
359
        :param keys: The keys to calculate heads for.
 
360
        :return: A set containing the heads, which may be mutated without
 
361
            affecting future lookups.
 
362
        """
 
363
        keys = frozenset(keys)
 
364
        try:
 
365
            return set(self._heads[keys])
 
366
        except KeyError:
 
367
            heads = self.graph.heads(keys)
 
368
            self._heads[keys] = heads
 
369
            return set(heads)
 
370
 
 
371
 
 
372
class HeadsCache(object):
 
373
    """A cache of results for graph heads calls."""
 
374
 
 
375
    def __init__(self, graph):
 
376
        self.graph = graph
 
377
        self._heads = {}
 
378
 
 
379
    def heads(self, keys):
 
380
        """Return the heads of keys.
 
381
 
 
382
        :see also: Graph.heads.
 
383
        :param keys: The keys to calculate heads for.
 
384
        :return: A set containing the heads, which may be mutated without
 
385
            affecting future lookups.
 
386
        """
 
387
        keys = frozenset(keys)
 
388
        try:
 
389
            return set(self._heads[keys])
 
390
        except KeyError:
 
391
            heads = self.graph.heads(keys)
 
392
            self._heads[keys] = heads
 
393
            return set(heads)
370
394
 
371
395
 
372
396
class _BreadthFirstSearcher(object):
373
 
    """Parallel search the breadth-first the ancestry of revisions.
 
397
    """Parallel search breadth-first the ancestry of revisions.
374
398
 
375
399
    This class implements the iterator protocol, but additionally
376
400
    1. provides a set of seen ancestors, and