remote: make add_missing_tags() linear

The add_missing_tags() method currently has quadratic behavior.
This is due to a linear number (based on number of tags T) of
calls to in_merge_bases_many, which has linear performance (based
on number of commits C in the repository).

Replace this O(T * C) algorithm with an O(T + C) algorithm by
using get_reachable_subset(). We ignore the return list and focus
instead on the reachable_flag assigned to the commits we care
about, because we need to interact with the tag ref and not just
the commit object.

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Derrick Stolee 2018-11-02 06:14:49 -07:00 committed by Junio C Hamano
parent 4c7bb45269
commit 85daa01f6b

View File

@ -1205,9 +1205,36 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
* sent to the other side.
*/
if (sent_tips.nr) {
const int reachable_flag = 1;
struct commit_list *found_commits;
struct commit **src_commits;
int nr_src_commits = 0, alloc_src_commits = 16;
ALLOC_ARRAY(src_commits, alloc_src_commits);
for_each_string_list_item(item, &src_tag) {
struct ref *ref = item->util;
struct commit *commit;
if (is_null_oid(&ref->new_oid))
continue;
commit = lookup_commit_reference_gently(the_repository,
&ref->new_oid,
1);
if (!commit)
/* not pushing a commit, which is not an error */
continue;
ALLOC_GROW(src_commits, nr_src_commits + 1, alloc_src_commits);
src_commits[nr_src_commits++] = commit;
}
found_commits = get_reachable_subset(sent_tips.tip, sent_tips.nr,
src_commits, nr_src_commits,
reachable_flag);
for_each_string_list_item(item, &src_tag) {
struct ref *dst_ref;
struct ref *ref = item->util;
struct commit *commit;
if (is_null_oid(&ref->new_oid))
@ -1223,7 +1250,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
* Is this tag, which they do not have, reachable from
* any of the commits we are sending?
*/
if (!in_merge_bases_many(commit, sent_tips.nr, sent_tips.tip))
if (!(commit->object.flags & reachable_flag))
continue;
/* Add it in */
@ -1231,7 +1258,12 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
oidcpy(&dst_ref->new_oid, &ref->new_oid);
dst_ref->peer_ref = copy_ref(ref);
}
clear_commit_marks_many(nr_src_commits, src_commits, reachable_flag);
free(src_commits);
free_commit_list(found_commits);
}
string_list_clear(&src_tag, 0);
free(sent_tips.tip);
}