bzr branch
http://gegoxaren.bato24.eu/bzr/b-gtk/fix-viz
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
1  | 
# -*- coding: UTF-8 -*-
 | 
2  | 
"""Directed graph production.
 | 
|
3  | 
||
4  | 
This module contains the code to produce an ordered directed graph of a
 | 
|
5  | 
bzr branch, such as we display in the tree view at the top of the bzrk
 | 
|
6  | 
window.
 | 
|
7  | 
"""
 | 
|
8  | 
||
9  | 
__copyright__ = "Copyright © 2005 Canonical Ltd."  | 
|
10  | 
__author__ = "Scott James Remnant <scott@ubuntu.com>"  | 
|
11  | 
||
12  | 
||
| 
198
by Jelmer Vernooij
 Add tests for DummyRevision.  | 
13  | 
from bzrlib.revision import Revision  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
14  | 
from bzrlib.tsort import merge_sort  | 
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
15  | 
|
16  | 
||
| 
198
by Jelmer Vernooij
 Add tests for DummyRevision.  | 
17  | 
class DummyRevision(Revision):  | 
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
18  | 
"""Dummy bzr revision.  | 
19  | 
||
20  | 
    Sometimes, especially in older bzr branches, a revision is referenced
 | 
|
21  | 
    as the parent of another but not actually present in the branch's store.
 | 
|
22  | 
    When this happens we use an instance of this class instead of the real
 | 
|
23  | 
    Revision object (which we can't get).
 | 
|
24  | 
    """
 | 
|
25  | 
def __init__(self, revid):  | 
|
| 
198
by Jelmer Vernooij
 Add tests for DummyRevision.  | 
26  | 
super(DummyRevision, self).__init__(revid)  | 
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
27  | 
self.committer = None  | 
| 
199.1.1
by Vincent Ladeuil
 Make bzr selftest happy again.  | 
28  | 
self.timestamp = None  | 
29  | 
self.timezone = None  | 
|
| 
198
by Jelmer Vernooij
 Add tests for DummyRevision.  | 
30  | 
self.message = revid  | 
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
31  | 
|
32  | 
||
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
33  | 
class RevisionProxy(object):  | 
34  | 
"""A revision proxy object.  | 
|
35  | 
||
36  | 
    This will demand load the revision it represents when the committer or
 | 
|
37  | 
    message attributes are accessed in order to populate them. It is 
 | 
|
38  | 
    constructed with the revision id and parent_ids list and a repository
 | 
|
39  | 
    object to request the revision from when needed.
 | 
|
40  | 
    """
 | 
|
41  | 
||
42  | 
def __init__(self, revid, parent_ids, repository):  | 
|
43  | 
self.revision_id = revid  | 
|
44  | 
self.parent_ids = parent_ids  | 
|
45  | 
self._repository = repository  | 
|
46  | 
self._revision = None  | 
|
47  | 
||
48  | 
def _get_attribute_getter(attr):  | 
|
49  | 
def get_attribute(self):  | 
|
50  | 
if self._revision is None:  | 
|
51  | 
self._load()  | 
|
52  | 
return getattr(self._revision, attr)  | 
|
53  | 
return get_attribute  | 
|
54  | 
committer = property(_get_attribute_getter('committer'))  | 
|
55  | 
message = property(_get_attribute_getter('message'))  | 
|
56  | 
properties = property(_get_attribute_getter('properties'))  | 
|
57  | 
timestamp = property(_get_attribute_getter('timestamp'))  | 
|
58  | 
timezone = property(_get_attribute_getter('timezone'))  | 
|
59  | 
||
60  | 
def _load(self):  | 
|
61  | 
"""Load the revision object."""  | 
|
62  | 
self._revision = self._repository.get_revision(self.revision_id)  | 
|
63  | 
||
64  | 
||
| 
27
by David Allouche
 refactor distances  | 
65  | 
class DistanceMethod(object):  | 
66  | 
||
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
67  | 
def __init__(self, repository, start_revid):  | 
68  | 
self.repository = repository  | 
|
69  | 
self.start_revid = start_revid  | 
|
| 
27
by David Allouche
 refactor distances  | 
70  | 
self.revisions = {}  | 
71  | 
self.children = {}  | 
|
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
72  | 
self.children_of_id = {start_revid: set()}  | 
| 
27
by David Allouche
 refactor distances  | 
