fetch: reduce duplicate in ref update status lines with placeholder

In the "remote -> local" line, if either ref is a substring of the other, the common part in the other string is replaced with "*". For example abc -> origin/abc refs/pull/123/head -> pull/123 become abc -> origin/* refs/*/head -> pull/123 Activated with fetch.output=compact. For the record, this output is not perfect. A single giant ref can push all refs very far to the right and likely be wrapped around. We may have a few options: - exclude these long lines smarter - break the line after "->", exclude it from column width calculation - implement a new format, { -> origin/}foo, which makes the problem go away at the cost of a bit harder to read - reverse all the arrows so we have "* <- looong-ref", again still hard to read. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Jul 1, 2016 at 18:03 UTC bc437d10202c015a5733f706dc44fa6bbf4d85b9
4 files changed +102 -2
Documentation/config.txt
+5
@@ -1220,6 +1220,11 @@ fetch.prune::
1220 If true, fetch will automatically behave as if the `--prune`
1221 option was given on the command line. See also `remote.<name>.prune`.
1222
1223 +fetch.output::
1224 + Control how ref update status is printed. Valid values are
1225 + `full` and `compact`. Default value is `full`. See section
1226 + OUTPUT in linkgit:git-fetch[1] for detail.
1227 +
1228 format.attach::
1229 Enable multipart/mixed attachments as the default for
1230 'format-patch'. The value can also be a double quoted string
Documentation/git-fetch.txt
+5
@@ -116,6 +116,11 @@ representing the status of a single ref. Each line is of the form:
116 The status of up-to-date refs is shown only if the --verbose option is
117 used.
118
119 +In compact output mode, specified with configuration variable
120 +fetch.output, if either entire `<from>` or `<to>` is found in the
121 +other string, it will be substituted with `*` in the other string. For
122 +example, `master -> origin/master` becomes `master -> origin/*`.
123 +
124 flag::
125 A single character indicating the status of the ref:
126 (space);; for a successfully fetched fast-forward;
builtin/fetch.c
+76 -1
@@ -451,6 +451,7 @@ fail:
451 }
452
453 static int refcol_width = 10;
454 +static int compact_format;
455
456 static void adjust_refcol_width(const struct ref *ref)
457 {
@@ -462,6 +463,7 @@ static void adjust_refcol_width(const struct ref *ref)
463
464 max = term_columns();
465 rlen = utf8_strwidth(prettify_refname(ref->name));
466 +
467 llen = utf8_strwidth(prettify_refname(ref->peer_ref->name));
468
469 /*
@@ -470,10 +472,19 @@ static void adjust_refcol_width(const struct ref *ref)
472 * anyway because we don't know if the error explanation part
473 * will be printed in update_local_ref)
474 */
475 + if (compact_format) {
476 + llen = 0;
477 + max = max * 2 / 3;
478 + }
479 len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
480 if (len >= max)
481 return;
482
483 + /*
484 + * Not precise calculation for compact mode because '*' can
485 + * appear on the left hand side of '->' and shrink the column
486 + * back.
487 + */
488 if (refcol_width < rlen)
489 refcol_width = rlen;
490 }
@@ -481,6 +492,16 @@ static void adjust_refcol_width(const struct ref *ref)
492 static void prepare_format_display(struct ref *ref_map)
493 {
494 struct ref *rm;
495 + const char *format = "full";
496 +
497 + git_config_get_string_const("fetch.output", &format);
498 + if (!strcasecmp(format, "full"))
499 + compact_format = 0;
500 + else if (!strcasecmp(format, "compact"))
501 + compact_format = 1;
502 + else
503 + die(_("configuration fetch.output contains invalid value %s"),
504 + format);
505
506 for (rm = ref_map; rm; rm = rm->next) {
507 if (rm->status == REF_STATUS_REJECT_SHALLOW ||
@@ -492,12 +513,66 @@ static void prepare_format_display(struct ref *ref_map)
513 }
514 }
515
516 +static void print_remote_to_local(struct strbuf *display,
517 + const char *remote, const char *local)
518 +{
519 + strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
520 +}
521 +
522 +static int find_and_replace(struct strbuf *haystack,
523 + const char *needle,
524 + const char *placeholder)
525 +{
526 + const char *p = strstr(haystack->buf, needle);
527 + int plen, nlen;
528 +
529 + if (!p)
530 + return 0;
531 +
532 + if (p > haystack->buf && p[-1] != '/')
533 + return 0;
534 +
535 + plen = strlen(p);
536 + nlen = strlen(needle);
537 + if (plen > nlen && p[nlen] != '/')
538 + return 0;
539 +
540 + strbuf_splice(haystack, p - haystack->buf, nlen,
541 + placeholder, strlen(placeholder));
542 + return 1;
543 +}
544 +
545 +static void print_compact(struct strbuf *display,
546 + const char *remote, const char *local)
547 +{
548 + struct strbuf r = STRBUF_INIT;
549 + struct strbuf l = STRBUF_INIT;
550 +
551 + if (!strcmp(remote, local)) {
552 + strbuf_addf(display, "%-*s -> *", refcol_width, remote);
553 + return;
554 + }
555 +
556 + strbuf_addstr(&r, remote);
557 + strbuf_addstr(&l, local);
558 +
559 + if (!find_and_replace(&r, local, "*"))
560 + find_and_replace(&l, remote, "*");
561 + print_remote_to_local(display, r.buf, l.buf);
562 +
563 + strbuf_release(&r);
564 + strbuf_release(&l);
565 +}
566 +
567 static void format_display(struct strbuf *display, char code,
568 const char *summary, const char *error,
569 const char *remote, const char *local)
570 {
571 strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(summary));
500 - strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
572 + if (!compact_format)
573 + print_remote_to_local(display, remote, local);
574 + else
575 + print_compact(display, remote, local);
576 if (error)
577 strbuf_addf(display, " (%s)", error);
578 }
t/t5510-fetch.sh
+16 -1
@@ -693,7 +693,7 @@ test_expect_success 'fetch aligned output' '
693 test_commit looooooooooooong-tag &&
694 (
695 cd full-output &&
696 - git fetch origin 2>&1 | \
696 + git -c fetch.output=full fetch origin 2>&1 | \
697 grep -e "->" | cut -c 22- >../actual
698 ) &&
699 cat >expect <<-\EOF &&
@@ -703,4 +703,19 @@ test_expect_success 'fetch aligned output' '
703 test_cmp expect actual
704 '
705
706 +test_expect_success 'fetch compact output' '
707 + git clone . compact &&
708 + test_commit extraaa &&
709 + (
710 + cd compact &&
711 + git -c fetch.output=compact fetch origin 2>&1 | \
712 + grep -e "->" | cut -c 22- >../actual
713 + ) &&
714 + cat >expect <<-\EOF &&
715 + master -> origin/*
716 + extraaa -> *
717 + EOF
718 + test_cmp expect actual
719 +'
720 +
721 test_done