1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
from dotgraph import Node, dot_output, invoke_dot, NoDot
from bzrlib.branch import Branch
from bzrlib.errors import BzrCommandError
import bzrlib.errors
import re
def add_relations(rev_id):
if rev_id in ancestors:
return
print rev_id
if rev_id not in nodes:
nodes[rev_id] = Node("n%d" % counter, label = rev_id)
counter += 1
revision = branch.get_revision(rev_id)
ancestors [rev_id] = []
for p in (p.revision_id for p in revision.parents):
add_relations(p)
if p not in descendants:
descendants[p] = []
descendants[p].append(rev_id)
ancestors [rev_id].append(rev_id)
def short_committer(committer):
new_committer = re.sub('<.*>', '', committer).strip(' ')
if len(new_committer) < 2:
return committer
return new_committer
def compact_descendants(descendants, ancestors):
new_descendants={}
skip = set()
for me, my_descendants in descendants.iteritems():
if me in skip:
continue
new_descendants[me] = []
for descendant in my_descendants:
new_descendant = descendant
while new_descendant in descendants and \
len(ancestors[new_descendant]) == 1 and \
len(descendants[new_descendant]) == 1:
skip.add(new_descendant)
if new_descendant in new_descendants:
del new_descendants[new_descendant]
new_descendant = descendants[new_descendant][0]
new_descendants[me].append(new_descendant)
return new_descendants
def graph_ancestry(branch, collapse=True):
nodes = {}
q = ((i+1, n) for (i, n) in enumerate(branch.revision_history()))
r = 1
for (revno, rev_id) in q:
nodes[rev_id] = Node("R%d" % revno, color="#ffff00", rev_id=rev_id)
ancestors = {}
descendants = {}
counter = 0
lines = [branch.last_patch()]
while len(lines) > 0:
new_lines = set()
for rev_id in lines:
if rev_id not in nodes:
nodes[rev_id] = Node("n%d" % counter, label=rev_id,
rev_id=rev_id)
counter+=1
try:
revision = branch.get_revision(rev_id)
except bzrlib.errors.NoSuchRevision:
nodes[rev_id].node_style.append('dotted')
continue
if nodes[rev_id].committer is None:
nodes[rev_id].committer = short_committer(revision.committer)
parent_ids = [r.revision_id for r in revision.parents]
ancestors [rev_id] = parent_ids
for parent in parent_ids:
if parent not in ancestors:
new_lines.add(parent)
descendants[parent] = []
descendants[parent].append(rev_id)
lines = new_lines
node_relations = []
if collapse:
visible_descendants = compact_descendants(descendants, ancestors)
else:
visible_descendants = descendants
for key, values in visible_descendants.iteritems():
for value in values:
node_relations.append((nodes[key], nodes[value]))
return node_relations
def write_ancestry_file(branch, filename, collapse=True):
b = Branch(branch)
relations = graph_ancestry(b, collapse)
ext = filename.split('.')[-1]
if ext in ('svg', 'svgz', 'gif', 'jpg', 'ps', 'fig', 'mif', 'png'):
try:
invoke_dot(dot_output(relations), filename, ext)
except NoDot, e:
raise BzrCommandError("Can't find 'dot'. Please ensure Graphviz"\
" is installed correctly.")
elif ext=='dot':
file(filename, 'wb').write("".join(list(dot_output(relations))))
else:
print "Unknown file extension: %s" % ext
|