prune_refs(): use delete_refs()

The old version just looped over the references to delete, calling delete_ref() on each one. But that has quadratic behavior, because each call to delete_ref() might have to rewrite the packed-refs file. This can be very expensive in a repository with a large number of references. In some (admittedly extreme) repositories, we've seen cases where the ref-pruning part of fetch takes multiple tens of minutes. Instead call delete_refs(), which (aside from being less code) has the optimization that it only rewrites the packed-refs file a single time. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Jun 22, 2015 at 16:02 UTC a087b432a79f85b34e8582219a0bdec73c5821f5
1 file changed +17 -8
builtin/fetch.c
+17 -8
@@ -790,20 +790,29 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
790 if (4 < i && !strncmp(".git", url + i - 3, 4))
791 url_len = i - 3;
792
793 - for (ref = stale_refs; ref; ref = ref->next) {
794 - if (!dry_run)
795 - result |= delete_ref(ref->name, NULL, 0);
796 - if (verbosity >= 0 && !shown_url) {
797 - fprintf(stderr, _("From %.*s\n"), url_len, url);
798 - shown_url = 1;
799 - }
800 - if (verbosity >= 0) {
793 + if (!dry_run) {
794 + struct string_list refnames = STRING_LIST_INIT_NODUP;
795 +
796 + for (ref = stale_refs; ref; ref = ref->next)
797 + string_list_append(&refnames, ref->name);
798 +
799 + result = delete_refs(&refnames);
800 + string_list_clear(&refnames, 0);
801 + }
802 +
803 + if (verbosity >= 0) {
804 + for (ref = stale_refs; ref; ref = ref->next) {
805 + if (!shown_url) {
806 + fprintf(stderr, _("From %.*s\n"), url_len, url);
807 + shown_url = 1;
808 + }
809 fprintf(stderr, " x %-*s %-*s -> %s\n",
810 TRANSPORT_SUMMARY(_("[deleted]")),
811 REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
812 warn_dangling_symref(stderr, dangling_msg, ref->name);
813 }
814 }
815 +
816 free(url);
817 free_refs(stale_refs);
818 return result;