~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to graph.py

  • Committer: Aaron Bentley
  • Date: 2005-06-07 18:52:04 UTC
  • Revision ID: abentley@panoramicfeedback.com-20050607185204-5fc1f0e3d393b909
Added NEWS, obsoleted bzr-pull

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from dotgraph import Node, dot_output, invoke_dot, invoke_dot_aa, NoDot, NoRsvg
2
 
from dotgraph import mail_map
3
 
from bzrlib.branch import Branch
4
 
from bzrlib.errors import BzrCommandError
5
 
import bzrlib.errors
6
 
import re
7
 
import os.path
8
 
 
9
 
mail_map.update({'aaron.bentley@utoronto.ca'     : 'Aaron Bentley',
10
 
                 'abentley@panoramicfeedback.com': 'Aaron Bentley',
11
 
                 'john@arbash-meinel.com'        : 'John A. Meinel',
12
 
                 'mbp@sourcefrog.net'            : 'Martin Pool'
13
 
                })
14
 
 
15
 
def add_relations(rev_id):
16
 
    if rev_id in ancestors:
17
 
        return
18
 
    print rev_id
19
 
    if rev_id not in nodes:
20
 
        nodes[rev_id] = Node("n%d" % counter, label = rev_id)
21
 
        counter += 1
22
 
    revision = branch.get_revision(rev_id)
23
 
    ancestors [rev_id] = []
24
 
    for p in (p.revision_id for p in revision.parents):
25
 
        add_relations(p)
26
 
        if p not in descendants:
27
 
            descendants[p] = []
28
 
        descendants[p].append(rev_id)
29
 
        ancestors [rev_id].append(rev_id)
30
 
 
31
 
def short_committer(committer):
32
 
    new_committer = re.sub('<.*>', '', committer).strip(' ')
33
 
    if len(new_committer) < 2:
34
 
        return committer
35
 
    return new_committer
36
 
 
37
 
def can_skip(rev_id, descendants, ancestors):
38
 
    if rev_id not in descendants:
39
 
        return False
40
 
    elif len(ancestors[rev_id]) != 1:
41
 
        return False
42
 
    elif len(descendants[ancestors[rev_id][0]]) != 1:
43
 
        return False
44
 
    elif len(descendants[rev_id]) != 1:
45
 
        return False
46
 
    else:
47
 
        return True
48
 
 
49
 
def compact_descendants(descendants, ancestors):
50
 
    new_descendants={}
51
 
    skip = set()
52
 
    for me, my_descendants in descendants.iteritems():
53
 
        if me in skip:
54
 
            continue
55
 
        new_descendants[me] = []
56
 
        for descendant in my_descendants:
57
 
            new_descendant = descendant
58
 
            while can_skip(new_descendant, descendants, ancestors):
59
 
                skip.add(new_descendant)
60
 
                if new_descendant in new_descendants:
61
 
                    del new_descendants[new_descendant]
62
 
                new_descendant = descendants[new_descendant][0]
63
 
            new_descendants[me].append(new_descendant)
64
 
    return new_descendants    
65
 
 
66
 
 
67
 
def graph_ancestry(branch, collapse=True):
68
 
    nodes = {}
69
 
    q = ((i+1, n) for (i, n) in enumerate(branch.revision_history()))
70
 
    r = 1
71
 
    try:
72
 
        branch_name = os.path.basename(branch.base)
73
 
    except AttributeError:
74
 
        branch_name = "main"
75
 
    for (revno, rev_id) in q:
76
 
        nodes[rev_id] = Node("R%d" % revno, color="#ffff00", rev_id=rev_id, 
77
 
                             cluster=branch_name)
78
 
 
79
 
    ancestors = {} 
80
 
    descendants = {}
81
 
    counter = 0
82
 
    lines = [branch.last_patch()]
83
 
    while len(lines) > 0:
84
 
        new_lines = set()
85
 
        for rev_id in lines:
86
 
            if rev_id not in nodes:
87
 
                nodes[rev_id] = Node("n%d" % counter, label=rev_id, 
88
 
                                     rev_id=rev_id)
89
 
                counter+=1
90
 
                
91
 
            try:
92
 
                revision = branch.get_revision(rev_id)
93
 
            except bzrlib.errors.NoSuchRevision:
94
 
                nodes[rev_id].node_style.append('dotted')
95
 
                continue
96
 
            if nodes[rev_id].committer is None:
97
 
                nodes[rev_id].committer = short_committer(revision.committer)
98
 
            parent_ids = [r.revision_id for r in revision.parents]
99
 
            ancestors [rev_id] = parent_ids
100
 
            for parent in parent_ids:
101
 
                if parent not in ancestors:
102
 
                    new_lines.add(parent)
103
 
                    descendants[parent] = []
104
 
                descendants[parent].append(rev_id)
105
 
        lines = new_lines
106
 
    node_relations = []
107
 
 
108
 
    if collapse:
109
 
        visible_descendants = compact_descendants(descendants, ancestors)
110
 
    else:
111
 
        visible_descendants = descendants
112
 
                
113
 
    for key, values in visible_descendants.iteritems():
114
 
        for value in values:
115
 
            node_relations.append((nodes[key], nodes[value]))
116
 
    return node_relations
117
 
 
118
 
def write_ancestry_file(branch, filename, collapse=True, antialias=True):
119
 
    b = Branch(branch)
120
 
    relations = graph_ancestry(b, collapse)
121
 
    ext = filename.split('.')[-1]
122
 
    if antialias and ext in ('png', 'jpg'):
123
 
        try:
124
 
            invoke_dot_aa(dot_output(relations), filename, ext)
125
 
        except NoDot, e:
126
 
            raise BzrCommandError("Can't find 'dot'.  Please ensure Graphviz"\
127
 
                " is installed correctly.")
128
 
        except NoRsvg, e:
129
 
            raise BzrCommandError("Can't find 'rsvg'.  Please ensure "\
130
 
                "librsvg-bin is installed correctly, or use --noantialias.")
131
 
    elif ext in ('svg', 'svgz', 'gif', 'jpg', 'ps', 'fig', 'mif', 'png'):
132
 
        try:
133
 
            invoke_dot(dot_output(relations), filename, ext)
134
 
        except NoDot, e:
135
 
            raise BzrCommandError("Can't find 'dot'.  Please ensure Graphviz"\
136
 
                " is installed correctly, or use --noantialias")
137
 
    elif ext=='dot':
138
 
        file(filename, 'wb').write("".join(list(dot_output(relations))))
139
 
    else:
140
 
        print "Unknown file extension: %s" % ext
141