40 | | |
40 | | |
41 | o 2 |
41 | o 2 |
42 |/ |
42 |/ |
43 o 0 |
43 o 0 |
44 |
44 |
45 Currently consider every changeset under a merge to be on the same branch |
45 Note that the ancestors of merges are understood by the current |
46 using revision number to sort them. |
46 algorithm to be on the same branch. This means no reordering will |
|
47 occur behind a merge. |
47 """ |
48 """ |
48 |
49 |
49 ### Quick summary of the algorithm |
50 ### Quick summary of the algorithm |
50 # |
51 # |
51 # This function is based around a "retention" principle. We keep revisions |
52 # This function is based around a "retention" principle. We keep revisions |
52 # in memory until we are ready to emit a whole branch that immediately |
53 # in memory until we are ready to emit a whole branch that immediately |
53 # "merge" into an existing one. This reduce the number of branch "ongoing" |
54 # "merges" into an existing one. This reduces the number of parallel |
54 # at the same time. |
55 # branches with interleaved revisions. |
55 # |
56 # |
56 # During iteration revs are split into two groups: |
57 # During iteration revs are split into two groups: |
57 # A) revision already emitted |
58 # A) revision already emitted |
58 # B) revision in "retention". They are stored as different subgroups. |
59 # B) revision in "retention". They are stored as different subgroups. |
59 # |
60 # |
60 # for each REV, we do the follow logic: |
61 # for each REV, we do the following logic: |
61 # |
62 # |
62 # a) if REV is a parent of (A), we will emit it. But before emitting it, |
63 # 1) if REV is a parent of (A), we will emit it. If there is a |
63 # we'll "free" all the revs from subgroup in (B) that were waiting for |
64 # retention group ((B) above) that is blocked on REV being |
64 # REV to be available. So we emit all revision of such subgroup before |
65 # available, we emit all the revisions out of that retention |
65 # emitting REV |
66 # group first. |
66 # |
67 # |
67 # b) else, we'll search for a subgroup in (B) awaiting for REV to be |
68 # 2) else, we'll search for a subgroup in (B) awaiting for REV to be |
68 # available, if such subgroup exist, we add REV to it and the subgroup is |
69 # available, if such subgroup exist, we add REV to it and the subgroup is |
69 # now awaiting for REV.parents() to be available. |
70 # now awaiting for REV.parents() to be available. |
70 # |
71 # |
71 # c) finally if no such group existed in (B), we create a new subgroup. |
72 # 3) finally if no such group existed in (B), we create a new subgroup. |
72 # |
73 # |
73 # |
74 # |
74 # To bootstrap the algorithm, we emit the tipmost revision. |
75 # To bootstrap the algorithm, we emit the tipmost revision (which |
|
76 # puts it in group (A) from above). |
75 |
77 |
76 revs.sort(reverse=True) |
78 revs.sort(reverse=True) |
77 |
79 |
78 # Set of parents of revision that have been yield. They can be considered |
80 # Set of parents of revision that have been emitted. They can be considered |
79 # unblocked as the graph generator is already aware of them so there is no |
81 # unblocked as the graph generator is already aware of them so there is no |
80 # need to delay the one that reference them. |
82 # need to delay the revisions that reference them. |
81 # |
83 # |
82 # If someone wants to prioritize a branch over the others, pre-filling this |
84 # If someone wants to prioritize a branch over the others, pre-filling this |
83 # set will force all other branches to wait until this branch is ready to be |
85 # set will force all other branches to wait until this branch is ready to be |
84 # outputed. |
86 # emitted. |
85 unblocked = set(firstbranch) |
87 unblocked = set(firstbranch) |
86 |
88 |
87 # list of group waiting to be displayed, each group is defined by: |
89 # list of groups waiting to be displayed, each group is defined by: |
88 # |
90 # |
89 # (revs: lists of revs waiting to be displayed, |
91 # (revs: lists of revs waiting to be displayed, |
90 # blocked: set of that cannot be displayed before those in 'revs') |
92 # blocked: set of that cannot be displayed before those in 'revs') |
91 # |
93 # |
92 # The second value ('blocked') correspond to parents of any revision in the |
94 # The second value ('blocked') correspond to parents of any revision in the |
93 # group ('revs') that is not itself contained in the group. The main idea |
95 # group ('revs') that is not itself contained in the group. The main idea |
94 # of this algorithm is to delay as much as possible the emission of any |
96 # of this algorithm is to delay as much as possible the emission of any |
95 # revision. This means waiting for the moment we are about to display |
97 # revision. This means waiting for the moment we are about to display |
96 # theses parents to display the revs in a group. |
98 # these parents to display the revs in a group. |
97 # |
99 # |
98 # This first implementation is smart until it meet a merge: it will emit |
100 # This first implementation is smart until it encounters a merge: it will |
99 # revs as soon as any parents is about to be emitted and can grow an |
101 # emit revs as soon as any parent is about to be emitted and can grow an |
100 # arbitrary number of revs in 'blocked'. In practice this mean we properly |
102 # arbitrary number of revs in 'blocked'. In practice this mean we properly |
101 # retains new branches but give up on any special ordering for ancestors of |
103 # retains new branches but gives up on any special ordering for ancestors |
102 # merges. The implementation can be improved to handle this better. |
104 # of merges. The implementation can be improved to handle this better. |
103 # |
105 # |
104 # The first subgroup is special. It correspond to all the revision that |
106 # The first subgroup is special. It corresponds to all the revision that |
105 # were already emitted. The 'revs' lists is expected to be empty and the |
107 # were already emitted. The 'revs' lists is expected to be empty and the |
106 # 'blocked' set contains the parents revisions of already emitted revision. |
108 # 'blocked' set contains the parents revisions of already emitted revision. |
107 # |
109 # |
108 # You could pre-seed the <parents> set of groups[0] to a specific |
110 # You could pre-seed the <parents> set of groups[0] to a specific |
109 # changesets to select what the first emitted branch should be. |
111 # changesets to select what the first emitted branch should be. |
128 |
130 |
129 # Seek for a subgroup blocked, waiting for the current revision. |
131 # Seek for a subgroup blocked, waiting for the current revision. |
130 matching = [i for i, g in enumerate(groups) if rev in g[1]] |
132 matching = [i for i, g in enumerate(groups) if rev in g[1]] |
131 |
133 |
132 if matching: |
134 if matching: |
133 # The main idea is to gather together all sets that await on |
135 # The main idea is to gather together all sets that are blocked |
134 # the same revision. |
136 # on the same revision. |
135 # |
137 # |
136 # This merging is done at the time we are about to add this |
138 # Groups are merged when a common blocking ancestor is |
137 # common awaited to the subgroup for simplicity purpose. Such |
139 # observed. For example, given two groups: |
138 # merge could happen sooner when we update the "blocked" set of |
140 # |
139 # revision. |
141 # revs [5, 4] waiting for 1 |
|
142 # revs [3, 2] waiting for 1 |
|
143 # |
|
144 # These two groups will be merged when we process |
|
145 # 1. In theory, we could have merged the groups when |
|
146 # we added 2 to the group it is now in (we could have |
|
147 # noticed the groups were both blocked on 1 then), but |
|
148 # the way it works now makes the algorithm simpler. |
140 # |
149 # |
141 # We also always keep the oldest subgroup first. We can |
150 # We also always keep the oldest subgroup first. We can |
142 # probably improve the behavior by having the longuest set |
151 # probably improve the behavior by having the longest set |
143 # first. That way, graph algorythms could minimise the length |
152 # first. That way, graph algorithms could minimise the length |
144 # of parallele lines their draw. This is currently not done. |
153 # of parallel lines their drawing. This is currently not done. |
145 targetidx = matching.pop(0) |
154 targetidx = matching.pop(0) |
146 trevs, tparents = groups[targetidx] |
155 trevs, tparents = groups[targetidx] |
147 for i in matching: |
156 for i in matching: |
148 gr = groups[i] |
157 gr = groups[i] |
149 trevs.extend(gr[0]) |
158 trevs.extend(gr[0]) |
150 tparents |= gr[1] |
159 tparents |= gr[1] |
151 # delete all merged subgroups (but the one we keep) (starting |
160 # delete all merged subgroups (except the one we kept) |
152 # from the last subgroup for performance and sanity reason) |
161 # (starting from the last subgroup for performance and |
|
162 # sanity reasons) |
153 for i in reversed(matching): |
163 for i in reversed(matching): |
154 del groups[i] |
164 del groups[i] |
155 else: |
165 else: |
156 # This is a new head. We create a new subgroup for it. |
166 # This is a new head. We create a new subgroup for it. |
157 targetidx = len(groups) |
167 targetidx = len(groups) |
158 groups.append(([], set([rev]))) |
168 groups.append(([], set([rev]))) |
159 |
169 |
160 gr = groups[targetidx] |
170 gr = groups[targetidx] |
161 |
171 |
162 # We now adds the current nodes to this subgroups. This is done |
172 # We now add the current nodes to this subgroups. This is done |
163 # after the subgroup merging because all elements from a subgroup |
173 # after the subgroup merging because all elements from a subgroup |
164 # that relied on this rev must preceed it. |
174 # that relied on this rev must precede it. |
165 # |
175 # |
166 # we also update the <parents> set to includes the parents on the |
176 # we also update the <parents> set to include the parents of the |
167 # new nodes. |
177 # new nodes. |
168 if rev == currentrev: # only display stuff in rev |
178 if rev == currentrev: # only display stuff in rev |
169 gr[0].append(rev) |
179 gr[0].append(rev) |
170 gr[1].remove(rev) |
180 gr[1].remove(rev) |
171 parents = [p for p in parentsfunc(rev) if p > nullrev] |
181 parents = [p for p in parentsfunc(rev) if p > nullrev] |
175 pendingset.add(p) |
185 pendingset.add(p) |
176 heappush(pendingheap, -p) |
186 heappush(pendingheap, -p) |
177 |
187 |
178 # Look for a subgroup to display |
188 # Look for a subgroup to display |
179 # |
189 # |
180 # When unblocked is empty (if clause), We are not waiting over any |
190 # When unblocked is empty (if clause), we were not waiting for any |
181 # revision during the first iteration (if no priority was given) or |
191 # revisions during the first iteration (if no priority was given) or |
182 # if we outputed a whole disconnected sets of the graph (reached a |
192 # if we emitted a whole disconnected set of the graph (reached a |
183 # root). In that case we arbitrarily takes the oldest known |
193 # root). In that case we arbitrarily take the oldest known |
184 # subgroup. The heuristique could probably be better. |
194 # subgroup. The heuristic could probably be better. |
185 # |
195 # |
186 # Otherwise (elif clause) this mean we have some emitted revision. |
196 # Otherwise (elif clause) if the subgroup is blocked on |
187 # if the subgroup awaits on the same revision that the outputed |
197 # a revision we just emitted, we can safely emit it as |
188 # ones, we can safely output it. |
198 # well. |
189 if not unblocked: |
199 if not unblocked: |
190 if len(groups) > 1: # display other subset |
200 if len(groups) > 1: # display other subset |
191 targetidx = 1 |
201 targetidx = 1 |
192 gr = groups[1] |
202 gr = groups[1] |
193 elif not gr[1] & unblocked: |
203 elif not gr[1] & unblocked: |