Skip to content

Commit 48923e8

Browse files
committed
Merge branch 'ds/merge-base-independent'
The code to implement "git merge-base --independent" was poorly done and was kept from the very beginning of the feature. * ds/merge-base-independent: commit-reach: stale commits may prune generation further commit-reach: use heuristic in remove_redundant() commit-reach: move compare_commits_by_gen commit-reach: use one walk in remove_redundant() commit-reach: reduce requirements for remove_redundant()
2 parents 682bbad + 41f3c99 commit 48923e8

File tree

1 file changed

+165
-25
lines changed

1 file changed

+165
-25
lines changed

commit-reach.c

Lines changed: 165 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,25 @@
1717

1818
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
1919

20+
static int compare_commits_by_gen(const void *_a, const void *_b)
21+
{
22+
const struct commit *a = *(const struct commit * const *)_a;
23+
const struct commit *b = *(const struct commit * const *)_b;
24+
25+
timestamp_t generation_a = commit_graph_generation(a);
26+
timestamp_t generation_b = commit_graph_generation(b);
27+
28+
if (generation_a < generation_b)
29+
return -1;
30+
if (generation_a > generation_b)
31+
return 1;
32+
if (a->date < b->date)
33+
return -1;
34+
if (a->date > b->date)
35+
return 1;
36+
return 0;
37+
}
38+
2039
static int queue_has_nonstale(struct prio_queue *queue)
2140
{
2241
int i;
@@ -156,14 +175,9 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
156175
return ret;
157176
}
158177

159-
static int remove_redundant(struct repository *r, struct commit **array, int cnt)
178+
static int remove_redundant_no_gen(struct repository *r,
179+
struct commit **array, int cnt)
160180
{
161-
/*
162-
* Some commit in the array may be an ancestor of
163-
* another commit. Move such commit to the end of
164-
* the array, and return the number of commits that
165-
* are independent from each other.
166-
*/
167181
struct commit **work;
168182
unsigned char *redundant;
169183
int *filled_index;
@@ -209,15 +223,156 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt
209223
for (i = filled = 0; i < cnt; i++)
210224
if (!redundant[i])
211225
array[filled++] = work[i];
212-
for (j = filled, i = 0; i < cnt; i++)
213-
if (redundant[i])
214-
array[j++] = work[i];
215226
free(work);
216227
free(redundant);
217228
free(filled_index);
218229
return filled;
219230
}
220231

