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.You are not allowed to attach a file to this page.