~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to graph.py

  • Committer: Robert Collins
  • Date: 2005-09-13 10:46:27 UTC
  • mto: (147.2.6) (364.1.3 bzrtools)
  • mto: This revision was merged to the branch mainline in revision 324.
  • Revision ID: robertc@robertcollins.net-20050913104627-51f938950a907475
handle inaccessible sibling archives somewhat - note version-0 is still not handled

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