fetch: reduce duplicate in ref update status lines with placeholder
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Fri, 1 Jul 2016 16:03:31 +0000 (18:03 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Jul 2016 18:48:25 +0000 (11:48 -0700)
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>
Documentation/config.txt
Documentation/git-fetch.txt
builtin/fetch.c
t/t5510-fetch.sh
index 2e1b2e486e615981c14f7e591f3f32aa06cdee7a..7f6e58d549744495020aefec5cbdf05a7354a994 100644 (file)
@@ -1220,6 +1220,11 @@ fetch.prune::
        If true, fetch will automatically behave as if the `--prune`
        option was given on the command line.  See also `remote.<name>.prune`.
 
+fetch.output::
+       Control how ref update status is printed. Valid values are
+       `full` and `compact`. Default value is `full`. See section
+       OUTPUT in linkgit:git-fetch[1] for detail.
+
 format.attach::
        Enable multipart/mixed attachments as the default for
        'format-patch'.  The value can also be a double quoted string
index 771dde51cda36f756db471f10590831a20b89da1..9e4216999d69f448b50d238d22e09289c6780661 100644 (file)
@@ -116,6 +116,11 @@ representing the status of a single ref. Each line is of the form:
 The status of up-to-date refs is shown only if the --verbose option is
 used.
 
+In compact output mode, specified with configuration variable
+fetch.output, if either entire `<from>` or `<to>` is found in the
+other string, it will be substituted with `*` in the other string. For
+example, `master -> origin/master` becomes `master -> origin/*`.
+
 flag::
        A single character indicating the status of the ref:
 (space);; for a successfully fetched fast-forward;
index 2bc609b7f7152af97c5adea0172ba90f807ac7b0..0a2eed180760613a5064b63ee9efdb3d0d5b6d1d 100644 (file)
@@ -451,6 +451,7 @@ static int s_update_ref(const char *action,
 }
 
 static int refcol_width = 10;
+static int compact_format;
 
 static void adjust_refcol_width(const struct ref *ref)
 {
@@ -462,6 +463,7 @@ static void adjust_refcol_width(const struct ref *ref)
 
        max    = term_columns();
        rlen   = utf8_strwidth(prettify_refname(ref->name));
+
        llen   = utf8_strwidth(prettify_refname(ref->peer_ref->name));
 
        /*
@@ -470,10 +472,19 @@ static void adjust_refcol_width(const struct ref *ref)
         * anyway because we don't know if the error explanation part
         * will be printed in update_local_ref)
         */
+       if (compact_format) {
+               llen = 0;
+               max = max * 2 / 3;
+       }
        len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
        if (len >= max)
                return;
 
+       /*
+        * Not precise calculation for compact mode because '*' can
+        * appear on the left hand side of '->' and shrink the column
+        * back.
+        */
        if (refcol_width < rlen)
                refcol_width = rlen;
 }
@@ -481,6 +492,16 @@ static void adjust_refcol_width(const struct ref *ref)
 static void prepare_format_display(struct ref *ref_map)
 {
        struct ref *rm;
+       const char *format = "full";
+
+       git_config_get_string_const("fetch.output", &format);
+       if (!strcasecmp(format, "full"))
+               compact_format = 0;
+       else if (!strcasecmp(format, "compact"))
+               compact_format = 1;
+       else
+               die(_("configuration fetch.output contains invalid value %s"),
+                   format);
 
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->status == REF_STATUS_REJECT_SHALLOW ||
@@ -492,12 +513,66 @@ static void prepare_format_display(struct ref *ref_map)
        }
 }
 
+static void print_remote_to_local(struct strbuf *display,
+                                 const char *remote, const char *local)
+{
+       strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+}
+
+static int find_and_replace(struct strbuf *haystack,
+                           const char *needle,
+                           const char *placeholder)
+{
+       const char *p = strstr(haystack->buf, needle);
+       int plen, nlen;
+
+       if (!p)
+               return 0;
+
+       if (p > haystack->buf && p[-1] != '/')
+               return 0;
+
+       plen = strlen(p);
+       nlen = strlen(needle);
+       if (plen > nlen && p[nlen] != '/')
+               return 0;
+
+       strbuf_splice(haystack, p - haystack->buf, nlen,
+                     placeholder, strlen(placeholder));
+       return 1;
+}
+
+static void print_compact(struct strbuf *display,
+                         const char *remote, const char *local)
+{
+       struct strbuf r = STRBUF_INIT;
+       struct strbuf l = STRBUF_INIT;
+
+       if (!strcmp(remote, local)) {
+               strbuf_addf(display, "%-*s -> *", refcol_width, remote);
+               return;
+       }
+
+       strbuf_addstr(&r, remote);
+       strbuf_addstr(&l, local);
+
+       if (!find_and_replace(&r, local, "*"))
+               find_and_replace(&l, remote, "*");
+       print_remote_to_local(display, r.buf, l.buf);
+
+       strbuf_release(&r);
+       strbuf_release(&l);
+}
+
 static void format_display(struct strbuf *display, char code,
                           const char *summary, const char *error,
                           const char *remote, const char *local)
 {
        strbuf_addf(display, "%c %-*s ", code, TRANSPORT_SUMMARY(summary));
-       strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local);
+       if (!compact_format)
+               print_remote_to_local(display, remote, local);
+       else
+               print_compact(display, remote, local);
        if (error)
                strbuf_addf(display, "  (%s)", error);
 }
index f50497eaa14c799680821eff4de61f47a63ab48b..603277655b5b44681ceab60bf4da5e02c54ee52e 100755 (executable)
@@ -693,7 +693,7 @@ test_expect_success 'fetch aligned output' '
        test_commit looooooooooooong-tag &&
        (
                cd full-output &&
-               git fetch origin 2>&1 | \
+               git -c fetch.output=full fetch origin 2>&1 | \
                        grep -e "->" | cut -c 22- >../actual
        ) &&
        cat >expect <<-\EOF &&
@@ -703,4 +703,19 @@ test_expect_success 'fetch aligned output' '
        test_cmp expect actual
 '
 
+test_expect_success 'fetch compact output' '
+       git clone . compact &&
+       test_commit extraaa &&
+       (
+               cd compact &&
+               git -c fetch.output=compact fetch origin 2>&1 | \
+                       grep -e "->" | cut -c 22- >../actual
+       ) &&
+       cat >expect <<-\EOF &&
+       master     -> origin/*
+       extraaa    -> *
+       EOF
+       test_cmp expect actual
+'
+
 test_done