Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1318ec9

Browse files
author
Vicent Marti
committed
Merge pull request libgit2#3492 from libgit2/vmg/redundant
merge-base: Remove redundant merge bases
2 parents d571a54 + b656e5e commit 1318ec9

File tree

10 files changed

+202
-22
lines changed

10 files changed

+202
-22
lines changed

src/commit_list.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define PARENT2 (1 << 1)
1414
#define RESULT (1 << 2)
1515
#define STALE (1 << 3)
16+
#define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT)
1617

1718
#define PARENTS_PER_COMMIT 2
1819
#define COMMIT_ALLOC \

src/merge.c

Lines changed: 170 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -302,30 +302,59 @@ static int interesting(git_pqueue *list)
302302
return 0;
303303
}
304304

305-
int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
305+
static void clear_commit_marks_1(git_commit_list **plist,
306+
git_commit_list_node *commit, unsigned int mark)
306307
{
307-
int error;
308-
unsigned int i;
309-
git_commit_list_node *two;
310-
git_commit_list *result = NULL, *tmp = NULL;
311-
git_pqueue list;
308+
while (commit) {
309+
unsigned int i;
312310

313-
/* If there's only the one commit, there can be no merge bases */
314-
if (twos->length == 0) {
315-
*out = NULL;
316-
return 0;
311+
if (!(mark & commit->flags))
312+
return;
313+
314+
commit->flags &= ~mark;
315+
316+
for (i = 1; i < commit->out_degree; i++) {
317+
git_commit_list_node *p = commit->parents[i];
318+
git_commit_list_insert(p, plist);
319+
}
320+
321+
commit = commit->out_degree ? commit->parents[0] : NULL;
317322
}
323+
}
318324

319-
/* if the commit is repeated, we have a our merge base already */
320-
git_vector_foreach(twos, i, two) {
321-
if (one == two)
322-
return git_commit_list_insert(one, out) ? 0 : -1;
325+
static void clear_commit_marks_many(git_vector *commits, unsigned int mark)
326+
{
327+
git_commit_list *list = NULL;
328+
git_commit_list_node *c;
329+
unsigned int i;
330+
331+
git_vector_foreach(commits, i, c) {
332+
git_commit_list_insert(c, &list);
323333
}
324334

325-
if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
326-
return -1;
335+
while (list)
336+
clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
337+
}
327338

328-
if (git_commit_list_parse(walk, one) < 0)
339+
static void clear_commit_marks(git_commit_list_node *commit, unsigned int mark)
340+
{
341+
git_commit_list *list = NULL;
342+
git_commit_list_insert(commit, &list);
343+
while (list)
344+
clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
345+
}
346+
347+
static int paint_down_to_common(
348+
git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
349+
{
350+
git_pqueue list;
351+
git_commit_list *result = NULL;
352+
git_commit_list_node *two;
353+
354+
int error;
355+
unsigned int i;
356+
357+
if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
329358
return -1;
330359

331360
one->flags |= PARENT1;
@@ -376,19 +405,138 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
376405
}
377406

378407
git_pqueue_free(&list);
408+
*out = result;
409+
return 0;
410+
}
411+
412+
static int remove_redundant(git_revwalk *walk, git_vector *commits)
413+
{
414+
git_vector work = GIT_VECTOR_INIT;
415+
unsigned char *redundant;
416+
unsigned int *filled_index;
417+
unsigned int i, j;
418+
int error = 0;
419+
420+
redundant = git__calloc(commits->length, 1);
421+
GITERR_CHECK_ALLOC(redundant);
422+
filled_index = git__calloc((commits->length - 1), sizeof(unsigned int));
423+
GITERR_CHECK_ALLOC(filled_index);
424+
425+
for (i = 0; i < commits->length; ++i) {
426+
if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0)
427+
goto done;
428+
}
429+
430+
for (i = 0; i < commits->length; ++i) {
431+
git_commit_list *common = NULL;
432+
git_commit_list_node *commit = commits->contents[i];
433+
434+
if (redundant[i])
435+
continue;
436+
437+
git_vector_clear(&work);
438+
439+
for (j = 0; j < commits->length; j++) {
440+
if (i == j || redundant[j])
441+
continue;
442+
443+
filled_index[work.length] = j;
444+
if ((error = git_vector_insert(&work, commits->contents[j])) < 0)
445+
goto done;
446+
}
447+
448+
error = paint_down_to_common(&common, walk, commit, &work);
449+
if (error < 0)
450+
goto done;
451+
452+
if (commit->flags & PARENT2)
453+
redundant[i] = 1;
454+
455+
for (j = 0; j < work.length; j++) {
456+
git_commit_list_node *w = work.contents[j];
457+
if (w->flags & PARENT1)
458+
redundant[filled_index[j]] = 1;
459+
}
460+
461+
clear_commit_marks(commit, ALL_FLAGS);
462+
clear_commit_marks_many(&work, ALL_FLAGS);
463+
464+
git_commit_list_free(&common);
465+
}
466+
467+
for (i = 0; i < commits->length; ++i) {
468+
if (redundant[i])
469+
commits->contents[i] = NULL;
470+
}
471+
472+
done:
473+
git__free(redundant);
474+
git__free(filled_index);
475+
git_vector_free(&work);
476+
return error;
477+
}
478+
479+
int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
480+
{
481+
int error;
482+
unsigned int i;
483+
git_commit_list_node *two;
484+
git_commit_list *result = NULL, *tmp = NULL;
485+
486+
/* If there's only the one commit, there can be no merge bases */
487+
if (twos->length == 0) {
488+
*out = NULL;
489+
return 0;
490+
}
491+
492+
/* if the commit is repeated, we have a our merge base already */
493+
git_vector_foreach(twos, i, two) {
494+
if (one == two)
495+
return git_commit_list_insert(one, out) ? 0 : -1;
496+
}
497+
498+
if (git_commit_list_parse(walk, one) < 0)
499+
return -1;
500+
501+
error = paint_down_to_common(&result, walk, one, twos);
502+
if (error < 0)
503+
return error;
379504

380505
/* filter out any stale commits in the results */
381506
tmp = result;
382507
result = NULL;
383508

384509
while (tmp) {
385-
struct git_commit_list *next = tmp->next;
386-
if (!(tmp->item->flags & STALE))
387-
if (git_commit_list_insert_by_date(tmp->item, &result) == NULL)
510+
git_commit_list_node *c = git_commit_list_pop(&tmp);
511+
if (!(c->flags & STALE))
512+
if (git_commit_list_insert_by_date(c, &result) == NULL)
388513
return -1;
514+
}
515+
516+
/*
517+
* more than one merge base -- see if there are redundant merge
518+
* bases and remove them
519+
*/
520+
if (result && result->next) {
521+
git_vector redundant = GIT_VECTOR_INIT;
522+
523+
while (result)
524+
git_vector_insert(&redundant, git_commit_list_pop(&result));
525+
526+
clear_commit_marks(one, ALL_FLAGS);
527+
clear_commit_marks_many(twos, ALL_FLAGS);
528+
529+
if ((error = remove_redundant(walk, &redundant)) < 0) {
530+
git_vector_free(&redundant);
531+
return error;
532+
}
533+
534+
git_vector_foreach(&redundant, i, two) {
535+
if (two != NULL)
536+
git_commit_list_insert_by_date(two, &result);
537+
}
389538

390-
git__free(tmp);
391-
tmp = next;
539+
git_vector_free(&redundant);
392540
}
393541

394542
*out = result;

tests/resources/redundant.git/HEAD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ref: refs/heads/master

tests/resources/redundant.git/config

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[core]
2+
repositoryformatversion = 0
3+
filemode = true
4+
bare = true
5+
logallrefupdates = true
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
P pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack
2+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# pack-refs with: peeled fully-peeled
2+
e18fa2788e9c4e12d83150808a31dfbfb1ae364f refs/heads/master
3+
91f4b95df4a59504a9813ba66912562931d990e3 refs/heads/ref2/ref28

tests/resources/redundant.git/refs/.gitkeep

Whitespace-only changes.

tests/revwalk/mergebase.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,23 @@ void test_revwalk_mergebase__octopus_merge_branch(void)
492492
*
493493
* a
494494
*/
495+
496+
void test_revwalk_mergebase__remove_redundant(void)
497+
{
498+
git_repository *repo;
499+
git_oid one, two, base;
500+
git_oidarray result = {NULL, 0};
501+
502+
cl_git_pass(git_repository_open(&repo, cl_fixture("redundant.git")));
503+
504+
cl_git_pass(git_oid_fromstr(&one, "d89137c93ba1ee749214ff4ce52ae9137bc833f9"));
505+
cl_git_pass(git_oid_fromstr(&two, "91f4b95df4a59504a9813ba66912562931d990e3"));
506+
cl_git_pass(git_oid_fromstr(&base, "6cb1f2352d974e1c5a776093017e8772416ac97a"));
507+
508+
cl_git_pass(git_merge_bases(&result, repo, &one, &two));
509+
cl_assert_equal_i(1, result.count);
510+
cl_assert_equal_oid(&base, &result.ids[0]);
511+
512+
git_oidarray_free(&result);
513+
git_repository_free(repo);
514+
}

0 commit comments

Comments
 (0)