/b-gtk/fix-viz

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz

« back to all changes in this revision

Viewing changes to branchview/linegraph.py

  • Committer: Jelmer Vernooij
  • Date: 2008-06-29 19:18:34 UTC
  • mto: This revision was merged to the branch mainline in revision 515.
  • Revision ID: jelmer@samba.org-20080629191834-ha2ecpv5szt96nge
Make sure signed testament matches repository data.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
__copyright__ = "Copyright © 2005 Canonical Ltd."
10
10
__author__    = "Scott James Remnant <scott@ubuntu.com>"
11
11
 
 
12
from bzrlib.revision import NULL_REVISION
12
13
from bzrlib.tsort import merge_sort
13
14
 
14
 
def linegraph(branch, start, maxnum):
15
 
    """Produce a directed graph of a bzr branch.
 
15
def linegraph(repository, start_revs, maxnum, broken_line_length = None,
 
16
              graph_data = True, mainline_only = False):
 
17
    """Produce a directed graph of a bzr repository.
16
18
 
17
19
    Returns a tuple of (line_graph, revid_index, columns_len) where
18
20
    * line_graph is a list of tuples of (revid,
41
43
    curved, kinked, etc.) and to pick the actual colours for each index.
42
44
    """
43
45
    
44
 
    # FIXME: This should be configurable
45
 
    BROKEN_LINE_LENGTH = 32
46
 
    
47
 
    # We get the mainline so we can pass it to merge_sort to make merge_sort
48
 
    # run faster.
49
 
    mainline = [None]
50
 
    mainline.extend(branch.revision_history())
51
 
    graph_parents = branch.repository.get_revision_graph(start)
 
46
    graph = repository.get_graph()
 
47
    graph_parents = {}
 
48
    ghosts = set()
52
49
    graph_children = {}
53
 
    for revid in graph_parents.iterkeys():
54
 
        graph_children[revid] = []
55
 
 
56
 
    merge_sorted_revisions = merge_sort(
57
 
        graph_parents,
58
 
        start,
59
 
        mainline,
60
 
        generate_revno=True)
 
50
    for (revid, parent_revids) in graph.iter_ancestry(start_revs):
 
51
        if parent_revids is None:
 
52
            ghosts.add(revid)
 
53
            continue
 
54
        if parent_revids == (NULL_REVISION,):
 
55
            graph_parents[revid] = ()
 
56
        else:
 
57
            graph_parents[revid] = parent_revids
 
58
        for parent in parent_revids:
 
59
            graph_children.setdefault(parent, []).append(revid)
 
60
        graph_children.setdefault(revid, [])
 
61
    for ghost in ghosts:
 
62
        for ghost_child in graph_children[ghost]:
 
63
            graph_parents[ghost_child] = [p for p in graph_parents[ghost_child]
 
64
                                          if p not in ghosts]
 
65
    graph_parents["top:"] = start_revs
 
66
 
 
67
    if len(graph_parents)>0:
 
68
        merge_sorted_revisions = merge_sort(
 
69
            graph_parents,
 
70
            "top:",
 
71
            generate_revno=True)
 
72
    else:
 
73
        merge_sorted_revisions = ()
 
74
    
 
75
    if mainline_only:
 
76
        merge_sorted_revisions = [elem for elem in merge_sorted_revisions \
 
77
                                  if len(elem[3])==1 ]
 
78
 
 
79
    assert merge_sorted_revisions[0][1] == "top:"
 
80
    merge_sorted_revisions = merge_sorted_revisions[1:]
61
81
    
62
82
    revid_index = {}
63
83
    revno_index = {}
81
101
        if maxnum and rev_index >= maxnum:
82
102
            break
83
103
        revid_index[revid] = rev_index
84
 
        revno_index[revno_sequence] = rev_index
85
 
        
86
 
        branch_id = revno_sequence[0:-1]
87
 
        
88
 
        branch_line = None
89
 
        if branch_id not in branch_lines:
90
 
            branch_line = []
91
 
            branch_lines[branch_id] = branch_line
92
 
        else:
93
 
            branch_line = branch_lines[branch_id]
94
 
        
95
 
        branch_line.append(rev_index)
96
104
        
97
105
        parents = graph_parents[revid]
98
 
        for parent_revid in parents:
99
 
            graph_children[parent_revid].append(revid)
100
 
        
101
106
        linegraph.append([revid,
102
107
                          None,
103
108
                          [],
104
109
                          parents,
105
110
                          None,
106
111
                          revno_sequence])
 
112
        
 
113
        if graph_data:
 
114
            revno_index[revno_sequence] = rev_index
 
115
            
 
116
            branch_id = revno_sequence[0:-1]
 
117
            
 
118
            branch_line = None
 
119
            if branch_id not in branch_lines:
 
120
                branch_line = []
 
121
                branch_lines[branch_id] = branch_line
 
122
            else:
 
123
                branch_line = branch_lines[branch_id]
 
124
            
 
125
            branch_line.append(rev_index)        
107
126
 
108
 
    branch_ids = branch_lines.keys()
109
 
    
110
 
    def branch_id_cmp(x, y):
111
 
        """Compaire branch_id's first by the number of digits, then reversed
