graphmod.py
122 lines
| 3.9 KiB
| text/x-python
|
PythonLexer
/ mercurial / graphmod.py
Dirkjan Ochtman
|
r6691 | # Revision graph generator for Mercurial | ||
# | ||||
# Copyright 2008 Dirkjan Ochtman <dirkjan@ochtman.nl> | ||||
# Copyright 2007 Joel Rosdahl <joel@rosdahl.net> | ||||
# | ||||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
# GNU General Public License version 2, incorporated herein by reference. | ||||
Dirkjan Ochtman
|
r6691 | |||
Peter Arrenbrecht
|
r8840 | """supports walking the history as DAGs suitable for graphical output | ||
The most basic format we use is that of:: | ||||
(id, type, data, [parentids]) | ||||
The node and parent ids are arbitrary integers which identify a node in the | ||||
context of the graph returned. Type is a constant specifying the node type. | ||||
Data depends on type. | ||||
""" | ||||
from mercurial.node import nullrev | ||||
CHANGESET = 'C' | ||||
Dirkjan Ochtman
|
r6691 | |||
Peter Arrenbrecht
|
r8836 | def revisions(repo, start, stop): | ||
Peter Arrenbrecht
|
r8840 | """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples | ||
Peter Arrenbrecht
|
r8836 | |||
This generator function walks through the revision history from revision | ||||
Peter Arrenbrecht
|
r8840 | start to revision stop (which must be less than or equal to start). It | ||
returns a tuple for each node. The node and parent ids are arbitrary | ||||
integers which identify a node in the context of the graph returned. | ||||
Peter Arrenbrecht
|
r8836 | """ | ||
cur = start | ||||
while cur >= stop: | ||||
ctx = repo[cur] | ||||
parents = [p.rev() for p in ctx.parents() if p.rev() != nullrev] | ||||
Peter Arrenbrecht
|
r8840 | yield (cur, CHANGESET, ctx, sorted(parents)) | ||
Peter Arrenbrecht
|
r8836 | cur -= 1 | ||
Nicolas Dumazet
|
r10111 | def filerevs(repo, path, start, stop, limit=None): | ||
Peter Arrenbrecht
|
r8840 | """file cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples | ||
Peter Arrenbrecht
|
r8836 | |||
This generator function walks through the revision history of a single | ||||
Peter Arrenbrecht
|
r8842 | file from revision start down to revision stop. | ||
Peter Arrenbrecht
|
r8836 | """ | ||
filerev = len(repo.file(path)) - 1 | ||||
Nicolas Dumazet
|
r10084 | rev = stop + 1 | ||
count = 0 | ||||
while filerev >= 0 and rev > stop: | ||||
Peter Arrenbrecht
|
r8836 | fctx = repo.filectx(path, fileid=filerev) | ||
parents = [f.linkrev() for f in fctx.parents() if f.path() == path] | ||||
Peter Arrenbrecht
|
r8840 | rev = fctx.rev() | ||
if rev <= start: | ||||
Dirkjan Ochtman
|
r9727 | yield (rev, CHANGESET, fctx.changectx(), sorted(parents)) | ||
Nicolas Dumazet
|
r10084 | count += 1 | ||
if count == limit: | ||||
break | ||||
Peter Arrenbrecht
|
r8836 | filerev -= 1 | ||
Peter Arrenbrecht
|
r8837 | def nodes(repo, nodes): | ||
Peter Arrenbrecht
|
r8840 | """cset DAG generator yielding (id, CHANGESET, ctx, [parentids]) tuples | ||
This generator function walks the given nodes. It only returns parents | ||||
that are in nodes, too. | ||||
""" | ||||
Peter Arrenbrecht
|
r8837 | include = set(nodes) | ||
for node in nodes: | ||||
ctx = repo[node] | ||||
parents = [p.rev() for p in ctx.parents() if p.node() in include] | ||||
Peter Arrenbrecht
|
r8840 | yield (ctx.rev(), CHANGESET, ctx, sorted(parents)) | ||
Peter Arrenbrecht
|
r8837 | |||
Peter Arrenbrecht
|
r8842 | def colored(dag): | ||
"""annotates a DAG with colored edge information | ||||
For each DAG node this function emits tuples:: | ||||
Dirkjan Ochtman
|
r6691 | |||
Peter Arrenbrecht
|
r8842 | (id, type, data, (col, color), [(col, nextcol, color)]) | ||
Dirkjan Ochtman
|
r6691 | |||
Peter Arrenbrecht
|
r8842 | with the following new elements: | ||
Peter Arrenbrecht
|
r8835 | - Tuple (col, color) with column and color index for the current node | ||
Peter Arrenbrecht
|
r8842 | - A list of tuples indicating the edges between the current node and its | ||
parents. | ||||
Dirkjan Ochtman
|
r6691 | """ | ||
Peter Arrenbrecht
|
r8841 | seen = [] | ||
Dirkjan Ochtman
|
r6691 | colors = {} | ||
Peter Arrenbrecht
|
r8841 | newcolor = 1 | ||
Peter Arrenbrecht
|
r8842 | for (cur, type, data, parents) in dag: | ||
Dirkjan Ochtman
|
r6691 | |||
Peter Arrenbrecht
|
r8841 | # Compute seen and next | ||
if cur not in seen: | ||||
seen.append(cur) # new head | ||||
colors[cur] = newcolor | ||||
newcolor += 1 | ||||
Dirkjan Ochtman
|
r6691 | |||
Peter Arrenbrecht
|
r8841 | col = seen.index(cur) | ||
color = colors.pop(cur) | ||||
next = seen[:] | ||||
Dirkjan Ochtman
|
r6691 | |||
Peter Arrenbrecht
|
r8842 | # Add parents to next | ||
Dirkjan Ochtman
|
r6691 | addparents = [p for p in parents if p not in next] | ||
Peter Arrenbrecht
|
r8841 | next[col:col + 1] = addparents | ||
Dirkjan Ochtman
|
r6691 | |||
# Set colors for the parents | ||||
for i, p in enumerate(addparents): | ||||
if not i: | ||||
colors[p] = color | ||||
else: | ||||
Peter Arrenbrecht
|
r8841 | colors[p] = newcolor | ||
newcolor += 1 | ||||
Dirkjan Ochtman
|
r6691 | |||
# Add edges to the graph | ||||
edges = [] | ||||
Peter Arrenbrecht
|
r8841 | for ecol, eid in enumerate(seen): | ||
if eid in next: | ||||
edges.append((ecol, next.index(eid), colors[eid])) | ||||
Peter Arrenbrecht
|
r8842 | elif eid == cur: | ||
Dirkjan Ochtman
|
r6691 | for p in parents: | ||
Peter Arrenbrecht
|
r8841 | edges.append((ecol, next.index(p), colors[p])) | ||
Dirkjan Ochtman
|
r6691 | |||
# Yield and move on | ||||
Peter Arrenbrecht
|
r8842 | yield (cur, type, data, (col, color), edges) | ||
Peter Arrenbrecht
|
r8841 | seen = next | ||