73  | 
self.parent_ids_of = {}  | 
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
74  | 
self.colours = { start_revid: 0 }  | 
| 
27
by David Allouche
 refactor distances  | 
75  | 
self.last_colour = 0  | 
76  | 
self.direct_parent_of = {}  | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
77  | 
self.graph = {}  | 
| 
27
by David Allouche
 refactor distances  | 
78  | 
|
| 
28
by David Allouche
 optimise by filling caches first  | 
79  | 
def fill_caches(self):  | 
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
80  | 
graph = self.repository.get_revision_graph_with_ghosts([self.start_revid])  | 
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
81  | 
for revid in graph.ghosts:  | 
82  | 
self.cache_revision(DummyRevision(revid))  | 
|
83  | 
for revid, parents in graph.get_ancestors().items():  | 
|
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
84  | 
self.cache_revision(RevisionProxy(revid, parents, self.repository))  | 
| 
27
by David Allouche
 refactor distances  | 
85  | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
86  | 
def cache_revision(self, revision):  | 
| 
27
by David Allouche
 refactor distances  | 
87  | 
        "Set the caches for a newly retrieved revision."""
 | 
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
88  | 
revid = revision.revision_id  | 
| 
27
by David Allouche
 refactor distances  | 
89  | 
        # Build a revision cache
 | 
90  | 
self.revisions[revid] = revision  | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
91  | 
        # Build a children dictionary
 | 
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
92  | 
for parent_id in revision.parent_ids:  | 
| 
27
by David Allouche
 refactor distances  | 
93  | 
self.children_of_id.setdefault(parent_id, set()).add(revision)  | 
94  | 
        # Build a parents dictionnary, where redundant parents will be removed,
 | 
|
95  | 
        # and that will be passed along tothe rest of program.
 | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
96  | 
if len(revision.parent_ids) != len(set(revision.parent_ids)):  | 
97  | 
            # fix the parent_ids list.
 | 
|
| 
20
by David Allouche
 ignore redundent parents  | 
98  | 
parent_ids = []  | 
99  | 
parent_ids_set = set()  | 
|
100  | 
for parent_id in revision.parent_ids:  | 
|
101  | 
if parent_id in parent_ids_set:  | 
|
102  | 
                    continue
 | 
|
103  | 
parent_ids.append(parent_id)  | 
|
104  | 
parent_ids_set.add(parent_id)  | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
105  | 
revision.parent_ids = parent_ids  | 
106  | 
self.parent_ids_of[revision] = list(revision.parent_ids)  | 
|
107  | 
self.graph[revid] = revision.parent_ids  | 
|
| 
27
by David Allouche
 refactor distances  | 
108  | 
|
109  | 
def make_children_map(self):  | 
|
110  | 
revisions = self.revisions  | 
|
111  | 
return dict((revisions[revid], c)  | 
|
112  | 
for (revid, c) in self.children_of_id.iteritems())  | 
|
113  | 
||
| 
37
by David Allouche
 early exit accurate sorting when maxnum is set  | 
114  | 
def sort_revisions(self, sorted_revids, maxnum):  | 
| 
27
by David Allouche
 refactor distances  | 
115  | 
revisions = self.revisions  | 
116  | 
parent_ids_of = self.parent_ids_of  | 
|
117  | 
children_of_id = self.children_of_id  | 
|
118  | 
        # Try to compact sequences of revisions on the same branch.
 | 
|
119  | 
distances = {}  | 
|
120  | 
skipped_revids = []  | 
|
121  | 
expected_id = sorted_revids[0]  | 
|
122  | 
pending_ids = []  | 
|
123  | 
while True:  | 
|
124  | 
revid = sorted_revids.pop(0)  | 
|
125  | 
if revid != expected_id:  | 
|
126  | 
skipped_revids.append(revid)  | 
|
127  | 
                continue
 | 
|
128  | 
revision = revisions[revid]  | 
|
129  | 
for child in children_of_id[revid]:  | 
|
130  | 
                # postpone if any child is missing
 | 
|
131  | 
if child.revision_id not in distances:  | 
|
132  | 
if expected_id not in pending_ids:  | 
|
133  | 
pending_ids.append(expected_id)  | 
|
134  | 
expected_id = pending_ids.pop(0)  | 
|
135  | 
skipped_revids.append(revid)  | 
|
136  | 
sorted_revids[:0] = skipped_revids  | 
|
| 
30
by David Allouche
 separate sorting and colouring  | 
