Skip to content

Commit 4edf6c1

Browse files
committed
Partial fix for libgit2#6532: insert-by-date order.
This commit fixes the following issues: 1. In `git_revwalk__push_commit`, if `opts->insert_by_date` is true, `git_commit_list_insert_by_date` is called. However, by this point the commit wasn’t parsed yet, so the `time` field still has 0 as value. Solved by parsing the commit immediately if `opts->insert_by_date` is true. 2. In the same function, there was an error in the boolean logic. When `opts->insert_by_date` was true, the commit would be inserted twice, first “by date” (not really, due to the issue described above) and then in the first position again. Logic was corrected. 3. In `prepare_walk`, when processing `user_input` and building the `commits` list, the order was being inverted. Assuming both fixes above, this would mean we would start negotiation by the oldest reference, not the newest one. This was fixed by producing a `commits` list in the same order as `user_input`. The output list for the list of “have” statements during the negotiation is still not the same as core git’s because there is an optimization missing (excluding ancestors of commits known to be common between the client and the server) but this commit brings it much closer to core git’s. Specifically, the example on the libgit2#6532 issue description now fetches exactly the same objects than core git, and other examples I tested as well.
1 parent 2f20fe8 commit 4edf6c1

File tree

3 files changed

+35
-7
lines changed

3 files changed

+35
-7
lines changed

src/libgit2/commit_list.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,18 @@ int git_commit_list_time_cmp(const void *a, const void *b)
4343
return 0;
4444
}
4545

46-
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
47-
{
46+
git_commit_list *git_commit_list_create(git_commit_list_node *item, git_commit_list *next) {
4847
git_commit_list *new_list = git__malloc(sizeof(git_commit_list));
4948
if (new_list != NULL) {
5049
new_list->item = item;
51-
new_list->next = *list_p;
50+
new_list->next = next;
5251
}
52+
return new_list;
53+
}
54+
55+
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
56+
{
57+
git_commit_list *new_list = git_commit_list_create(item, *list_p);
5358
*list_p = new_list;
5459
return new_list;
5560
}

src/libgit2/commit_list.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
4949
int git_commit_list_generation_cmp(const void *a, const void *b);
5050
int git_commit_list_time_cmp(const void *a, const void *b);
5151
void git_commit_list_free(git_commit_list **list_p);
52+
git_commit_list *git_commit_list_create(git_commit_list_node *item, git_commit_list *next);
5253
git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
5354
git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p);
5455
int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit);

src/libgit2/revwalk.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,13 @@ int git_revwalk__push_commit(git_revwalk *walk, const git_oid *oid, const git_re
8383

8484
commit->uninteresting = opts->uninteresting;
8585
list = walk->user_input;
86-
if ((opts->insert_by_date &&
87-
git_commit_list_insert_by_date(commit, &list) == NULL) ||
86+
87+
/* To insert by date, we need to parse so we know the date. */
88+
if (opts->insert_by_date && ((error = git_commit_list_parse(walk, commit)) < 0))
89+
return error;
90+
91+
if ((opts->insert_by_date == 0 ||
92+
git_commit_list_insert_by_date(commit, &list) == NULL) &&
8893
git_commit_list_insert(commit, &list) == NULL) {
8994
git_error_set_oom();
9095
return -1;
@@ -609,7 +614,7 @@ static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, g
609614
static int prepare_walk(git_revwalk *walk)
610615
{
611616
int error = 0;
612-
git_commit_list *list, *commits = NULL;
617+
git_commit_list *list, *commits = NULL, *commits_last = NULL;
613618
git_commit_list_node *next;
614619

615620
/* If there were no pushes, we know that the walk is already over */
@@ -618,6 +623,12 @@ static int prepare_walk(git_revwalk *walk)
618623
return GIT_ITEROVER;
619624
}
620625

626+
/*
627+
* This is a bit convoluted, but necessary to maintain the order of
628+
* the commits. This is especially important in situations where
629+
* git_revwalk__push_glob is called with a git_revwalk__push_options
630+
* setting insert_by_date = 1, which is critical for fetch negotiation.
631+
*/
621632
for (list = walk->user_input; list; list = list->next) {
622633
git_commit_list_node *commit = list->item;
623634
if ((error = git_commit_list_parse(walk, commit)) < 0)
@@ -627,8 +638,19 @@ static int prepare_walk(git_revwalk *walk)
627638
mark_parents_uninteresting(commit);
628639

629640
if (!commit->seen) {
641+
git_commit_list *new_list = NULL;
642+
if ((new_list = git_commit_list_create(commit, NULL)) == NULL) {
643+
git_error_set_oom();
644+
return -1;
645+
}
646+
630647
commit->seen = 1;
631-
git_commit_list_insert(commit, &commits);
648+
if (commits_last == NULL)
649+
commits = new_list;
650+
else
651+
commits_last->next = new_list;
652+
653+
commits_last = new_list;
632654
}
633655
}
634656

0 commit comments

Comments
 (0)