43
40
It's up to you how to actually draw the nodes and lines (straight,
44
41
curved, kinked, etc.) and to pick the actual colours for each index.
46
assert isinstance(start_revs, list)
47
def update_root_progress(step_number):
48
"""IFF our container received a root progress bar, then update it."""
49
if root_progress is not None:
50
root_progress.update(None, step_number)
44
# FIXME: This should be configurable
45
BROKEN_LINE_LENGTH = 32
47
# We get the mainline so we can pass it to merge_sort to make merge_sort
49
mainline = branch.revision_history()
50
graph_parents = branch.repository.get_revision_graph(start)
54
51
graph_children = {}
55
update_root_progress(1)
56
progress_bar = ui.ui_factory.nested_progress_bar()
58
progress_bar.update("Arranging tree fragments")
59
for i, (revid, parent_revids) in enumerate(graph.iter_ancestry(start_revs)):
62
if parent_revids is None:
65
if parent_revids == (NULL_REVISION,):
66
graph_parents[revid] = ()
68
graph_parents[revid] = parent_revids
69
for parent in parent_revids:
70
graph_children.setdefault(parent, []).append(revid)
71
graph_children.setdefault(revid, [])
73
progress_bar.finished()
75
update_root_progress(2)
76
progress_bar = ui.ui_factory.nested_progress_bar()
78
progress_bar.update("Removing ghosts", 0, len(ghosts))
79
for i, ghost in enumerate(ghosts):
81
progress_bar.update(None, i)
82
for ghost_child in graph_children[ghost]:
83
graph_parents[ghost_child] = [p for p in graph_parents[ghost_child]
86
progress_bar.finished()
87
graph_parents["top:"] = start_revs
89
if len(graph_parents)>0:
90
merge_sorted_revisions = merge_sort(
95
merge_sorted_revisions = ()
98
merge_sorted_revisions = [elem for elem in merge_sorted_revisions \
101
assert merge_sorted_revisions[0][1] == "top:"
102
merge_sorted_revisions = merge_sorted_revisions[1:]
52
for revid in graph_parents.iterkeys():
53
graph_children[revid] = []
55
merge_sorted_revisions = merge_sort(
107
64
# This will hold an item for each "branch". For a revisions, the revsion
108
65
# number less the least significant digit is the branch_id, and used as the
109
66
# key for the dict. Hence revision with the same revsion number less the
112
69
# and these two revisions will be in the same branch line. Each value is
113
70
# a list of rev_indexes in the branch.
118
update_root_progress(3)
119
progress_bar = ui.ui_factory.nested_progress_bar()
121
progress_bar.update("Finding nodes", 0, len(merge_sorted_revisions))
122
for (rev_index, (sequence_number,
126
end_of_merge)) in enumerate(merge_sorted_revisions):
128
if rev_index % 25 == 0:
129
progress_bar.update(None, rev_index)
130
if maxnum and rev_index >= maxnum:
132
revid_index[revid] = rev_index
134
parents = graph_parents[revid]
135
linegraph.append([revid,
143
revno_index[revno_sequence] = rev_index
145
branch_id = revno_sequence[0:-1]
148
if branch_id not in branch_lines:
150
branch_lines[branch_id] = branch_line
152
branch_line = branch_lines[branch_id]
154
branch_line.append(rev_index)
156
progress_bar.finished()
159
branch_ids = branch_lines.keys()
161
def branch_id_cmp(x, y):
162
"""Compaire branch_id's first by the number of digits, then reversed
168
return cmp(len_x, len_y)
170
branch_ids.sort(branch_id_cmp)
171
# This will hold a tuple of (child_index, parent_index, col_index) for each
172
# line that needs to be drawn. If col_index is not none, then the line is
173
# drawn along that column, else the the line can be drawn directly between
174
# the child and parent because either the child and parent are in the same
175
# branch line, or the child and parent are 1 row apart.
177
empty_column = [False for i in range(len(graph_parents))]
178
# This will hold a bit map for each cell. If the cell is true, then the
179
# cell allready contains a node or line. This use when deciding what column
180
# to place a branch line or line in, without it overlaping something else.
181
columns = [list(empty_column)]
184
update_root_progress(4)
185
progress_bar = ui.ui_factory.nested_progress_bar()
187
progress_bar.update("Organizing edges", 0, len(branch_ids))
188
for i, branch_id in enumerate(branch_ids):
190
progress_bar.update(None, i)
191
branch_line = branch_lines[branch_id]
193
# Find the col_index for the direct parent branch. This will be the
194
# starting point when looking for a free column.
197
if len(branch_id) > 1:
198
parent_revno = branch_id[0:-1]
199
if parent_revno in revno_index:
200
parent_index = revno_index[parent_revno]
201
parent_node = linegraph[parent_index][1]
203
parent_col_index = parent_node[0]
206
col_search_order = _branch_line_col_search_order(columns,
208
color = reduce(lambda x, y: x+y, branch_id, 0)
212
last_rev_index = None
213
for rev_index in branch_line:
215
if broken_line_length and \
216
rev_index - last_rev_index > broken_line_length:
217
line_range.append(last_rev_index+1)
218
line_range.append(rev_index-1)
220
line_range.extend(range(last_rev_index+1, rev_index))
222
line_range.append(rev_index)
223
last_rev_index = rev_index
226
if broken_line_length and \
227
parent_index - last_rev_index > broken_line_length:
228
line_range.append(last_rev_index+1)
230
line_range.extend(range(last_rev_index+1, parent_index))
232
col_index = _find_free_column(columns,
236
node = (col_index, color)
237
for rev_index in branch_line:
238
linegraph[rev_index][1] = node
239
columns[col_index][rev_index] = True
241
for rev_index in branch_line:
246
end_of_merge) = merge_sorted_revisions[rev_index]
248
linegraph[rev_index][4] = graph_children[revid]
249
col_index = linegraph[rev_index][1][0]
251
for parent_revid in graph_parents[revid]:
252
if parent_revid in revid_index:
254
parent_index = revid_index[parent_revid]
255
parent_node = linegraph[parent_index][1]
257
parent_col_index = parent_node[0]
259
parent_col_index = None
261
_line_col_search_order(columns,
265
# If this line is really long, break it.
266
if len(branch_id) > 0 and \
267
broken_line_length and \
268
parent_index - rev_index > broken_line_length:
269
child_line_col_index = \
270
_find_free_column(columns,
274
_mark_column_as_used(columns,
275
child_line_col_index,
278
# Recall _line_col_search_order to reset it back to
281
_line_col_search_order(columns,
284
parent_col_line_index = \
285
_find_free_column(columns,
289
_mark_column_as_used(columns,
290
parent_col_line_index,
292
lines.append((rev_index,
294
(child_line_col_index,
295
parent_col_line_index)))
297
line_col_index = col_index
298
if parent_index - rev_index >1:
299
line_range = range(rev_index + 1, parent_index)
301
_find_free_column(columns,
305
_mark_column_as_used(columns,
308
lines.append((rev_index,
312
progress_bar.finished()
314
update_root_progress(5)
315
progress_bar = ui.ui_factory.nested_progress_bar()
317
progress_bar.update("Prettifying graph", 0, len(lines))
318
for i, (child_index, parent_index, line_col_indexes) in enumerate(lines):
320
progress_bar.update(None, i)
321
(child_col_index, child_color) = linegraph[child_index][1]
322
(parent_col_index, parent_color) = linegraph[parent_index][1]
324
if len(line_col_indexes) == 1:
325
if parent_index - child_index == 1:
326
linegraph[child_index][2].append(
331
# line from the child's column to the lines column
332
linegraph[child_index][2].append(
336
# lines down the line's column
337
for line_part_index in range(child_index+1, parent_index-1):
338
linegraph[line_part_index][2].append(
339
(line_col_indexes[0],
342
# line from the line's column to the parent's column
343
linegraph[parent_index-1][2].append(
344
(line_col_indexes[0],
349
# line from the child's column to the lines column
350
linegraph[child_index][2].append(
75
for (rev_index, (sequence_number,
79
end_of_merge)) in enumerate(merge_sorted_revisions):
80
if maxnum and rev_index >= maxnum:
82
revid_index[revid] = rev_index
83
revno_index[revno_sequence] = rev_index
85
branch_id = revno_sequence[0:-1]
88
if branch_id not in branch_lines:
90
branch_lines[branch_id] = branch_line
92
branch_line = branch_lines[branch_id]
94
branch_line.append(rev_index)
96
parents = graph_parents[revid]
97
for parent_revid in parents:
98
graph_children[parent_revid].append(revid)
100
linegraph.append([revid,
107
branch_ids = branch_lines.keys()
109
def branch_id_cmp(x, y):
110
"""Compaire branch_id's first by the number of digits, then reversed
116
return cmp(len_x, len_y)
118
branch_ids.sort(branch_id_cmp)
119
# This will hold a tuple of (child_index, parent_index, col_index) for each
120
# line that needs to be drawn. If col_index is not none, then the line is
121
# drawn along that column, else the the line can be drawn directly between
122
# the child and parent because either the child and parent are in the same
123
# branch line, or the child and parent are 1 row apart.
125
empty_column = [False for i in range(len(graph_parents))]
126
# This will hold a bit map for each cell. If the cell is true, then the
127
# cell allready contains a node or line. This use when deciding what column
128
# to place a branch line or line in, without it overlaping something else.
129
columns = [list(empty_column)]
132
for branch_id in branch_ids:
133
branch_line = branch_lines[branch_id]
135
# Find the col_index for the direct parent branch. This will be the
136
# starting point when looking for a free column.
139
if len(branch_id) > 1:
140
parent_revno = branch_id[0:-1]
141
if parent_revno in revno_index:
142
parent_index = revno_index[parent_revno]
143
parent_node = linegraph[parent_index][1]
145
parent_col_index = parent_node[0]
148
col_search_order = _branch_line_col_search_order(columns,
150
color = reduce(lambda x, y: x+y, branch_id, 0)
154
last_rev_index = None
155
for rev_index in branch_line:
157
if BROKEN_LINE_LENGTH and \
158
rev_index - last_rev_index > BROKEN_LINE_LENGTH:
159
line_range.append(last_rev_index+1)
160
line_range.append(rev_index-1)
162
line_range.extend(range(last_rev_index+1, rev_index))
164
line_range.append(rev_index)
165
last_rev_index = rev_index
168
if BROKEN_LINE_LENGTH and \
169
parent_index - last_rev_index > BROKEN_LINE_LENGTH:
170
line_range.append(last_rev_index+1)
172
line_range.extend(range(last_rev_index+1, parent_index))
174
col_index = _find_free_column(columns,
178
node = (col_index, color)
179
for rev_index in branch_line:
180
linegraph[rev_index][1] = node
181
columns[col_index][rev_index] = True
183
for rev_index in branch_line:
188
end_of_merge) = merge_sorted_revisions[rev_index]
190
linegraph[rev_index][4] = graph_children[revid]
191
col_index = linegraph[rev_index][1][0]
193
for parent_revid in graph_parents[revid]:
194
if parent_revid in revid_index:
196
parent_index = revid_index[parent_revid]
197
parent_node = linegraph[parent_index][1]
199
parent_col_index = parent_node[0]
201
parent_col_index = None
203
_line_col_search_order(columns,
207
# If this line is really long, break it.
208
if len(branch_id) > 0 and \
209
BROKEN_LINE_LENGTH and \
210
parent_index - rev_index > BROKEN_LINE_LENGTH:
211
child_line_col_index = \
212
_find_free_column(columns,
216
_mark_column_as_used(columns,
217
child_line_col_index,
220
# Recall _line_col_search_order to reset it back to
223
_line_col_search_order(columns,
226
parent_col_line_index = \
227
_find_free_column(columns,
231
_mark_column_as_used(columns,
232
parent_col_line_index,
234
lines.append((rev_index,
236
(child_line_col_index,
237
parent_col_line_index)))
239
line_col_index = col_index
240
if parent_index - rev_index >1:
241
line_range = range(rev_index + 1, parent_index)
243
_find_free_column(columns,
247
_mark_column_as_used(columns,
250
lines.append((rev_index,
254
for (child_index, parent_index, line_col_indexes) in lines:
255
(child_col_index, child_color) = linegraph[child_index][1]
256
(parent_col_index, parent_color) = linegraph[parent_index][1]
258
if len(line_col_indexes) == 1:
259
if parent_index - child_index == 1:
260
linegraph[child_index][2].append(
265
# line from the child's column to the lines column
266
linegraph[child_index][2].append(
270
# lines down the line's column
271
for line_part_index in range(child_index+1, parent_index-1):
272
linegraph[line_part_index][2].append(
273
(line_col_indexes[0],
352
274
line_col_indexes[0],
355
linegraph[child_index+1][2].append(
356
(line_col_indexes[0],
361
linegraph[parent_index-2][2].append(
365
# line from the line's column to the parent's column
366
linegraph[parent_index-1][2].append(
367
(line_col_indexes[1],
371
progress_bar.finished()
372
return (linegraph, revid_index, len(columns))
374
return (linegraph, revid_index, 0)
276
# line from the line's column to the parent's column
277
linegraph[parent_index-1][2].append(
278
(line_col_indexes[0],
283
# line from the child's column to the lines column
284
linegraph[child_index][2].append(
289
linegraph[child_index+1][2].append(
290
(line_col_indexes[0],
295
linegraph[parent_index-2][2].append(
299
# line from the line's column to the parent's column
300
linegraph[parent_index-1][2].append(
301
(line_col_indexes[1],
306
return (linegraph, revid_index, len(columns))
377
308
def _branch_line_col_search_order(columns, parent_col_index):
378
309
for col_index in range(parent_col_index, len(columns)):