232+
static int remove_redundant_with_gen(struct repository *r,
233+
struct commit **array, int cnt)
234+
{
235+
int i, count_non_stale = 0, count_still_independent = cnt;
236+
timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
237+
struct commit **walk_start, **sorted;
238+
size_t walk_start_nr = 0, walk_start_alloc = cnt;
239+
int min_gen_pos = 0;
240+
241+
/*
242+
* Sort the input by generation number, ascending. This allows
243+
* us to increase the "min_generation" limit when we discover
244+
* the commit with lowest generation is STALE. The index
245+
* min_gen_pos points to the current position within 'array'
246+
* that is not yet known to be STALE.
247+
*/
248+
ALLOC_ARRAY(sorted, cnt);
249+
COPY_ARRAY(sorted, array, cnt);
250+
QSORT(sorted, cnt, compare_commits_by_gen);
251+
min_generation = commit_graph_generation(sorted[0]);
252+
253+
ALLOC_ARRAY(walk_start, walk_start_alloc);
254+
255+
/* Mark all parents of the input as STALE */
256+
for (i = 0; i < cnt; i++) {
257+
struct commit_list *parents;
258+
259+
repo_parse_commit(r, array[i]);
260+
array[i]->object.flags |= RESULT;
261+
parents = array[i]->parents;
262+
263+
while (parents) {
264+
repo_parse_commit(r, parents->item);
265+
if (!(parents->item->object.flags & STALE)) {
266+
parents->item->object.flags |= STALE;
267+
ALLOC_GROW(walk_start, walk_start_nr + 1, walk_start_alloc);
268+
walk_start[walk_start_nr++] = parents->item;
269+
}
270+
parents = parents->next;
271+
}
272+
}
273+
274+
QSORT(walk_start, walk_start_nr, compare_commits_by_gen);
275+
276+
/* remove STALE bit for now to allow walking through parents */
277+
for (i = 0; i < walk_start_nr; i++)
278+
walk_start[i]->object.flags &= ~STALE;
279+
280+
/*
281+
* Start walking from the highest generation. Hopefully, it will
282+
* find all other items during the first-parent walk, and we can
283+
* terminate early. Otherwise, we will do the same amount of work
284+
* as before.
285+
*/
286+
for (i = walk_start_nr - 1; i >= 0 && count_still_independent > 1; i--) {
287+
/* push the STALE bits up to min generation */
288+
struct commit_list *stack = NULL;
289+
290+
commit_list_insert(walk_start[i], &stack);
291+
walk_start[i]->object.flags |= STALE;
292+
293+
while (stack) {
294+
struct commit_list *parents;
295+
struct commit *c = stack->item;
296+
297+
repo_parse_commit(r, c);
298+
299+
if (c->object.flags & RESULT) {
300+
c->object.flags &= ~RESULT;
301+
if (--count_still_independent <= 1)
302+
break;
303+
if (oideq(&c->object.oid, &sorted[min_gen_pos]->object.oid)) {
304+
while (min_gen_pos < cnt - 1 &&
305+
(sorted[min_gen_pos]->object.flags & STALE))
306+
min_gen_pos++;
307+
min_generation = commit_graph_generation(sorted[min_gen_pos]);
308+
}
309+
}
310+
311+
if (commit_graph_generation(c) < min_generation) {
312+
pop_commit(&stack);
313+
continue;
314+
}
315+
316+
parents = c->parents;
317+
while (parents) {
318+
if (!(parents->item->object.flags & STALE)) {
319+
parents->item->object.flags |= STALE;
320+
commit_list_insert(parents->item, &stack);
321+
break;
322+
}
323+
parents = parents->next;
324+
}
325+
326+
/* pop if all parents have been visited already */
327+
if (!parents)
328+
pop_commit(&stack);
329+
}
330+
free_commit_list(stack);
331+
}
332+
free(sorted);
333+
334+
/* clear result */
335+
for (i = 0; i < cnt; i++)
336+
array[i]->object.flags &= ~RESULT;
337+
338+
/* rearrange array */
339+
for (i = count_non_stale = 0; i < cnt; i++) {
340+
if (!(array[i]->object.flags & STALE))
341+
array[count_non_stale++] = array[i];
342+
}
343+
344+
/* clear marks */
345+
clear_commit_marks_many(walk_start_nr, walk_start, STALE);
346+
free(walk_start);
347+
348+
return count_non_stale;
349+
}
350+
351+
static int remove_redundant(struct repository *r, struct commit **array, int cnt)
352+
{
353+
/*
354+
* Some commit in the array may be an ancestor of
355+
* another commit. Move the independent commits to the
356+
* beginning of 'array' and return their number. Callers
357+
* should not rely upon the contents of 'array' after
358+
* that number.
359+
*/
360+
if (generation_numbers_enabled(r)) {
361+
int i;
362+
363+
/*
364+
* If we have a single commit with finite generation
365+
* number, then the _with_gen algorithm is preferred.
366+
*/
367+
for (i = 0; i < cnt; i++) {
368+
if (commit_graph_generation(array[i]) < GENERATION_NUMBER_INFINITY)
369+
return remove_redundant_with_gen(r, array, cnt);
370+
}
371+
}
372+
373+
return remove_redundant_no_gen(r, array, cnt);
374+
}
375+
221376
static struct commit_list *get_merge_bases_many_0(struct repository *r,
222377
struct commit *one,
223378
int n,
@@ -561,21 +716,6 @@ int commit_contains(struct ref_filter *filter, struct commit *commit,
561716
return repo_is_descendant_of(the_repository, commit, list);
562717
}
563718

564-
static int compare_commits_by_gen(const void *_a, const void *_b)
565-
{
566-
const struct commit *a = *(const struct commit * const *)_a;
567-
const struct commit *b = *(const struct commit * const *)_b;
568-
569-
timestamp_t generation_a = commit_graph_generation(a);
570-
timestamp_t generation_b = commit_graph_generation(b);
571-
572-
if (generation_a < generation_b)
573-
return -1;
574-
if (generation_a > generation_b)
575-
return 1;
576-
return 0;
577-
}
578-
579719
int can_all_from_reach_with_flag(struct object_array *from,
580720
unsigned int with_flag,
581721
unsigned int assign_flag,

0 commit comments

Comments
 (0)