|
|
# Copyright (C) 2010-2023 RhodeCode GmbH
|
|
|
#
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
# it under the terms of the GNU Affero General Public License, version 3
|
|
|
# (only), as published by the Free Software Foundation.
|
|
|
#
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
# GNU General Public License for more details.
|
|
|
#
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
#
|
|
|
# This program is dual-licensed. If you wish to learn more about the
|
|
|
# RhodeCode Enterprise Edition, including its added features, Support services,
|
|
|
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
|
|
|
|
|
"""
|
|
|
Modified mercurial DAG graph functions that re-uses VCS structure
|
|
|
|
|
|
It allows to have a shared codebase for DAG generation for hg, git, svn repos
|
|
|
"""
|
|
|
|
|
|
nullrev = -1
|
|
|
|
|
|
|
|
|
def grandparent(parent_idx_func, lowest_idx, roots, head):
|
|
|
"""
|
|
|
Return all ancestors of head in roots which commit is
|
|
|
greater or equal to lowest_idx.
|
|
|
"""
|
|
|
pending = {head}
|
|
|
seen = set()
|
|
|
kept = set()
|
|
|
llowestrev = max(nullrev, lowest_idx)
|
|
|
while pending:
|
|
|
r = pending.pop()
|
|
|
if r >= llowestrev and r not in seen:
|
|
|
if r in roots:
|
|
|
kept.add(r)
|
|
|
else:
|
|
|
pending.update(parent_idx_func(r))
|
|
|
seen.add(r)
|
|
|
return sorted(kept)
|
|
|
|
|
|
|
|
|
def _dagwalker(repo, commits):
|
|
|
if not commits:
|
|
|
return
|
|
|
|
|
|
def get_parent_indexes(idx):
|
|
|
return [commit.idx for commit in repo[idx].parents]
|
|
|
|
|
|
indexes = [commit['idx'] for commit in commits]
|
|
|
lowest_idx = min(indexes)
|
|
|
known_indexes = set(indexes)
|
|
|
|
|
|
grandparnet_cache = {}
|
|
|
for commit in commits:
|
|
|
parents = sorted(set([p['idx'] for p in commit['parents']
|
|
|
if p['idx'] in known_indexes]))
|
|
|
mpars = [p['idx'] for p in commit['parents'] if
|
|
|
p['idx'] != nullrev and p['idx'] not in parents]
|
|
|
for mpar in mpars:
|
|
|
gp = grandparnet_cache.get(mpar)
|
|
|
if gp is None:
|
|
|
gp = grandparnet_cache[mpar] = grandparent(
|
|
|
get_parent_indexes, lowest_idx, indexes, mpar)
|
|
|
if not gp:
|
|
|
parents.append(mpar)
|
|
|
else:
|
|
|
parents.extend(g for g in gp if g not in parents)
|
|
|
|
|
|
yield (commit['raw_id'], commit['idx'], parents, commit['branch'])
|
|
|
|
|
|
|
|
|
def _colored(dag):
|
|
|
"""annotates a DAG with colored edge information
|
|
|
|
|
|
For each DAG node this function emits tuples::
|
|
|
|
|
|
((col, color), [(col, nextcol, color)])
|
|
|
|
|
|
with the following new elements:
|
|
|
|
|
|
- Tuple (col, color) with column and color index for the current node
|
|
|
- A list of tuples indicating the edges between the current node and its
|
|
|
parents.
|
|
|
"""
|
|
|
seen = []
|
|
|
colors = {}
|
|
|
newcolor = 1
|
|
|
|
|
|
for commit_id, commit_idx, parents, branch in dag:
|
|
|
|
|
|
# Compute seen and next_
|
|
|
if commit_idx not in seen:
|
|
|
seen.append(commit_idx) # new head
|
|
|
colors[commit_idx] = newcolor
|
|
|
newcolor += 1
|
|
|
|
|
|
col = seen.index(commit_idx)
|
|
|
color = colors.pop(commit_idx)
|
|
|
next_ = seen[:]
|
|
|
|
|
|
# Add parents to next_
|
|
|
addparents = [p for p in parents if p not in next_]
|
|
|
next_[col:col + 1] = addparents
|
|
|
|
|
|
# Set colors for the parents
|
|
|
for i, p in enumerate(addparents):
|
|
|
if i == 0:
|
|
|
colors[p] = color
|
|
|
else:
|
|
|
colors[p] = newcolor
|
|
|
newcolor += 1
|
|
|
|
|
|
# Add edges to the graph
|
|
|
edges = []
|
|
|
for ecol, eid in enumerate(seen):
|
|
|
if eid in next_:
|
|
|
edges.append((ecol, next_.index(eid), colors[eid]))
|
|
|
elif eid == commit_idx:
|
|
|
total_parents = len(parents)
|
|
|
edges.extend([
|
|
|
(ecol, next_.index(p),
|
|
|
_get_edge_color(p, total_parents, color, colors))
|
|
|
for p in parents])
|
|
|
|
|
|
# Yield and move on
|
|
|
yield (commit_id, (col, color), edges, branch)
|
|
|
seen = next_
|
|
|
|
|
|
|
|
|
def _get_edge_color(parent, total_parents, color, colors):
|
|
|
if total_parents <= 1:
|
|
|
return color
|
|
|
|
|
|
return colors.get(parent, color)
|
|
|
|