fetch: centralize logic to print remote URL

When fetching from a remote, we not only print the actual references that have changed, but will also print the URL from which we have fetched them to standard output. The logic to handle this is duplicated across two different callsites with some non-trivial logic to compute the anonymized URL. Furthermore, we're using global state to track whether we have already shown the URL to the user or not. Refactor the code by moving it into `format_display()`. Like this, we can convert the global variable into a member of `display_state`. And second, we can deduplicate the logic to compute the anonymized URL. This also works as expected when fetching from multiple remotes, for example via a group of remotes, as we do this by forking a standalone git-fetch(1) process per remote that is to be fetched. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Mar 20, 2023 at 13:35 UTC c4ef5edbc952302097d6a55ac490bad7726bf840
1 file changed +44 -55
builtin/fetch.c
+44 -55
@@ -50,6 +50,9 @@ enum {
50 struct display_state {
51 int refcol_width;
52 int compact_format;
53 +
54 + char *url;
55 + int url_len, shown_url;
56 };
57
58 static int fetch_prune_config = -1; /* unspecified */
@@ -84,7 +87,6 @@ static const char *submodule_prefix = "";
87 static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
88 static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
89 static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
87 -static int shown_url = 0;
90 static struct refspec refmap = REFSPEC_INIT_FETCH;
91 static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
92 static struct string_list server_options = STRING_LIST_INIT_DUP;
@@ -776,13 +778,27 @@ static int refcol_width(const struct ref *ref, int compact_format)
778 return rlen;
779 }
780
779 -static void display_state_init(struct display_state *display_state, struct ref *ref_map)
781 +static void display_state_init(struct display_state *display_state, struct ref *ref_map,
782 + const char *raw_url)
783 {
784 struct ref *rm;
785 const char *format = "full";
786 + int i;
787
788 memset(display_state, 0, sizeof(*display_state));
789
790 + if (raw_url)
791 + display_state->url = transport_anonymize_url(raw_url);
792 + else
793 + display_state->url = xstrdup("foreign");
794 +
795 + display_state->url_len = strlen(display_state->url);
796 + for (i = display_state->url_len - 1; display_state->url[i] == '/' && 0 <= i; i--)
797 + ;
798 + display_state->url_len = i + 1;
799 + if (4 < i && !strncmp(".git", display_state->url + i - 3, 4))
800 + display_state->url_len = i - 3;
801 +
802 if (verbosity < 0)
803 return;
804
@@ -816,6 +832,11 @@ static void display_state_init(struct display_state *display_state, struct ref *
832 }
833 }
834
835 +static void display_state_release(struct display_state *display_state)
836 +{
837 + free(display_state->url);
838 +}
839 +
840 static void print_remote_to_local(struct display_state *display_state,
841 struct strbuf *display_buffer,
842 const char *remote, const char *local)
@@ -883,6 +904,12 @@ static void format_display(struct display_state *display_state,
904 if (verbosity < 0)
905 return;
906
907 + if (!display_state->shown_url) {
908 + strbuf_addf(display_buffer, _("From %.*s\n"),
909 + display_state->url_len, display_state->url);
910 + display_state->shown_url = 1;
911 + }
912 +
913 width = (summary_width + strlen(summary) - gettext_width(summary));
914
915 strbuf_addf(display_buffer, " %c %-*s ", code, width, summary);
@@ -1122,33 +1149,28 @@ N_("it took %.2f seconds to check forced updates; you can use\n"
1149 "to avoid this check\n");
1150
1151 static int store_updated_refs(struct display_state *display_state,
1125 - const char *raw_url, const char *remote_name,
1152 + const char *remote_name,
1153 int connectivity_checked,
1154 struct ref_transaction *transaction, struct ref *ref_map,
1155 struct fetch_head *fetch_head)
1156 {
1130 - int url_len, i, rc = 0;
1157 + int rc = 0;
1158 struct strbuf note = STRBUF_INIT;
1159 const char *what, *kind;
1160 struct ref *rm;
1134 - char *url;
1161 int want_status;
1162 int summary_width = 0;
1163
1164 if (verbosity >= 0)
1165 summary_width = transport_summary_width(ref_map);
1166
1141 - if (raw_url)
1142 - url = transport_anonymize_url(raw_url);
1143 - else
1144 - url = xstrdup("foreign");
1145 -
1167 if (!connectivity_checked) {
1168 struct check_connected_options opt = CHECK_CONNECTED_INIT;
1169
1170 rm = ref_map;
1171 if (check_connected(iterate_ref_map, &rm, &opt)) {
1151 - rc = error(_("%s did not send all necessary objects\n"), url);
1172 + rc = error(_("%s did not send all necessary objects\n"),
1173 + display_state->url);
1174 goto abort;
1175 }
1176 }
@@ -1232,13 +1254,6 @@ static int store_updated_refs(struct display_state *display_state,
1254 what = rm->name;
1255 }
1256
1235 - url_len = strlen(url);
1236 - for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
1237 - ;
1238 - url_len = i + 1;
1239 - if (4 < i && !strncmp(".git", url + i - 3, 4))
1240 - url_len = i - 3;
1241 -
1257 strbuf_reset(&note);
1258 if (*what) {
1259 if (*kind)
@@ -1248,7 +1263,8 @@ static int store_updated_refs(struct display_state *display_state,
1263
1264 append_fetch_head(fetch_head, &rm->old_oid,
1265 rm->fetch_head_status,
1251 - note.buf, url, url_len);
1266 + note.buf, display_state->url,
1267 + display_state->url_len);
1268
1269 strbuf_reset(&note);
1270 if (ref) {
@@ -1266,14 +1282,8 @@ static int store_updated_refs(struct display_state *display_state,
1282 *what ? what : "HEAD",
1283 "FETCH_HEAD", summary_width);
1284 }
1269 - if (note.len) {
1270 - if (!shown_url) {
1271 - fprintf(stderr, _("From %.*s\n"),
1272 - url_len, url);
1273 - shown_url = 1;
1274 - }
1285 + if (note.len)
1286 fputs(note.buf, stderr);
1276 - }
1287 }
1288 }
1289
@@ -1293,7 +1303,6 @@ static int store_updated_refs(struct display_state *display_state,
1303
1304 abort:
1305 strbuf_release(&note);
1296 - free(url);
1306 return rc;
1307 }
1308
@@ -1365,7 +1374,7 @@ static int fetch_and_consume_refs(struct display_state *display_state,
1374 }
1375
1376 trace2_region_enter("fetch", "consume_refs", the_repository);
1368 - ret = store_updated_refs(display_state, transport->url, transport->remote->name,
1377 + ret = store_updated_refs(display_state, transport->remote->name,
1378 connectivity_checked, transaction, ref_map,
1379 fetch_head);
1380 trace2_region_leave("fetch", "consume_refs", the_repository);
@@ -1378,30 +1387,15 @@ out:
1387 static int prune_refs(struct display_state *display_state,
1388 struct refspec *rs,
1389 struct ref_transaction *transaction,
1381 - struct ref *ref_map,
1382 - const char *raw_url)
1390 + struct ref *ref_map)
1391 {
1384 - int url_len, i, result = 0;
1392 + int result = 0;
1393 struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map);
1394 struct strbuf err = STRBUF_INIT;
1387 - char *url;
1395 const char *dangling_msg = dry_run
1396 ? _(" (%s will become dangling)")
1397 : _(" (%s has become dangling)");
1398
1392 - if (raw_url)
1393 - url = transport_anonymize_url(raw_url);
1394 - else
1395 - url = xstrdup("foreign");
1396 -
1397 - url_len = strlen(url);
1398 - for (i = url_len - 1; url[i] == '/' && 0 <= i; i--)
1399 - ;
1400 -
1401 - url_len = i + 1;
1402 - if (4 < i && !strncmp(".git", url + i - 3, 4))
1403 - url_len = i - 3;
1404 -
1399 if (!dry_run) {
1400 if (transaction) {
1401 for (ref = stale_refs; ref; ref = ref->next) {
@@ -1426,10 +1420,6 @@ static int prune_refs(struct display_state *display_state,
1420
1421 for (ref = stale_refs; ref; ref = ref->next) {
1422 struct strbuf sb = STRBUF_INIT;
1429 - if (!shown_url) {
1430 - fprintf(stderr, _("From %.*s\n"), url_len, url);
1431 - shown_url = 1;
1432 - }
1423 format_display(display_state, &sb, '-', _("[deleted]"), NULL,
1424 _("(none)"), ref->name,
1425 summary_width);
@@ -1441,7 +1431,6 @@ static int prune_refs(struct display_state *display_state,
1431
1432 cleanup:
1433 strbuf_release(&err);
1444 - free(url);
1434 free_refs(stale_refs);
1435 return result;
1436 }
@@ -1596,7 +1585,7 @@ static int do_fetch(struct transport *transport,
1585 {
1586 struct ref_transaction *transaction = NULL;
1587 struct ref *ref_map = NULL;
1599 - struct display_state display_state;
1588 + struct display_state display_state = { 0 };
1589 int autotags = (transport->remote->fetch_tags == 1);
1590 int retcode = 0;
1591 const struct ref *remote_refs;
@@ -1678,7 +1667,7 @@ static int do_fetch(struct transport *transport,
1667 if (retcode)
1668 goto cleanup;
1669
1681 - display_state_init(&display_state, ref_map);
1670 + display_state_init(&display_state, ref_map, transport->url);
1671
1672 if (atomic_fetch) {
1673 transaction = ref_transaction_begin(&err);
@@ -1697,11 +1686,10 @@ static int do_fetch(struct transport *transport,
1686 * don't care whether --tags was specified.
1687 */
1688 if (rs->nr) {
1700 - retcode = prune_refs(&display_state, rs, transaction, ref_map, transport->url);
1689 + retcode = prune_refs(&display_state, rs, transaction, ref_map);
1690 } else {
1691 retcode = prune_refs(&display_state, &transport->remote->fetch,
1703 - transaction, ref_map,
1704 - transport->url);
1692 + transaction, ref_map);
1693 }
1694 if (retcode != 0)
1695 retcode = 1;
@@ -1812,6 +1800,7 @@ cleanup:
1800 error("%s", err.buf);
1801 }
1802
1803 + display_state_release(&display_state);
1804 close_fetch_head(&fetch_head);
1805 strbuf_release(&err);
1806 free_refs(ref_map);