~abentley/bzrtools/bzrtools.dev

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
from dotgraph import Node, dot_output, invoke_dot
from bzrlib.branch import Branch
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)
    if len(new_committer) < 2:
        return committer
    return new_committer
    

def graph_ancestry(branch):
    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")

    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)
                counter+=1
                
            try:
                revision = branch.get_revision(rev_id)
            except bzrlib.errors.NoSuchRevision:
                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 = []
    def compact_descendants():
        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    
                
    for key, values in compact_descendants().iteritems():
        for value in values:
            node_relations.append((nodes[key], nodes[value]))
    return node_relations

def write_ancestry_file(branch, file):
    b = Branch(branch)
    relations = graph_ancestry(b)
    invoke_dot(dot_output(relations), file)