]> git.ipfire.org Git - thirdparty/git.git/commitdiff
fetch-prune: optimize dangling-ref reporting
authorPhil Hord <phil.hord@gmail.com>
Mon, 23 Jun 2025 23:43:26 +0000 (16:43 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 Jun 2025 23:54:14 +0000 (16:54 -0700)
When pruning during `git fetch` we check each pruned ref against the
ref_store one at a time to decide whether to report it as dangling.
This causes every local ref to be scanned for each ref being pruned.

If there are N refs in the repo and M refs being pruned, this code is
O(M*N). However, `git remote prune` uses a very similar function that
is only O(N*log(M)).

Remove the wasteful ref scanning for each pruned ref and use the faster
version already available in refs_warn_dangling_symrefs.

In a repo with 126,000 refs, where I was pruning 28,000 refs, this
code made about 3.6 billion calls to strcmp and consumed 410 seconds
of CPU. (Invariably in that time, my remote would timeout and the
fetch would fail anyway.)

After this change, the same operation completes in under a second.

Signed-off-by: Phil Hord <phil.hord@gmail.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/fetch.c
builtin/remote.c
refs.c

index fe2b26c74aecab386b3fb9be5b27761f09be800c..f05530b62ee415b18607ce0f0483e8acf081bdf8 100644 (file)
@@ -1384,9 +1384,13 @@ static int prune_refs(struct display_state *display_state,
        int result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
        struct strbuf err = STRBUF_INIT;
+       struct string_list refnames = STRING_LIST_INIT_NODUP;
        const char *dangling_msg = dry_run
-               ? _("   (%s will become dangling)")
-               : _("   (%s has become dangling)");
+               ? _("   %s will become dangling after %s is deleted")
+               : _("   %s has become dangling after %s was deleted");
+
+       for (ref = stale_refs; ref; ref = ref->next)
+               string_list_append(&refnames, ref->name);
 
        if (!dry_run) {
                if (transaction) {
@@ -1397,15 +1401,9 @@ static int prune_refs(struct display_state *display_state,
                                        goto cleanup;
                        }
                } else {
-                       struct string_list refnames = STRING_LIST_INIT_NODUP;
-
-                       for (ref = stale_refs; ref; ref = ref->next)
-                               string_list_append(&refnames, ref->name);
-
                        result = refs_delete_refs(get_main_ref_store(the_repository),
                                                  "fetch: prune", &refnames,
                                                  0);
-                       string_list_clear(&refnames, 0);
                }
        }
 
@@ -1417,12 +1415,14 @@ static int prune_refs(struct display_state *display_state,
                                           _("(none)"), ref->name,
                                           &ref->new_oid, &ref->old_oid,
                                           summary_width);
-                       refs_warn_dangling_symref(get_main_ref_store(the_repository),
-                                                 stderr, dangling_msg, ref->name);
                }
+               string_list_sort(&refnames);
+               refs_warn_dangling_symrefs(get_main_ref_store(the_repository),
+                                          stderr, dangling_msg, &refnames);
        }
 
 cleanup:
+       string_list_clear(&refnames, 0);
        strbuf_release(&err);
        free_refs(stale_refs);
        return result;
index 043596328638afc555cb00eaa2dcae2c69e2d713..30cf4100d4495194761f19153c3ebe0d75cf2e38 100644 (file)
@@ -1516,8 +1516,8 @@ static int prune_remote(const char *remote, int dry_run)
        struct string_list refs_to_prune = STRING_LIST_INIT_NODUP;
        struct string_list_item *item;
        const char *dangling_msg = dry_run
-               ? _(" %s will become dangling!")
-               : _(" %s has become dangling!");
+               ? _(" %s will become dangling after %s is deleted!")
+               : _(" %s has become dangling after %s was deleted!");
 
        get_remote_ref_states(remote, &states, GET_REF_STATES);
 
diff --git a/refs.c b/refs.c
index 0f41b2fd4a6b679a1cfcaa9a584c382068146212..18ee7b01584644488ae80283f79234c17aafdddb 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -461,7 +461,9 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU
                return 0;
        }
 
-       fprintf(d->fp, d->msg_fmt, refname);
+       skip_prefix(refname, "refs/remotes/", &refname);
+       skip_prefix(resolves_to, "refs/remotes/", &resolves_to);
+       fprintf(d->fp, d->msg_fmt, refname, resolves_to);
        fputc('\n', d->fp);
        return 0;
 }