~abentley/bzrtools/bzrtools.dev

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