82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
# Copyright (c) 2013-2016 Hewlett-Packard Development Company, L.P.
|
|
# Copyright (c) 2018 Hewlett Packard Enterprise Development Company LP
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import re
|
|
|
|
|
|
def get_node_to_pick(node):
|
|
m = re.search(r'(.*)(\d+)$', node)
|
|
if m:
|
|
# get copy of a another change
|
|
node_number = int(m.group(2)) - 1
|
|
node_name = m.group(1)
|
|
if node_number > 0:
|
|
node_name += str(node_number)
|
|
return node_name
|
|
return None
|
|
|
|
|
|
_NOT_VISITED = 0
|
|
_VISITED = 1
|
|
_FINISHED = 2
|
|
|
|
|
|
def reverse_toposort(data):
|
|
|
|
# convert to dict for linear lookup times when returning
|
|
data = dict(data)
|
|
|
|
# keep track of nodes visited and processed
|
|
# by checking if a child has been visited before but not processed you
|
|
# can detect a back edge and abort since the graph is not acyclic
|
|
visited = dict()
|
|
|
|
# DFS algorithm with customization to handle use of '=' notation for merge
|
|
# commits and also the additional dependency for cherry-picking
|
|
nodes_to_visit = []
|
|
for i in data.keys():
|
|
if i not in visited:
|
|
nodes_to_visit.append(i)
|
|
|
|
while nodes_to_visit:
|
|
node = nodes_to_visit.pop()
|
|
if visited.get(node) is _VISITED:
|
|
# already visited so just return it with it's deps
|
|
yield (node, data[node])
|
|
visited[node] = _FINISHED
|
|
continue
|
|
elif visited.get(node) is _FINISHED:
|
|
continue
|
|
|
|
visited[node] = _VISITED
|
|
nodes_to_visit.append(node)
|
|
# special case for cherry-picking changes
|
|
c_node = get_node_to_pick(node)
|
|
if c_node and c_node not in visited:
|
|
nodes_to_visit.append(c_node)
|
|
|
|
for d in data[node]:
|
|
r_d = d.strip('=')
|
|
if r_d not in visited:
|
|
nodes_to_visit.append(r_d)
|
|
else:
|
|
# if we've already visited a dep but not processed it,
|
|
# then we have a back edge of some kind
|
|
if visited[r_d] is _VISITED:
|
|
message = ("Graph is not acyclic: %s is a dependency "
|
|
"of %s, but has been visited without being "
|
|
"processed before it." % (r_d, node))
|
|
raise RuntimeError(message)
|