max
max

Reputation: 52343

Visualization of Mercurial revision tree

What's the easiest way to produce a visualization of Mercurial revision tree using Python (ideally, Python 3)?

I am guessing it would have to be through a combination 2 libraries: one that provides an interface to the Mercurial repository, and one that visualizes graphs.

Use case: we have written a (pure Python) continuous integration testing module. We'd like it to display the revision tree, marking each node as "passed", "failed", "in progress", "not tested" or something along these lines.

Upvotes: 0

Views: 1000

Answers (2)

Jonas Kölker
Jonas Kölker

Reputation: 7837

Here, have my script:

#!/usr/bin/env python
from itertools import chain
import hglib
#from pygraphviz import *

repo = hglib.open('.')

log = dict((t[0], t) for t in repo.log())
def graph(): return dict((v, []) for v in log)

forward, backward, jump = (graph() for i in range(3))

for target in log:
    for parent in repo.parents(target) or []:
        source = parent[0]
        forward[source].append(target)
        backward[target].append(source)

def endpoint(v):
    if len(forward[v]) != 1: return True
    if len(backward[forward[v][0]]) != 1: return True
    if len(backward[v]) != 1: return True
    if len(forward[backward[v][0]]) != 1: return True

for v in forward:
    if endpoint(v):
        w = v
        while len(forward[w]) == 1 == len(backward[forward[w][0]]):
            w = forward[w][0]
        jump[v] = w
    else: del jump[v]

def vertex(tupl): return 'v' + tupl[1][:5]

print 'digraph {'

colors = ['red', 'green', 'blue', 'yellow', 'cyan', 'magenta',
          'orange', 'chartreuse']
authors = dict()

for v in sorted(forward, key=int):
    if not endpoint(v) or v not in jump: continue
    node = 'v%s' % v
    if jump[v] != v:
        sep = '+' if forward[v] == jump[v] else '::'
        label = '%s%s%s' % (v, sep, jump[v])
        assert int(jump[v]) > int(v)
        v = jump[v]
        del jump[v]
    else:
        label = v
    author = log[v][4]
    print '// %s' % author
    if author not in authors: authors[author] = colors[len(authors) %
                                                       len(colors)]
    attrs = dict(color=authors[author], label=label, style='bold').items()
    print '\t%s [%s]' % (node, ','.join('%s="%s"' % kv for kv in attrs))
    for w in forward[v]: print '\t%s -> v%s' % (node, w)

print '}'

As you can tell, I use hglib to get at the Mercurial data (hglib.open('.').log() is a list of tuples, one per commit). I use graphviz for visualization. No library, I just *print* the damn thing ;-)

I run the script plus graphviz like this:

python version-dag.py > log.dot ; dot -Tpdf log.dot -o log.pdf

... and then look at the glorious .pdf file. Graphviz can do png, eps and perhaps other formats. The jump dictionary is for compression single-parent-paths down to a single node. Enjoy :)

Upvotes: 1

edallme
edallme

Reputation: 934

For the visualization part, I would check out NetworkX. It is a Python library that lets you do graph/network processing, import/export, and visualization.

Upvotes: 1

Related Questions