remote prune: optimize "dangling symref" check/warning

When 'git remote prune' was used to delete many refs in a repository with many refs, a lot of time was spent checking for (now) dangling symbolic refs pointing to the deleted ref, since warn_dangling_symref() was once per deleted ref to check all other refs in the repository. Avoid this using the new warn_dangling_symrefs() function which makes one pass over all refs and checks for all the deleted refs in one go, after they have all been deleted. Signed-off-by: Jens Lindström <jl@opera.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jens Lindström committed May 23, 2014 at 12:30 UTC e6bea66db6229a0975ebba510a1d2827bf09cb7f
3 files changed +25 -2
builtin/remote.c
+6 -1
@@ -1313,6 +1313,7 @@ static int prune_remote(const char *remote, int dry_run)
1313 {
1314 int result = 0, i;
1315 struct ref_states states;
1316 + struct string_list delete_refs_list = STRING_LIST_INIT_NODUP;
1317 const char **delete_refs;
1318 const char *dangling_msg = dry_run
1319 ? _(" %s will become dangling!")
@@ -1339,6 +1340,8 @@ static int prune_remote(const char *remote, int dry_run)
1340 for (i = 0; i < states.stale.nr; i++) {
1341 const char *refname = states.stale.items[i].util;
1342
1343 + string_list_insert(&delete_refs_list, refname);
1344 +
1345 if (!dry_run)
1346 result |= delete_ref(refname, NULL, 0);
1347
@@ -1348,9 +1351,11 @@ static int prune_remote(const char *remote, int dry_run)
1351 else
1352 printf_ln(_(" * [pruned] %s"),
1353 abbrev_ref(refname, "refs/remotes/"));
1351 - warn_dangling_symref(stdout, dangling_msg, refname);
1354 }
1355
1356 + warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list);
1357 + string_list_clear(&delete_refs_list, 0);
1358 +
1359 free_remote_ref_states(&states);
1360 return result;
1361 }
refs.c
+18 -1
@@ -1611,6 +1611,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
1611 struct warn_if_dangling_data {
1612 FILE *fp;
1613 const char *refname;
1614 + const struct string_list *refnames;
1615 const char *msg_fmt;
1616 };
1617
@@ -1625,8 +1626,12 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
1626 return 0;
1627
1628 resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
1628 - if (!resolves_to || strcmp(resolves_to, d->refname))
1629 + if (!resolves_to
1630 + || (d->refname
1631 + ? strcmp(resolves_to, d->refname)
1632 + : !string_list_has_string(d->refnames, resolves_to))) {
1633 return 0;
1634 + }
1635
1636 fprintf(d->fp, d->msg_fmt, refname);
1637 fputc('\n', d->fp);
@@ -1639,6 +1644,18 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
1644
1645 data.fp = fp;
1646 data.refname = refname;
1647 + data.refnames = NULL;
1648 + data.msg_fmt = msg_fmt;
1649 + for_each_rawref(warn_if_dangling_symref, &data);
1650 +}
1651 +
1652 +void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
1653 +{
1654 + struct warn_if_dangling_data data;
1655 +
1656 + data.fp = fp;
1657 + data.refname = NULL;
1658 + data.refnames = refnames;
1659 data.msg_fmt = msg_fmt;
1660 for_each_rawref(warn_if_dangling_symref, &data);
1661 }
refs.h
+1
@@ -89,6 +89,7 @@ static inline const char *has_glob_specials(const char *pattern)
89 extern int for_each_rawref(each_ref_fn, void *);
90
91 extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
92 +extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list* refnames);
93
94 /*
95 * Lock the packed-refs file for writing. Flags is passed to