137  | 
del skipped_revids[:]  | 
| 
27
by David Allouche
 refactor distances  | 
138  | 
                    break
 | 
139  | 
else:  | 
|
140  | 
                # all children are here, push!
 | 
|
141  | 
distances[revid] = len(distances)  | 
|
| 
37
by David Allouche
 early exit accurate sorting when maxnum is set  | 
142  | 
if maxnum is not None and len(distances) > maxnum:  | 
143  | 
                    # bail out early if a limit was specified
 | 
|
144  | 
sorted_revids[:0] = skipped_revids  | 
|
145  | 
for revid in sorted_revids:  | 
|
146  | 
distances[revid] = len(distances)  | 
|
147  | 
                    break
 | 
|
| 
27
by David Allouche
 refactor distances  | 
148  | 
                # all parents will need to be pushed as soon as possible
 | 
149  | 
for parent in parent_ids_of[revision]:  | 
|
150  | 
if parent not in pending_ids:  | 
|
151  | 
pending_ids.insert(0, parent)  | 
|
152  | 
if not pending_ids:  | 
|
153  | 
                    break
 | 
|
| 
22
by David Allouche
 sort revisions to be grouped by branch  | 
154  | 
expected_id = pending_ids.pop(0)  | 
| 
27
by David Allouche
 refactor distances  | 
155  | 
                # if the next expected revid has already been skipped, requeue
 | 
| 
31
by David Allouche
 fix a bug with fast sorting  | 
156  | 
                # the skipped ids, except those that would go right back to the
 | 
157  | 
                # skipped list.
 | 
|
| 
27
by David Allouche
 refactor distances  | 
158  | 
if expected_id in skipped_revids:  | 
159  | 
pos = skipped_revids.index(expected_id)  | 
|
160  | 
sorted_revids[:0] = skipped_revids[pos:]  | 
|
161  | 
del skipped_revids[pos:]  | 
|
| 
30
by David Allouche
 separate sorting and colouring  | 
162  | 
self.distances = distances  | 
| 
27
by David Allouche
 refactor distances  | 
163  | 
return sorted(distances, key=distances.get)  | 
164  | 
||
| 
30
by David Allouche
 separate sorting and colouring  | 
165  | 
def choose_colour(self, revid):  | 
166  | 
revision = self.revisions[revid]  | 
|
| 
27
by David Allouche
 refactor distances  | 
167  | 
children_of_id = self.children_of_id  | 
168  | 
parent_ids_of = self.parent_ids_of  | 
|
169  | 
colours = self.colours  | 
|
170  | 
        # choose colour
 | 
|
171  | 
the_children = children_of_id[revid]  | 
|
172  | 
if len(the_children) == 1:  | 
|
173  | 
[child] = the_children  | 
|
174  | 
if len(parent_ids_of[child]) == 1:  | 
|
175  | 
                # one-one relationship between parent and child, same
 | 
|
176  | 
                # colour
 | 
|
177  | 
colours[revid] = colours[child.revision_id]  | 
|
178  | 
else:  | 
|
179  | 
self.choose_colour_one_child(revision, child)  | 
|
180  | 
else:  | 
|
| 
30
by David Allouche
 separate sorting and colouring  | 
181  | 
self.choose_colour_many_children(revision, the_children)  | 
| 
27
by David Allouche
 refactor distances  | 
182  | 
|
183  | 
def choose_colour_one_child(self, revision, child):  | 
|
184  | 
revid = revision.revision_id  | 
|
185  | 
direct_parent_of = self.direct_parent_of  | 
|
186  | 
revisions = self.revisions  | 
|
187  | 
        # one child with multiple parents, the first parent with
 | 
|
188  | 
        # the same committer gets the colour
 | 
|
189  | 
direct_parent = direct_parent_of.get(child)  | 
|
190  | 
if direct_parent is None:  | 
|
191  | 
            # if it has not been found yet, find it now and remember
 | 
|
192  | 
for parent_id in self.parent_ids_of[child]:  | 
|
193  | 
parent_revision = revisions[parent_id]  | 
|
194  | 
if parent_revision.committer == child.committer:  | 
|
195  | 
                    # found the first parent with the same committer
 | 
