321
328
It's up to you how to actually draw the nodes and lines (straight,
322
329
curved, kinked, etc.) and to pick the actual colours for each index.
331
if not len(merge_sorted):
333
# split merge_sorted into a map:
335
# FIXME: get a hint on this from the merge_sorted data rather than
336
# calculating it ourselves
337
# mapping from rev_id to the sequence number of the next lowest rev
339
# mapping from rev_id to next-in-branch-revid - may be None for end
341
next_branch_revid = {}
342
# the stack we are in in the sorted data for determining which
343
# next_lower_rev to set. It is a stack which has one list at each
344
# depth - the ids at that depth that need the same id allocated.
346
for seq, revid, indent, end_merge in merge_sorted:
347
revs[revid] = (seq, indent, end_merge)
348
if indent == len(current_stack):
349
# new merge group starts
350
current_stack.append([revid])
351
elif indent == len(current_stack) - 1:
352
# part of the current merge group
353
current_stack[-1].append(revid)
355
# end of a merge group
356
while current_stack[-1]:
357
stack_rev_id = current_stack[-1].pop()
358
# record the next lower rev for this rev:
359
next_lower_rev[stack_rev_id] = seq
360
# if this followed a non-end-merge rev in this group note that
361
if len(current_stack[-1]):
362
if not revs[current_stack[-1][-1]][2]:
363
next_branch_revid[current_stack[-1][-1]] = stack_rev_id
365
# append to the now-current merge group
366
current_stack[-1].append(revid)
367
# assign a value to all the depth 0 revisions
368
while current_stack[-1]:
369
stack_rev_id = current_stack[-1].pop()
370
# record the next lower rev for this rev:
371
next_lower_rev[stack_rev_id] = len(merge_sorted)
372
# if this followed a non-end-merge rev in this group note that
373
if len(current_stack[-1]):
374
if not revs[current_stack[-1][-1]][2]:
375
next_branch_revid[current_stack[-1][-1]] = stack_rev_id
377
# a list of the current revisions we are drawing lines TO indicating
378
# the sequence of their lines on the screen.
379
# i.e. [A, B, C] means that the line to A, to B, and to C are in
380
# (respectively), 0, 1, 2 on the screen.
381
hanging = [merge_sorted[0][1]]
382
for seq, revid, indent, end_merge in merge_sorted:
383
# a list of the lines to draw: their position in the
384
# previous row, their position in this row, and the colour
385
# (which is the colour they are routing to).
330
390
for h_idx, hang in enumerate(hanging):
391
# one of these will be the current lines node:
392
# we are drawing a line. h_idx
331
393
if hang == revid:
332
# We've matched a hanging revision, so need to output a node
394
# we have found the current lines node
334
395
node = (h_idx, colours[revid])
397
# note that we might have done the main parent
398
drawn_parents = set()
400
def draw_line(from_idx, to_idx, revision_id):
402
n_idx = new_hanging.index(revision_id)
404
# force this to be vertical at the place this rev was
406
new_hanging.insert(to_idx, revision_id)
408
lines.append((from_idx, n_idx, colours[revision_id]))
411
# we want to draw a line to the next commit on 'this' branch
413
# drop this line first.
414
parent_id = next_branch_revid[revid]
415
draw_line(h_idx, h_idx, parent_id)
416
# we have drawn this parent
417
drawn_parents.add(parent_id)
419
# this is the last revision in a 'merge', show where it came from
420
if len(parent_ids[revisions[revid]]) > 1:
422
# parents means this commit was a merge, and being
423
# the end point of a merge group means that all
424
# the parent revisions were merged into branches
425
# to the left of this before this was committed
426
# - so we want to show this as a new branch from
428
# to do this, we show the parent with the lowest
429
# sequence number, which is the one that this
430
# branch 'spawned from', and no others.
431
# If this sounds like a problem, remember that:
432
# if the parent was not already in our mainline
433
# it would show up as a merge into this making
434
# this not the end of a merge-line.
435
lowest = len(merge_sorted)
436
for parent_id in parent_ids[revisions[revid]]:
437
if revs[parent_id][0] < lowest:
438
lowest = revs[parent_id][0]
439
assert lowest != len(merge_sorted)
440
draw_line(h_idx, len(new_hanging), merge_sorted[lowest][1])
441
drawn_parents.add(merge_sorted[lowest][1])
442
elif len(parent_ids[revisions[revid]]) == 1:
443
# only one parent, must show this link to be useful.
444
parent_id = parent_ids[revisions[revid]][0]
445
draw_line(h_idx, len(new_hanging), parent_id)
446
drawn_parents.add(parent_id)
448
# what do we want to draw lines to from here:
449
# each parent IF its relevant.
336
451
# Now we need to hang its parents, we put them at the point
337
452
# the old column was so anything to the right of this has
338
453
# to move outwards to make room. We also try and collapse
339
454
# hangs to keep the graph small.
455
# RBC: we do not draw lines to parents that were already merged
456
# unless its the last revision in a merge group.
340
457
for parent_id in parent_ids[revisions[revid]]:
342
n_idx = new_hanging.index(parent_id)
344
n_idx = len(new_hanging)
345
new_hanging.append(parent_id)
346
lines.append((h_idx, n_idx, colours[parent_id]))
458
if parent_id in drawn_parents:
460
parent_seq = revs[parent_id][0]
461
parent_depth = revs[parent_id][1]
462
if parent_depth == indent + 1:
463
# the parent was a merge into this branch
464
# determine if it was already merged into the mainline
465
# via a different merge:
466
# if all revisions between us and parent_seq have a
467
# indent greater than there are no revisions with a lower indent than
469
# we do not use 'parent_depth < indent' because that would allow
470
# un-uniqueified merges to show up, and merge_sorted should take
471
# care of that for us (but does not trim the values)
472
if parent_seq < next_lower_rev[revid]:
473
draw_line(h_idx, len(new_hanging), parent_id)
474
elif parent_depth == indent and parent_seq == seq + 1:
475
# part of this branch
476
draw_line(h_idx, len(new_hanging), parent_id)
348
# Revision keeps on hanging, adjust for any change in the
349
# graph shape and try to collapse hangs to keep the graph
352
n_idx = new_hanging.index(hang)
354
n_idx = len(new_hanging)
355
new_hanging.append(hang)
356
lines.append((h_idx, n_idx, colours[hang]))
478
# draw a line from the previous position of this line to the
480
# h_idx is the old position.
481
# new_indent is the new position.
482
draw_line(h_idx, len(new_hanging), hang)
483
# we've calculated the row, assign new_hanging to hanging to setup for
357
485
hanging = new_hanging
359
487
yield (revisions[revid], node, lines)