112
 
        by their value"""
113
 
        len_x = len(x)
114
 
        len_y = len(y)
115
 
        if len_x == len_y:
116
 
            return -cmp(x, y)
117
 
        return cmp(len_x, len_y)
118
 
    
119
 
    branch_ids.sort(branch_id_cmp)
120
 
    # This will hold a tuple of (child_index, parent_index, col_index) for each
121
 
    # line that needs to be drawn. If col_index is not none, then the line is
122
 
    # drawn along that column, else the the line can be drawn directly between
123
 
    # the child and parent because either the child and parent are in the same
124
 
    # branch line, or the child and parent are 1 row apart.
125
 
    lines = []
126
 
    empty_column = [False for i in range(len(graph_parents))]
127
 
    # This will hold a bit map for each cell. If the cell is true, then the
128
 
    # cell allready contains a node or line. This use when deciding what column
129
 
    # to place a branch line or line in, without it overlaping something else.
130
 
    columns = [list(empty_column)]
131
 
    
132
 
    
133
 
    for branch_id in branch_ids:
134
 
        branch_line = branch_lines[branch_id]
135
 
        
136
 
        # Find the col_index for the direct parent branch. This will be the
137
 
        # starting point when looking for a free column.
138
 
        parent_col_index = 0
139
 
        parent_index = None
140
 
        if len(branch_id) > 1:
141
 
            parent_revno = branch_id[0:-1]
142
 
            if parent_revno in revno_index:
143
 
                parent_index = revno_index[parent_revno]
144
 
                parent_node = linegraph[parent_index][1]
145
 
                if parent_node:
146
 
                    parent_col_index = parent_node[0]
147
 
                
148
 
        
149
 
        col_search_order = _branch_line_col_search_order(columns,
150
 
                                                         parent_col_index)
151
 
        color = reduce(lambda x, y: x+y, branch_id, 0)
152
 
        cur_cont_line = []
153
 
        
154
 
        line_range = []
155
 
        last_rev_index = None
156
 
        for rev_index in branch_line:
157
 
            if last_rev_index:
158
 
                if BROKEN_LINE_LENGTH and \
159
 
                   rev_index - last_rev_index > BROKEN_LINE_LENGTH:
160
 
                    line_range.append(last_rev_index+1)
161
 
                    line_range.append(rev_index-1)
162
 
                else:
163
 
                    line_range.extend(range(last_rev_index+1, rev_index))
164
 
            
165
 
            line_range.append(rev_index)
166
 
            last_rev_index = rev_index
167
 
        
168
 
        if parent_index:
169
 
            if BROKEN_LINE_LENGTH and \
170
 
               parent_index - last_rev_index > BROKEN_LINE_LENGTH:
171
 
                line_range.append(last_rev_index+1)
172
 
            else:
173
 
                line_range.extend(range(last_rev_index+1, parent_index))
174
 
        
175
 
        col_index = _find_free_column(columns,
176
 
                                      empty_column,
177
 
                                      col_search_order,
178
 
                                      line_range)
179
 
        node = (col_index, color)
180
 
        for rev_index in branch_line:
181
 
            linegraph[rev_index][1] = node
182
 
            columns[col_index][rev_index] = True
183
 
        
184
 
        for rev_index in branch_line:
185
 
            (sequence_number,
186
 
                 revid,
187
 
                 merge_depth,
188
 
                 revno_sequence,
189
 
                 end_of_merge) = merge_sorted_revisions[rev_index]
190
 
            
191
 
            linegraph[rev_index][4] = graph_children[revid]
192
 
            col_index = linegraph[rev_index][1][0]
193
 
            
194
 
            for parent_revid in graph_parents[revid]:
195
 
                if parent_revid in revid_index:
196
 
                    
197
 
                    parent_index = revid_index[parent_revid]                            
 
127
    if graph_data:
 
128
        branch_ids = branch_lines.keys()
 
129
    
 
130
        def branch_id_cmp(x, y):
 
131
            """Compaire branch_id's first by the number of digits, then reversed
 