|
196  | 
direct_parent = parent_revision  | 
|
197  | 
direct_parent_of[child] = direct_parent  | 
|
198  | 
                    break
 | 
|
199  | 
if direct_parent == revision:  | 
|
200  | 
self.colours[revid] = self.colours[child.revision_id]  | 
|
201  | 
else:  | 
|
202  | 
self.colours[revid] = self.last_colour = self.last_colour + 1  | 
|
203  | 
||
| 
30
by David Allouche
 separate sorting and colouring  | 
204  | 
def choose_colour_many_children(self, revision, the_children):  | 
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
205  | 
"""Colour revision revision."""  | 
| 
27
by David Allouche
 refactor distances  | 
206  | 
revid = revision.revision_id  | 
207  | 
direct_parent_of = self.direct_parent_of  | 
|
208  | 
        # multiple children, get the colour of the last displayed child
 | 
|
209  | 
        # with the same committer which does not already have its colour
 | 
|
210  | 
        # taken
 | 
|
211  | 
available = {}  | 
|
212  | 
for child in the_children:  | 
|
213  | 
if child.committer != revision.committer:  | 
|
214  | 
                continue
 | 
|
215  | 
direct_parent = direct_parent_of.get(child)  | 
|
216  | 
if direct_parent == revision:  | 
|
217  | 
self.colours[revid] = self.colours[child.revision_id]  | 
|
218  | 
                break
 | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
219  | 
            # FIXME: Colouring based on whats been displayed MUST be done with 
 | 
220  | 
            # knowledge of the revisions being output.
 | 
|
221  | 
            # until the refactoring to fold graph() into this more compactly is
 | 
|
222  | 
            # done, I've disabled this reuse. RBC 20060403
 | 
|
223  | 
            # if direct_parent is None:
 | 
|
224  | 
            #     available[child] = distances[child.revision_id] 
 | 
|
225  | 
            #   .. it will be something like available[child] =  \
 | 
|
226  | 
            #  revs[child.revision_id][0] - which is the sequence number
 | 
|
| 
27
by David Allouche
 refactor distances  | 
227  | 
else:  | 
228  | 
if available:  | 
|
229  | 
sorted_children = sorted(available, key=available.get)  | 
|
230  | 
child = sorted_children[-1]  | 
|
231  | 
direct_parent_of[child] = revision  | 
|
232  | 
self.colours[revid] = self.colours[child.revision_id]  | 
|
233  | 
else:  | 
|
234  | 
                # no candidate children is available, pick the next
 | 
|
235  | 
                # colour
 | 
|
236  | 
self.colours[revid] = self.last_colour = self.last_colour + 1  | 
|
237  | 
||
238  | 
||
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
239  | 
def distances(repository, start_revid):  | 
| 
27
by David Allouche
 refactor distances  | 
240  | 
"""Sort the revisions.  | 
241  | 
||
242  | 
    Traverses the branch revision tree starting at start and produces an
 | 
|
243  | 
    ordered list of revisions such that a revision always comes after
 | 
|
244  | 
    any revision it is the parent of.
 | 
|
245  | 
||
246  | 
    Returns a tuple of (revids, revisions, colours, children)
 | 
|
247  | 
    """
 | 
|
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
248  | 
distance = DistanceMethod(repository, start_revid)  | 
| 
28
by David Allouche
 optimise by filling caches first  | 
249  | 
distance.fill_caches()  | 
| 
246
by Jelmer Vernooij
 Use repository rather than branch where possible.  | 
250  | 
distance.merge_sorted = merge_sort(distance.graph, distance.start_revid)  | 
| 
29
by David Allouche
 optimise initial sorting  | 
251  | 
children = distance.make_children_map()  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
252  | 
|
253  | 
for seq, revid, merge_depth, end_of_merge in distance.merge_sorted:  | 
|
| 
30
by David Allouche
 separate sorting and colouring  | 
254  | 
distance.choose_colour(revid)  | 
| 
27
by David Allouche
 refactor distances  | 
255  | 
|
| 
28
by David Allouche
 optimise by filling caches first  | 
256  | 
revisions = distance.revisions  | 
257  | 
colours = distance.colours  | 
|
258  | 
parent_ids_of = distance.parent_ids_of  | 
|
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
259  | 
return (revisions, colours, children, parent_ids_of, distance.merge_sorted)  | 
| 
20
by David Allouche
 ignore redundent parents  | 
