25
25
ctypedef struct PyObject:
28
int PyString_CheckExact(object)
30
int PyObject_RichCompareBool(object, object, int)
33
int PyTuple_CheckExact(object)
28
34
object PyTuple_New(Py_ssize_t n)
29
35
Py_ssize_t PyTuple_GET_SIZE(object t)
30
36
PyObject * PyTuple_GET_ITEM(object t, Py_ssize_t o)
31
37
void PyTuple_SET_ITEM(object t, Py_ssize_t o, object v)
39
int PyList_CheckExact(object)
33
40
Py_ssize_t PyList_GET_SIZE(object l)
34
41
PyObject * PyList_GET_ITEM(object l, Py_ssize_t o)
35
42
int PyList_SetItem(object l, Py_ssize_t o, object l) except -1
108
127
return <_KnownGraphNode>temp_node
111
cdef _KnownGraphNode _get_parent(parents, Py_ssize_t pos):
130
cdef _KnownGraphNode _get_tuple_node(tpl, Py_ssize_t pos):
112
131
cdef PyObject *temp_node
113
cdef _KnownGraphNode node
115
temp_node = PyTuple_GET_ITEM(parents, pos)
133
temp_node = PyTuple_GET_ITEM(tpl, pos)
116
134
return <_KnownGraphNode>temp_node
138
cdef _KnownGraphNode real_node
143
cdef object _sort_list_nodes(object lst_or_tpl, int reverse):
144
"""Sort a list of _KnownGraphNode objects.
146
If lst_or_tpl is a list, it is allowed to mutate in place. It may also
147
just return the input list if everything is already sorted.
149
cdef _KnownGraphNode node1, node2
150
cdef int do_swap, is_tuple
151
cdef Py_ssize_t length
153
is_tuple = PyTuple_CheckExact(lst_or_tpl)
154
if not (is_tuple or PyList_CheckExact(lst_or_tpl)):
155
raise TypeError('lst_or_tpl must be a list or tuple.')
156
length = len(lst_or_tpl)
157
if length == 0 or length == 1:
161
node1 = _get_tuple_node(lst_or_tpl, 0)
162
node2 = _get_tuple_node(lst_or_tpl, 1)
164
node1 = _get_list_node(lst_or_tpl, 0)
165
node2 = _get_list_node(lst_or_tpl, 1)
167
do_swap = PyObject_RichCompareBool(node1.key, node2.key, Py_LT)
169
do_swap = PyObject_RichCompareBool(node2.key, node1.key, Py_LT)
173
return (node2, node1)
175
# Swap 'in-place', since lists are mutable
177
PyList_SetItem(lst_or_tpl, 1, node1)
179
PyList_SetItem(lst_or_tpl, 0, node2)
181
# For all other sizes, we just use 'sorted()'
183
# Note that sorted() is just list(iterable).sort()
184
lst_or_tpl = list(lst_or_tpl)
185
lst_or_tpl.sort(key=get_key, reverse=reverse)
119
189
cdef class _MergeSorter
121
191
cdef class KnownGraph:
216
286
PyList_Append(tails, node)
289
def _find_tips(self):
290
cdef PyObject *temp_node
291
cdef _KnownGraphNode node
296
while PyDict_Next(self._nodes, &pos, NULL, &temp_node):
297
node = <_KnownGraphNode>temp_node
298
if PyList_GET_SIZE(node.children) == 0:
299
PyList_Append(tips, node)
219
302
def _find_gdfo(self):
220
303
cdef _KnownGraphNode node
221
304
cdef _KnownGraphNode child
323
406
if node.parents is not None and PyTuple_GET_SIZE(node.parents) > 0:
324
407
for pos from 0 <= pos < PyTuple_GET_SIZE(node.parents):
325
parent_node = _get_parent(node.parents, pos)
408
parent_node = _get_tuple_node(node.parents, pos)
326
409
last_item = last_item + 1
327
410
if last_item < PyList_GET_SIZE(pending):
328
411
Py_INCREF(parent_node) # SetItem steals a ref
397
480
# We started from the parents, so we don't need to do anymore work
398
481
return topo_order
484
"""Return a reverse topological ordering which is 'stable'.
486
There are a few constraints:
487
1) Reverse topological (all children before all parents)
489
3) 'stable' sorting, so that we get the same result, independent of
490
machine, or extra data.
491
To do this, we use the same basic algorithm as topo_sort, but when we
492
aren't sure what node to access next, we sort them lexicographically.
495
cdef Py_ssize_t pos, last_item
496
cdef _KnownGraphNode node, node2, parent_node
498
tips = self._find_tips()
499
# Split the tips based on prefix
501
for pos from 0 <= pos < PyList_GET_SIZE(tips):
502
node = _get_list_node(tips, pos)
503
if PyString_CheckExact(node.key) or len(node.key) == 1:
507
temp = PyDict_GetItem(prefix_tips, prefix)
509
prefix_tips[prefix] = [node]
511
tip_nodes = <object>temp
512
PyList_Append(tip_nodes, node)
515
for prefix in sorted(prefix_tips):
516
temp = PyDict_GetItem(prefix_tips, prefix)
518
tip_nodes = <object>temp
519
pending = _sort_list_nodes(tip_nodes, 1)
520
last_item = PyList_GET_SIZE(pending) - 1
521
while last_item >= 0:
522
node = _get_list_node(pending, last_item)
523
last_item = last_item - 1
524
if node.parents is None:
527
PyList_Append(result, node.key)
528
# Sorting the parent keys isn't strictly necessary for stable
529
# sorting of a given graph. But it does help minimize the
530
# differences between graphs
531
# For bzr.dev ancestry:
533
# 7.73ms RichCompareBool sort
534
parents = _sort_list_nodes(node.parents, 1)
535
for pos from 0 <= pos < len(parents):
536
if PyTuple_CheckExact(parents):
537
parent_node = _get_tuple_node(parents, pos)
539
parent_node = _get_list_node(parents, pos)
540
# TODO: GraphCycle detection
541
parent_node.seen = parent_node.seen + 1
543
== PyList_GET_SIZE(parent_node.children)):
544
# All children have been processed, queue up this
546
last_item = last_item + 1
547
if last_item < PyList_GET_SIZE(pending):
548
Py_INCREF(parent_node) # SetItem steals a ref
549
PyList_SetItem(pending, last_item, parent_node)
551
PyList_Append(pending, parent_node)
401
555
def merge_sort(self, tip_key):
402
556
"""Compute the merge sorted graph output."""
407
561
# shown a specific impact, yet.
408
562
sorter = _MergeSorter(self, tip_key)
409
563
return sorter.topo_order()
565
def get_parent_keys(self, key):
566
"""Get the parents for a key
568
Returns a list containg the parents keys. If the key is a ghost,
569
None is returned. A KeyError will be raised if the key is not in
572
:param keys: Key to check (eg revision_id)
573
:return: A list of parents
575
return self._nodes[key].parent_keys
577
def get_child_keys(self, key):
578
"""Get the children for a key
580
Returns a list containg the children keys. A KeyError will be raised
581
if the key is not in the graph.
583
:param keys: Key to check (eg revision_id)
584
:return: A list of children
586
return self._nodes[key].child_keys
412
589
cdef class _MergeSortNode:
443
620
self.completed = 0
445
622
def __repr__(self):
446
return '%s(depth:%s rev:%s,%s,%s first:%s seen:%s)' % (self.__class__.__name__,
623
return '%s(%s depth:%s rev:%s,%s,%s first:%s seen:%s)' % (
624
self.__class__.__name__, self.key,
447
625
self.merge_depth,
448
626
self._revno_first, self._revno_second, self._revno_last,
449
627
self.is_first_child, self.seen_by_child)
519
696
ms_node = self._get_ms_node(node)
520
697
ms_node.merge_depth = merge_depth
698
if node.parents is None:
699
raise RuntimeError('ghost nodes should not be pushed'
700
' onto the stack: %s' % (node,))
521
701
if PyTuple_GET_SIZE(node.parents) > 0:
522
parent_node = _get_parent(node.parents, 0)
702
parent_node = _get_tuple_node(node.parents, 0)
523
703
ms_node.left_parent = parent_node
524
ms_node.left_pending_parent = parent_node
704
if parent_node.parents is None: # left-hand ghost
705
ms_node.left_pending_parent = None
706
ms_node.left_parent = None
708
ms_node.left_pending_parent = parent_node
525
709
if PyTuple_GET_SIZE(node.parents) > 1:
526
710
ms_node.pending_parents = []
527
711
for pos from 1 <= pos < PyTuple_GET_SIZE(node.parents):
528
parent_node = _get_parent(node.parents, pos)
712
parent_node = _get_tuple_node(node.parents, pos)
529
713
if parent_node.parents is None: # ghost
531
715
PyList_Append(ms_node.pending_parents, parent_node)