132
            by their value"""
 
133
            len_x = len(x)
 
134
            len_y = len(y)
 
135
            if len_x == len_y:
 
136
                return -cmp(x, y)
 
137
            return cmp(len_x, len_y)
 
138
        
 
139
        branch_ids.sort(branch_id_cmp)
 
140
        # This will hold a tuple of (child_index, parent_index, col_index) for each
 
141
        # line that needs to be drawn. If col_index is not none, then the line is
 
142
        # drawn along that column, else the the line can be drawn directly between
 
143
        # the child and parent because either the child and parent are in the same
 
144
        # branch line, or the child and parent are 1 row apart.
 
145
        lines = []
 
146
        empty_column = [False for i in range(len(graph_parents))]
 
147
        # This will hold a bit map for each cell. If the cell is true, then the
 
148
        # cell allready contains a node or line. This use when deciding what column
 
149
        # to place a branch line or line in, without it overlaping something else.
 
150
        columns = [list(empty_column)]
 
151
        
 
152
        
 
153
        for branch_id in branch_ids:
 
154
            branch_line = branch_lines[branch_id]
 
155
            
 
156
            # Find the col_index for the direct parent branch. This will be the
 
157
            # starting point when looking for a free column.
 
158
            parent_col_index = 0
 
159
            parent_index = None
 
160
            if len(branch_id) > 1:
 
161
                parent_revno = branch_id[0:-1]
 
162
                if parent_revno in revno_index:
 
163
                    parent_index = revno_index[parent_revno]
198
164
                    parent_node = linegraph[parent_index][1]
199
165
                    if parent_node:
200
166
                        parent_col_index = parent_node[0]
 
167
                    
 
168
            
 
169
            col_search_order = _branch_line_col_search_order(columns,
 
170
                                                             parent_col_index)
 
171
            color = reduce(lambda x, y: x+y, branch_id, 0)
 
172
            cur_cont_line = []
 
173
            
 
174
            line_range = []
 
175
            last_rev_index = None
 
176
            for rev_index in branch_line:
 
177
                if last_rev_index:
 
178
                    if broken_line_length and \
 
179
                       rev_index - last_rev_index > broken_line_length:
 
180
                        line_range.append(last_rev_index+1)
 
181
                        line_range.append(rev_index-1)
201
182
                    else:
202
 
                        parent_col_index = None
203
 
                    col_search_order = \
204
 
                            _line_col_search_order(columns,
205
 
                                                   parent_col_index,
206
 
                                                   col_index)
207
 
                        
208
 
                    # If this line is really long, break it.
209
 
                    if len(branch_id) > 0 and \
210
 
                       BROKEN_LINE_LENGTH and \
211
 
                       parent_index - rev_index > BROKEN_LINE_LENGTH:
212
 
                        child_line_col_index = \
213
 
                            _find_free_column(columns,
214
 
                                              empty_column,
215
 
                                              col_search_order,
216
 
                                              (rev_index + 1,))
217
 
                        _mark_column_as_used(columns,
218
 
                                             child_line_col_index,
219
 
                                             (rev_index + 1,))
220
 
                        
221
 
                        # Recall _line_col_search_order to reset it back to
222
 
                        # the beging.
 
183
                        line_range.extend(range(last_rev_index+1, rev_index))
 
184
                
 
185
                line_range.append(rev_index)
 
186
                last_rev_index = rev_index
 
187
            
 
188
            if parent_index:
 
189
                if broken_line_length and \
 
190
                   parent_index - last_rev_index > broken_line_length:
 
191
                    line_range.append(last_rev_index+1)
 
192
                else:
 
193
                    line_range.extend(range(last_rev_index+1, parent_index))
 
194
            
 
195
            col_index = _find_free_column(columns,
 
196
                                          empty_column,
 
197
                                          col_search_order,
 
198
                                          line_range)
 
199
            node = (col_index, color)
 
200
            for rev_index in branch_line:
 
201
                linegraph[rev_index][1] = node
 
202
                columns[col_index][rev_index] = True
 
203
            
 
204
            for rev_index in branch_line:
 
205
                (sequence_number,
 
206
                     revid,
 
207
                     merge_depth,
 
208
                     revno_sequence,
 
209
                     end_of_merge) = merge_sorted_revisions[rev_index]
 
210
                
 
211
                linegraph[rev_index][4] = graph_children[revid]
 
212
                col_index = linegraph[rev_index][1][0]
 
213
                
 
214
                for parent_revid in graph_parents[revid]:
 
215
                    if parent_revid in revid_index:
 
216
                        
 
217
                        parent_index = revid_index[parent_revid]                            
 
218
                        parent_node = linegraph[parent_index][1]
 
219
                        if parent_node:
 
220
                            parent_col_index = parent_node[0]
 
221
                        else:
 
222
                            parent_col_index = None
223
223
                        col_search_order = \
224
224
                                _line_col_search_order(columns,
225
225
                                                       parent_col_index,
226
226
                                                       col_index)
227
 
                        parent_col_line_index = \
228
 
                            _find_free_column(columns,
229
 
                                              empty_column,
230
 
                                              col_search_order,
231
 
                                              (parent_index - 1,))
232
 
                        _mark_column_as_used(columns,
233
 
                                             parent_col_line_index,
234
 
                                             (parent_index - 1,))
235
 
                        lines.append((rev_index,
236
 
                                      parent_index,
237
 
                                      (child_line_col_index,
238
 
                                       parent_col_line_index)))
239
 
                    else :
240
 
                        line_col_index = col_index
241
 
                        if parent_index - rev_index >1:
242
 
                            line_range = range(rev_index + 1, parent_index)
243
 
                            line_col_index = \
244
 
                                _find_free_column(columns,
245
 
                                                  empty_column,
246
 
                                                  col_search_order,
247
 
                                                  line_range)
248
 
                            _mark_column_as_used(columns,
249
 
                                                 line_col_index,
250
 
                                                 line_range)
251
 
                        lines.append((rev_index,
252
 
                                      parent_index,
253
 
                                      (line_col_index,)))
254
 
    
255
 
    for (child_index, parent_index, line_col_indexes) in lines:
256
 
        (child_col_index, child_color) = linegraph[child_index][1]
257
 
        (parent_col_index, parent_color) = linegraph[parent_index][1]
 
227
                            
 
228
                        # If this line is really long, break it.
 
229
                        if len(branch_id) > 0 and \
 
230
                           broken_line_length and \
 
231
                           parent_index - rev_index > broken_line_length:
 
232
                            child_line_col_index = \
 
233
                                _find_free_column(columns,
 
234
                                                  empty_column,
 
235
                                                  col_search_order,
 
236
                                                  (rev_index + 1,))
 
237
                            _mark_column_as_used(columns,
 
238
                                                 child_line_col_index,
 
239
                                                 (rev_index + 1,))
 
240
                            
 
241
                            # Recall _line_col_search_order to reset it back to
 
242
                            # the beging.
 
243
                            col_search_order = \
 
244
                                    _line_col_search_order(columns,
 
245
                                                           parent_col_index,
 
246
                                                           col_index)
 
247
                            parent_col_line_index = \
 
248
                                _find_free_column(columns,
 
249
                                                  empty_column,
 
250
                                                  col_search_order,
 
251
                                                  (parent_index - 1,))
 
252
                            _mark_column_as_used(columns,
 
253
                                                 parent_col_line_index,
 
254
                                                 (parent_index - 1,))
 
255
                            lines.append((rev_index,
 
256
                                          parent_index,
 
257
                                          (child_line_col_index,
 
258
                                           parent_col_line_index)))
 
259
                        else :
 
260
                            line_col_index = col_index
 
261
                            if parent_index - rev_index >1:
 
262
                                line_range = range(rev_index + 1, parent_index)
 
263
                                line_col_index = \
 
264
                                    _find_free_column(columns,
 
265
                                                      empty_column,
 
266
                                                      col_search_order,
 
267
                                                      line_range)
 
268
                                _mark_column_as_used(columns,
 
269
                                                     line_col_index,
 
270
                                                     line_range)
 
271
                            lines.append((rev_index,
 
272
                                          parent_index,
 
273
                                          (line_col_index,)))
258
274
        
259
 
        if len(line_col_indexes) == 1:
260
 
            if parent_index - child_index == 1:
261
 
                linegraph[child_index][2].append(
262
 
                    (child_col_index,
263
 
                     parent_col_index,
264
 
                     parent_color))
 
275
        for (child_index, parent_index, line_col_indexes) in lines:
 
276
            (child_col_index, child_color) = linegraph[child_index][1]
 
277
            (parent_col_index, parent_color) = linegraph[parent_index][1]
 
278
            
 
279
            if len(line_col_indexes) == 1:
 
280
                if parent_index - child_index == 1:
 
281
                    linegraph[child_index][2].append(
 
282
                        (child_col_index,
 
283
                         parent_col_index,
 
284
                         parent_color))
 
285
                else:
 
286
                    # line from the child's column to the lines column
 
287
                    linegraph[child_index][2].append(
 
288
                        (child_col_index,
 
289
                         line_col_indexes[0],
 
290
                         parent_color))
 
291
                    # lines down the line's column
 
292
                    for line_part_index in range(child_index+1, parent_index-1):
 
293
                        linegraph[line_part_index][2].append(
 
294
                            (line_col_indexes[0],   
 
295
                             line_col_indexes[0],
 
296
                             parent_color))
 
297
                    # line from the line's column to the parent's column
 
298
                    linegraph[parent_index-1][2].append(
 
299
                        (line_col_indexes[0],
 
300
                         parent_col_index,
 
301
                         parent_color))
265
302
            else:
 
303
                # Broken line
266
304
                # line from the child's column to the lines column
267
305
                linegraph[child_index][2].append(
268
306
                    (child_col_index,
269
307
                     line_col_indexes[0],
270
308
                     parent_color))
271
 
                # lines down the line's column
272
 
                for line_part_index in range(child_index+1, parent_index-1):
273
 
                    linegraph[line_part_index][2].append(
274
 
                        (line_col_indexes[0],   
275
 
                         line_col_indexes[0],
276
 
                         parent_color))
 
309
                # Broken line end
 
310
                linegraph[child_index+1][2].append(
 
311
                    (line_col_indexes[0],
 
312
                     None,
 
313
                     parent_color))
 
314
                
 
315
                # Broken line end 
 
316
                linegraph[parent_index-2][2].append(
 
317
                    (None,
 
318
                     line_col_indexes[1],
 
319
                     parent_color))
277
320
                # line from the line's column to the parent's column
278
321
                linegraph[parent_index-1][2].append(
279
 
                    (line_col_indexes[0],
 
322
                    (line_col_indexes[1],
280
323
                     parent_col_index,
281
324
                     parent_color))
282
 
        else:
283
 
            # Broken line
284
 
            # line from the child's column to the lines column
285
 
            linegraph[child_index][2].append(
286
 
                (child_col_index,
287
 
                 line_col_indexes[0],
288
 
                 parent_color))
289
 
            # Broken line end
290
 
            linegraph[child_index+1][2].append(
291
 
                (line_col_indexes[0],
292
 
                 None,
293
 
                 parent_color))
294
 
            
295
 
            # Broken line end 
296
 
            linegraph[parent_index-2][2].append(
297
 
                (None,
298
 
                 line_col_indexes[1],
299
 
                 parent_color))
300
 
            # line from the line's column to the parent's column
301
 
            linegraph[parent_index-1][2].append(
302
 
                (line_col_indexes[1],
303
 
                 parent_col_index,
304
 
                 parent_color))
305
 
            
 
325
        return (linegraph, revid_index, len(columns))
 
326
    else:
 
327
        return (linegraph, revid_index, 0)
306
328
    
307
 
    return (linegraph, revid_index, len(columns))
308
329
 
309
330
def _branch_line_col_search_order(columns, parent_col_index):
310
331
    for col_index in range(parent_col_index, len(columns)):