260  | 
|
| 
41
by David Allouche
 restore --maxnum functionality, reflush some comments  | 
261  | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
262  | 
def graph(revisions, colours, merge_sorted):  | 
| 
3
by Scott James Remnant
 Split the display in two with a pane, we'll use the bottom half to show  | 
263  | 
"""Produce a directed graph of a bzr branch.  | 
264  | 
||
265  | 
    For each revision it then yields a tuple of (revision, node, lines).
 | 
|
266  | 
    If the revision is only referenced in the branch and not present in the
 | 
|
267  | 
    store, revision will be a DummyRevision object, otherwise it is the bzr
 | 
|
268  | 
    Revision object with the meta-data for the revision.
 | 
|
269  | 
||
270  | 
    Node is a tuple of (column, colour) with column being a zero-indexed
 | 
|
271  | 
    column number of the graph that this revision represents and colour
 | 
|
272  | 
    being a zero-indexed colour (which doesn't specify any actual colour
 | 
|
273  | 
    in particular) to draw the node in.
 | 
|
274  | 
||
275  | 
    Lines is a list of tuples which represent lines you should draw away
 | 
|
276  | 
    from the revision, if you also need to draw lines into the revision
 | 
|
277  | 
    you should use the lines list from the previous iteration.  Each
 | 
|
278  | 
    typle in the list is in the form (start, end, colour) with start and
 | 
|
279  | 
    end being zero-indexed column numbers and colour as in node.
 | 
|
280  | 
||
281  | 
    It's up to you how to actually draw the nodes and lines (straight,
 | 
|
282  | 
    curved, kinked, etc.) and to pick the actual colours for each index.
 | 
|
283  | 
    """
 | 
|
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
284  | 
if not len(merge_sorted):  | 
285  | 
        return
 | 
|
286  | 
    # split merge_sorted into a map:
 | 
|
287  | 
revs = {}  | 
|
288  | 
    # FIXME: get a hint on this from the merge_sorted data rather than
 | 
|
289  | 
    # calculating it ourselves
 | 
|
290  | 
    # mapping from rev_id to the sequence number of the next lowest rev
 | 
|
291  | 
next_lower_rev = {}  | 
|
292  | 
    # mapping from rev_id to next-in-branch-revid - may be None for end
 | 
|
293  | 
    # of branch
 | 
|
294  | 
next_branch_revid = {}  | 
|
295  | 
    # the stack we are in in the sorted data for determining which 
 | 
|
296  | 
    # next_lower_rev to set. It is a stack which has one list at each
 | 
|
297  | 
    # depth - the ids at that depth that need the same id allocated.
 | 
|
298  | 
current_stack = [[]]  | 
|
299  | 
for seq, revid, indent, end_merge in merge_sorted:  | 
|
300  | 
revs[revid] = (seq, indent, end_merge)  | 
|
301  | 
if indent == len(current_stack):  | 
|
302  | 
            # new merge group starts
 | 
|
303  | 
current_stack.append([revid])  | 
|
304  | 
elif indent == len(current_stack) - 1:  | 
|
305  | 
            # part of the current merge group
 | 
|
306  | 
current_stack[-1].append(revid)  | 
|
307  | 
else:  | 
|
308  | 
            # end of a merge group
 | 
|
309  | 
while current_stack[-1]:  | 
|
310  | 
stack_rev_id = current_stack[-1].pop()  | 
|
311  | 
                # record the next lower rev for this rev:
 | 
|
312  | 
next_lower_rev[stack_rev_id] = seq  | 
|
313  | 
                # if this followed a non-end-merge rev in this group note that
 | 
|
314  | 
if len(current_stack[-1]):  | 
|
315  | 
if not revs[current_stack[-1][-1]][2]:  | 
|
316  | 
next_branch_revid[current_stack[-1][-1]] = stack_rev_id  | 
|
317  | 
current_stack.pop()  | 
|
318  | 
            # append to the now-current merge group
 | 
|
319  | 
current_stack[-1].append(revid)  | 
|
320  | 
    # assign a value to all the depth 0 revisions
 | 
|
321  | 
while current_stack[-1]:  | 
|
322  | 
stack_rev_id = current_stack[-1].pop()  | 
|
323  | 
        # record the next lower rev for this rev:
 | 
