Attachment 'dotlog.py'

Download

   1 # dotlog.py - display revision graph using graphviz DOT
   2 
   3 import sys
   4 import time
   5 
   6 from mercurial.cmdutil import revrange, show_changeset
   7 from mercurial.i18n import _
   8 from mercurial.node import nullid, nullrev, short
   9 from mercurial.util import Abort
  10 
  11 def get_limit(limit_opt):
  12     if limit_opt:
  13         try:
  14             limit = int(limit_opt)
  15         except ValueError:
  16             raise Abort(_("limit must be a positive integer"))
  17         if limit <= 0:
  18             raise Abort(_("limit must be positive"))
  19     else:
  20         limit = sys.maxint
  21     return limit
  22 
  23 def get_revs(repo, rev_opt):
  24     if rev_opt:
  25         revs = revrange(repo, rev_opt)
  26         return (max(revs), min(revs))
  27     else:
  28         return (repo.changelog.count() - 1, 0)
  29 
  30 def best_pair(branch_pop, color_pop):
  31     colors = sorted(color_pop.keys())
  32     thresh = max(branch_pop.itervalues())
  33     ok_branches = set([b for b, p in branch_pop.iteritems() if p >= thresh])
  34     thresh = min(color_pop.itervalues())
  35     ok_colors = set([c for c, p in color_pop.iteritems() if p <= thresh])
  36     prefix = ":"
  37     while True:
  38         for b in ok_branches:
  39             c = colors[(prefix + b).__hash__() % len(colors)]
  40             if c in ok_colors:
  41                 #print branch_pop[b], b, c, color_pop[c]
  42                 return b, c
  43         prefix = str(len(prefix)) + prefix
  44     
  45 def get_colors(numcolors, branch_pop):
  46     colors = {}
  47     color_pop = dict([(i, 0) for i in range(1, numcolors +1)])
  48     while branch_pop:
  49         branch, color = best_pair(branch_pop, color_pop)
  50         colors[branch] = color
  51         # Per-node cost 1, per-branch cost 2
  52         color_pop[color] += branch_pop[branch] + 2
  53         del branch_pop[branch]
  54     #print colors
  55     return colors
  56     
  57 def dotlog(ui, repo, **opts):
  58     """produce a dot file
  59     """
  60     
  61     limit = get_limit(opts["limit"])
  62     (start_rev, stop_rev) = get_revs(repo, opts["rev"])
  63     stop_rev = max(stop_rev, start_rev - limit + 1)
  64     if start_rev == nullrev:
  65         return
  66 
  67     revisions = set(range(start_rev, stop_rev -1, -1))
  68     parents = dict([(r,set()) for r in revisions])
  69     children = dict([(r,set()) for r in revisions])
  70     branch = {}
  71     for rev in revisions:
  72         for p in repo.changelog.parentrevs(rev):
  73             if p in revisions:
  74                 parents[rev].add(p)
  75                 children[p].add(rev)
  76         ctx = repo.changectx(rev)
  77         branch[rev] = ctx.branch()
  78     if opts['elide_simple']:
  79         multiparent = set([r for r in revisions 
  80             if len(parents[r]) != 1])
  81         multichild = set([r for r in revisions 
  82             if len(children[r]) != 1])
  83         todisplay = set([r for r in revisions
  84             if r in multiparent or r in multichild
  85                 or len(parents[r] & multichild)
  86                 or len(children[r] & multiparent)
  87                 or len(set([branch[v] for v in (set([r])|parents[r]|children[r])]))>1])
  88     else:
  89         todisplay = revisions
  90     # Choose branch colours intelligently
  91     # Best to set both of these at once
  92     colorscheme = opts.get('colorscheme', 'set312')
  93     numcolors = opts.get('numcolors', 12)
  94     branch_popularity = {}
  95     for rev in todisplay:
  96         b = branch[rev]
  97         branch_popularity[b] = 1 + branch_popularity.get(b, 0)
  98     branchcolors = get_colors(numcolors, branch_popularity)
  99     #branchcolors = dict([(b, 1 + (n % 12)) for n, b in enumerate(set(branch.values()))])
 100     nodes = []
 101     arcs = []
 102     for rev in todisplay:
 103         node = repo.lookup(rev)
 104         ctx = repo.changectx(rev)
 105         #print rev, 
 106         #print repo.changelog.parentrevs(rev)
 107         day = time.strftime("%Y-%m-%d", time.localtime(ctx.date()[0]))
 108         nodes.append("r%d [ color = %d, label = \"%s\\n%s\\n%s\" ];\n" %
 109             (rev, branchcolors[branch[rev]], day, branch[rev], short(node)))
 110         for p in parents[rev]:
 111             if p in todisplay:
 112                 if branch[p] == branch[rev]:
 113                     if branch[rev] == "default":
 114                         weight = 20
 115                     else:
 116                         weight = 5
 117                     penwidth = 2
 118                 else:
 119                     weight = 1
 120                     penwidth = 0.5
 121                 arcs.append("r%d -> r%d [ weight = %d, style = \"setlinewidth(%d)\" ]\n" % (p, rev, weight, penwidth))
 122             else:
 123                 elided = 0
 124                 while p not in todisplay:
 125                     p = parents[p].copy().pop()
 126                     elided += 1
 127                 arcs.append("r%d -> r%d [ labeldistance = 15, color=\"gray\", style=\"setlinewidth(2)\", label=\"%d elided\", weight = 2 ];\n" % (p, rev, elided))
 128     f = open(opts['output'], "w")
 129     f.write("digraph {\n")
 130     f.write("size = \"7.5,10.5\";\n")
 131     f.write("fontname = \"Helvetica\";\n")
 132     f.write("mclimit = 100.0;\n")
 133     f.write("node [ style = filled, colorscheme=set312, fontname=\"Helvetica\" ];\n")
 134     f.write("edge [ fontname=\"Helvetica\" ];\n")
 135     f.write("".join(nodes))
 136     f.write("".join(arcs))
 137     f.write("}\n")
 138     f.close()
 139 
 140 cmdtable = {
 141     "dotlog":
 142         (dotlog,
 143          [('l', 'limit', '', _('limit number of changes displayed')),
 144           ('r', 'rev', [], _('show the specified revision or range')),
 145           ('e', 'elide-simple', None, _('Elide simple revisions')),
 146           ('o', 'output', '', _('Output file to write'))],
 147          _('hg dotlog [OPTION]...')),
 148 }

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2008-05-06 22:47:10, 5.3 KB) [[attachment:dotlog.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.