|
324  | 
next_lower_rev[stack_rev_id] = len(merge_sorted)  | 
|
325  | 
        # if this followed a non-end-merge rev in this group note that
 | 
|
326  | 
if len(current_stack[-1]):  | 
|
327  | 
if not revs[current_stack[-1][-1]][2]:  | 
|
328  | 
next_branch_revid[current_stack[-1][-1]] = stack_rev_id  | 
|
329  | 
||
330  | 
    # a list of the current revisions we are drawing lines TO indicating
 | 
|
331  | 
    # the sequence of their lines on the screen.
 | 
|
332  | 
    # i.e. [A, B, C] means that the line to A, to B, and to C are in
 | 
|
333  | 
    # (respectively), 0, 1, 2 on the screen.
 | 
|
334  | 
hanging = [merge_sorted[0][1]]  | 
|
335  | 
for seq, revid, indent, end_merge in merge_sorted:  | 
|
336  | 
        # a list of the lines to draw: their position in the
 | 
|
337  | 
        # previous row, their position in this row, and the colour
 | 
|
338  | 
        # (which is the colour they are routing to).
 | 
|
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
339  | 
lines = []  | 
340  | 
||
341  | 
new_hanging = []  | 
|
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
342  | 
|
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
343  | 
for h_idx, hang in enumerate(hanging):  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
344  | 
            # one of these will be the current lines node:
 | 
345  | 
            # we are drawing a line. h_idx 
 | 
|
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
346  | 
if hang == revid:  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
347  | 
                # we have found the current lines node
 | 
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
348  | 
node = (h_idx, colours[revid])  | 
349  | 
||
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
350  | 
                # note that we might have done the main parent
 | 
351  | 
drawn_parents = set()  | 
|
352  | 
||
353  | 
def draw_line(from_idx, to_idx, revision_id):  | 
|
354  | 
try:  | 
|
355  | 
n_idx = new_hanging.index(revision_id)  | 
|
356  | 
except ValueError:  | 
|
357  | 
                        # force this to be vertical at the place this rev was
 | 
|
358  | 
                        # drawn.
 | 
|
359  | 
new_hanging.insert(to_idx, revision_id)  | 
|
360  | 
n_idx = to_idx  | 
|
361  | 
lines.append((from_idx, n_idx, colours[revision_id]))  | 
|
362  | 
||
363  | 
||
364  | 
                # we want to draw a line to the next commit on 'this' branch
 | 
|
365  | 
if not end_merge:  | 
|
366  | 
                    # drop this line first.
 | 
|
367  | 
parent_id = next_branch_revid[revid]  | 
|
368  | 
draw_line(h_idx, h_idx, parent_id)  | 
|
369  | 
                    # we have drawn this parent
 | 
|
370  | 
drawn_parents.add(parent_id)  | 
|
371  | 
else:  | 
|
372  | 
                    # this is the last revision in a 'merge', show where it came from
 | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
373  | 
if len(revisions[revid].parent_ids) > 1:  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
374  | 
                        # having > 1
 | 
375  | 
                        # parents means this commit was a merge, and being
 | 
|
376  | 
                        # the end point of a merge group means that all
 | 
|
377  | 
                        # the parent revisions were merged into branches
 | 
|
378  | 
                        # to the left of this before this was committed
 | 
|
379  | 
                        # - so we want to show this as a new branch from
 | 
|
380  | 
                        # those revisions.
 | 
|
381  | 
                        # to do this, we show the parent with the lowest
 | 
|
382  | 
                        # sequence number, which is the one that this
 | 
|
383  | 
                        # branch 'spawned from', and no others.
 | 
|
384  | 
                        # If this sounds like a problem, remember that:
 | 
|
385  | 
                        # if the parent was not already in our mainline
 | 
|
386  | 
                        # it would show up as a merge into this making
 | 
|
387  | 
                        # this not the end of a merge-line.
 | 
|
388  | 
lowest = len(merge_sorted)  | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
389  | 
for parent_id in revisions[revid].parent_ids:  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
390  | 
if revs[parent_id][0] < lowest:  | 
391  | 
lowest = revs[parent_id][0]  | 
|
392  | 
assert lowest != len(merge_sorted)  | 
|
393  | 
draw_line(h_idx, len(new_hanging), merge_sorted[lowest][1])  | 
|
394  | 
drawn_parents.add(merge_sorted[lowest][1])  | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
395  | 
elif len(revisions[revid].parent_ids) == 1:  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
396  | 
                        # only one parent, must show this link to be useful.
 | 
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
397  | 
parent_id = revisions[revid].parent_ids[0]  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
398  | 
draw_line(h_idx, len(new_hanging), parent_id)  | 
399  | 
drawn_parents.add(parent_id)  | 
|
400  | 
||
401  | 
                # what do we want to draw lines to from here:
 | 
|
402  | 
                # each parent IF its relevant.
 | 
|
403  | 
                #
 | 
|
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
404  | 
                # Now we need to hang its parents, we put them at the point
 | 
405  | 
                # the old column was so anything to the right of this has
 | 
|
406  | 
                # to move outwards to make room.  We also try and collapse
 | 
|
407  | 
                # hangs to keep the graph small.
 | 
|
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
408  | 
                # RBC: we do not draw lines to parents that were already merged
 | 
409  | 
                # unless its the last revision in a merge group.
 | 
|
| 
37.1.3
by Robert Collins
 Some more tweaking on the graph stuff - reducing duplicate effort and leveraging bzrlib more.  | 
410  | 
for parent_id in revisions[revid].parent_ids:  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
411  | 
if parent_id in drawn_parents:  | 
412  | 
                        continue
 | 
|
413  | 
parent_seq = revs[parent_id][0]  | 
|
414  | 
parent_depth = revs[parent_id][1]  | 
|
415  | 
if parent_depth == indent + 1:  | 
|
| 
41
by David Allouche
 restore --maxnum functionality, reflush some comments  | 
416  | 
                        # The parent was a merge into this branch determine if
 | 
417  | 
                        # it was already merged into the mainline via a
 | 
|
418  | 
                        # different merge: if all revisions between us and
 | 
|
419  | 
                        # parent_seq have a indent greater than there are no
 | 
|
420  | 
                        # revisions with a lower indent than us.
 | 
|
421  | 
                        # We do not use 'parent_depth < indent' because that
 | 
|
422  | 
                        # would allow un-uniqueified merges to show up, and
 | 
|
423  | 
                        # merge_sorted should take care of that for us (but
 | 
|
424  | 
                        # does not trim the values)
 | 
|
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
425  | 
if parent_seq < next_lower_rev[revid]:  | 
426  | 
draw_line(h_idx, len(new_hanging), parent_id)  | 
|
427  | 
elif parent_depth == indent and parent_seq == seq + 1:  | 
|
428  | 
                        # part of this branch
 | 
|
429  | 
draw_line(h_idx, len(new_hanging), parent_id)  | 
|
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
430  | 
else:  | 
| 
37.1.2
by Robert Collins
 Make revision sorting and linking use merge_sorted from latest bzr.dev. This  | 
431  | 
                # draw a line from the previous position of this line to the 
 | 
432  | 
                # new position.
 | 
|
433  | 
                # h_idx is the old position.
 | 
|
434  | 
                # new_indent is the new position. 
 | 
|
435  | 
draw_line(h_idx, len(new_hanging), hang)  | 
|
436  | 
        # we've calculated the row, assign new_hanging to hanging to setup for
 | 
|
437  | 
        # the next row
 | 
|
| 
1
by Scott James Remnant
 Commit the first version of bzrk.  | 
438  | 
hanging = new_hanging  | 
439  | 
||
440  | 
yield (revisions[revid], node, lines)  | 
|
| 
2
by Scott James Remnant
 Split the same branch functionality out into a separate function so  | 
441  | 
|
| 
27
by David Allouche
 refactor distances  | 
442  | 
|
| 
2
by Scott James Remnant
 Split the same branch functionality out into a separate function so  | 
443  | 
def same_branch(a, b):  | 
444  | 
"""Return whether we think revisions a and b are on the same branch."""  | 
|
445  | 
if len(a.parent_ids) == 1:  | 
|
446  | 
        # Defacto same branch if only parent
 | 
|
447  | 
return True  | 
|
448  | 
elif a.committer == b.committer:  | 
|
449  | 
        # Same committer so may as well be
 | 
|
450  | 
return True  | 
|
451  | 
else:  | 
|
452  | 
return False  |