From a52397cce6c1049c3c9507d734243ee125d11f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 26 Jun 2016 07:58:06 +0200 Subject: git-fetch.txt: document fetch output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This documents the ref update status of fetch. The structure of this output is defined in [1]. The ouput content is refined a bit in [2] [3] [4]. This patch is a copy from git-push.txt, modified a bit because the flag '-' means different things in push (delete) and fetch (tag update). PS. For code archaeologists, the discussion mentioned in [1] is probably [5]. [1] 165f390 (git-fetch: more terse fetch output - 2007-11-03) [2] 6315472 (fetch: report local storage errors ... - 2008-06-26) [3] f360d84 (builtin-fetch: add --prune option - 2009-11-10) [4] 0997ada (fetch: describe new refs based on where... - 2012-04-16) [5] http://thread.gmane.org/gmane.comp.version-control.git/61657 Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index efe56e0..cbf441f 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -99,6 +99,52 @@ The latter use of the `remote..fetch` values can be overridden by giving the `--refmap=` parameter(s) on the command line. +OUTPUT +------ + +The output of "git fetch" depends on the transport method used; this +section describes the output when fetching over the Git protocol +(either locally or via ssh) and Smart HTTP protocol. + +The status of the fetch is output in tabular form, with each line +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. + +flag:: + A single character indicating the status of the ref: +(space);; for a successfully fetched fast-forward; +`+`;; for a successful forced update; +`x`;; for a successfully pruned ref; +`-`;; for a successful tag update; +`*`;; for a successfully fetched new ref; +`!`;; for a ref that was rejected or failed to update; and +`=`;; for a ref that was up to date and did not need fetching. + +summary:: + For a successfully fetched ref, the summary shows the old and new + values of the ref in a form suitable for using as an argument to + `git log` (this is `..` in most cases, and + `...` for forced non-fast-forward updates). + +from:: + The name of the remote ref being fetched from, minus its + `refs//` prefix. In the case of deletion, the name of + the remote ref is "(none)". + +to:: + The name of the local ref being updated, minus its + `refs//` prefix. + +reason:: + A human-readable explanation. In the case of successfully fetched + refs, no explanation is needed. For a failed ref, the reason for + failure is described. EXAMPLES -------- -- cgit v0.10.2-6-g49f6 From d0b39a03cd5d6d5f9e2eac4907d9f47ec3d821da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 26 Jun 2016 07:58:07 +0200 Subject: fetch: refactor ref update status formatting code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it easier to change the formatting later. And it makes sure translators cannot mess up format specifiers and break Git. There are a couple call sites where the length of the second column is TRANSPORT_SUMMARY_WIDTH instead of calculated by TRANSPORT_SUMMARY(), which is enforced now. The result should be the same because these call sites do not contain characters outside ASCII range. The two strbuf_addf() calls instead of one is mostly to reduce diff-noise in a future patch where "ref -> ref" is reformatted differently. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/fetch.c b/builtin/fetch.c index 1582ca7..a7f152a 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -451,6 +451,16 @@ fail: #define REFCOL_WIDTH 10 +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 (error) + strbuf_addf(display, " (%s)", error); +} + static int update_local_ref(struct ref *ref, const char *remote, const struct ref *remote_ref, @@ -467,9 +477,8 @@ static int update_local_ref(struct ref *ref, if (!oidcmp(&ref->old_oid, &ref->new_oid)) { if (verbosity > 0) - strbuf_addf(display, "= %-*s %-*s -> %s", - TRANSPORT_SUMMARY(_("[up to date]")), - REFCOL_WIDTH, remote, pretty_ref); + format_display(display, '=', _("[up to date]"), NULL, + remote, pretty_ref); return 0; } @@ -481,10 +490,9 @@ static int update_local_ref(struct ref *ref, * If this is the head, and it's not okay to update * the head, and the old value of the head isn't empty... */ - strbuf_addf(display, - _("! %-*s %-*s -> %s (can't fetch in current branch)"), - TRANSPORT_SUMMARY(_("[rejected]")), - REFCOL_WIDTH, remote, pretty_ref); + format_display(display, '!', _("[rejected]"), + _("can't fetch in current branch"), + remote, pretty_ref); return 1; } @@ -492,11 +500,9 @@ static int update_local_ref(struct ref *ref, starts_with(ref->name, "refs/tags/")) { int r; r = s_update_ref("updating tag", ref, 0); - strbuf_addf(display, "%c %-*s %-*s -> %s%s", - r ? '!' : '-', - TRANSPORT_SUMMARY(_("[tag update]")), - REFCOL_WIDTH, remote, pretty_ref, - r ? _(" (unable to update local ref)") : ""); + format_display(display, r ? '!' : '-', _("[tag update]"), + r ? _("unable to update local ref") : NULL, + remote, pretty_ref); return r; } @@ -527,11 +533,9 @@ static int update_local_ref(struct ref *ref, (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(ref->new_oid.hash); r = s_update_ref(msg, ref, 0); - strbuf_addf(display, "%c %-*s %-*s -> %s%s", - r ? '!' : '*', - TRANSPORT_SUMMARY(what), - REFCOL_WIDTH, remote, pretty_ref, - r ? _(" (unable to update local ref)") : ""); + format_display(display, r ? '!' : '*', what, + r ? _("unable to update local ref") : NULL, + remote, pretty_ref); return r; } @@ -545,11 +549,9 @@ static int update_local_ref(struct ref *ref, (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(ref->new_oid.hash); r = s_update_ref("fast-forward", ref, 1); - strbuf_addf(display, "%c %-*s %-*s -> %s%s", - r ? '!' : ' ', - TRANSPORT_SUMMARY_WIDTH, quickref.buf, - REFCOL_WIDTH, remote, pretty_ref, - r ? _(" (unable to update local ref)") : ""); + format_display(display, r ? '!' : ' ', quickref.buf, + r ? _("unable to update local ref") : NULL, + remote, pretty_ref); strbuf_release(&quickref); return r; } else if (force || ref->force) { @@ -562,18 +564,14 @@ static int update_local_ref(struct ref *ref, (recurse_submodules != RECURSE_SUBMODULES_ON)) check_for_new_submodule_commits(ref->new_oid.hash); r = s_update_ref("forced-update", ref, 1); - strbuf_addf(display, "%c %-*s %-*s -> %s (%s)", - r ? '!' : '+', - TRANSPORT_SUMMARY_WIDTH, quickref.buf, - REFCOL_WIDTH, remote, pretty_ref, - r ? _("unable to update local ref") : _("forced update")); + format_display(display, r ? '!' : '+', quickref.buf, + r ? _("unable to update local ref") : _("forced update"), + remote, pretty_ref); strbuf_release(&quickref); return r; } else { - strbuf_addf(display, "! %-*s %-*s -> %s %s", - TRANSPORT_SUMMARY(_("[rejected]")), - REFCOL_WIDTH, remote, pretty_ref, - _("(non-fast-forward)")); + format_display(display, '!', _("[rejected]"), _("non-fast-forward"), + remote, pretty_ref); return 1; } } @@ -714,11 +712,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, rc |= update_local_ref(ref, what, rm, ¬e); free(ref); } else - strbuf_addf(¬e, "* %-*s %-*s -> FETCH_HEAD", - TRANSPORT_SUMMARY_WIDTH, - *kind ? kind : "branch", - REFCOL_WIDTH, - *what ? what : "HEAD"); + format_display(¬e, '*', + *kind ? kind : "branch", NULL, + *what ? what : "HEAD", + "FETCH_HEAD"); if (note.len) { if (verbosity >= 0 && !shown_url) { fprintf(stderr, _("From %.*s\n"), @@ -812,13 +809,15 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, if (verbosity >= 0) { for (ref = stale_refs; ref; ref = ref->next) { + struct strbuf sb = STRBUF_INIT; if (!shown_url) { fprintf(stderr, _("From %.*s\n"), url_len, url); shown_url = 1; } - fprintf(stderr, " x %-*s %-*s -> %s\n", - TRANSPORT_SUMMARY(_("[deleted]")), - REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name)); + format_display(&sb, 'x', _("[deleted]"), NULL, + _("(none)"), prettify_refname(ref->name)); + fprintf(stderr, " %s\n",sb.buf); + strbuf_release(&sb); warn_dangling_symref(stderr, dangling_msg, ref->name); } } -- cgit v0.10.2-6-g49f6 From 2cb040baa6fcb8a6314a54933cbcb4d3fcb60401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sun, 26 Jun 2016 07:58:08 +0200 Subject: fetch: change flag code for displaying tag update and deleted ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes the fetch flag code consistent with push, where '-' means deleted ref. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index cbf441f..771dde5 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -120,8 +120,8 @@ flag:: A single character indicating the status of the ref: (space);; for a successfully fetched fast-forward; `+`;; for a successful forced update; -`x`;; for a successfully pruned ref; -`-`;; for a successful tag update; +`-`;; for a successfully pruned ref; +`t`;; for a successful tag update; `*`;; for a successfully fetched new ref; `!`;; for a ref that was rejected or failed to update; and `=`;; for a ref that was up to date and did not need fetching. diff --git a/builtin/fetch.c b/builtin/fetch.c index a7f152a..8177f90 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -500,7 +500,7 @@ static int update_local_ref(struct ref *ref, starts_with(ref->name, "refs/tags/")) { int r; r = s_update_ref("updating tag", ref, 0); - format_display(display, r ? '!' : '-', _("[tag update]"), + format_display(display, r ? '!' : 't', _("[tag update]"), r ? _("unable to update local ref") : NULL, remote, pretty_ref); return r; @@ -814,7 +814,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map, fprintf(stderr, _("From %.*s\n"), url_len, url); shown_url = 1; } - format_display(&sb, 'x', _("[deleted]"), NULL, + format_display(&sb, '-', _("[deleted]"), NULL, _("(none)"), prettify_refname(ref->name)); fprintf(stderr, " %s\n",sb.buf); strbuf_release(&sb); -- cgit v0.10.2-6-g49f6 From 6bc91f23a6e14d540ab6950b438d40cf678143f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Fri, 1 Jul 2016 18:03:30 +0200 Subject: fetch: align all "remote -> local" output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do align "remote -> local" output by allocating 10 columns to "remote". That produces aligned output only for short refs. An extra pass is performed to find the longest remote ref name (that does not produce a line longer than terminal width) to produce better aligned output. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano diff --git a/builtin/fetch.c b/builtin/fetch.c index 8177f90..2bc609b 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -15,6 +15,7 @@ #include "submodule.h" #include "connected.h" #include "argv-array.h" +#include "utf8.h" static const char * const builtin_fetch_usage[] = { N_("git fetch [] [ [...]]"), @@ -449,14 +450,54 @@ fail: : STORE_REF_ERROR_OTHER; } -#define REFCOL_WIDTH 10 +static int refcol_width = 10; + +static void adjust_refcol_width(const struct ref *ref) +{ + int max, rlen, llen, len; + + /* uptodate lines are only shown on high verbosity level */ + if (!verbosity && !oidcmp(&ref->peer_ref->old_oid, &ref->old_oid)) + return; + + max = term_columns(); + rlen = utf8_strwidth(prettify_refname(ref->name)); + llen = utf8_strwidth(prettify_refname(ref->peer_ref->name)); + + /* + * rough estimation to see if the output line is too long and + * should not be counted (we can't do precise calculation + * anyway because we don't know if the error explanation part + * will be printed in update_local_ref) + */ + len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen; + if (len >= max) + return; + + if (refcol_width < rlen) + refcol_width = rlen; +} + +static void prepare_format_display(struct ref *ref_map) +{ + struct ref *rm; + + for (rm = ref_map; rm; rm = rm->next) { + if (rm->status == REF_STATUS_REJECT_SHALLOW || + !rm->peer_ref || + !strcmp(rm->name, "HEAD")) + continue; + + adjust_refcol_width(rm); + } +} 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); + strbuf_addf(display, "%-*s -> %s", refcol_width, remote, local); if (error) strbuf_addf(display, " (%s)", error); } @@ -618,6 +659,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, goto abort; } + prepare_format_display(ref_map); + /* * We do a pass for each fetch_head_status type in their enum order, so * merged entries are written before not-for-merge. That lets readers diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 454d896..f50497e 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -688,4 +688,19 @@ test_expect_success 'fetching with auto-gc does not lock up' ' ) ' +test_expect_success 'fetch aligned output' ' + git clone . full-output && + test_commit looooooooooooong-tag && + ( + cd full-output && + git fetch origin 2>&1 | \ + grep -e "->" | cut -c 22- >../actual + ) && + cat >expect <<-\EOF && + master -> origin/master + looooooooooooong-tag -> looooooooooooong-tag + EOF + test_cmp expect actual +' + test_done -- cgit v0.10.2-6-g49f6 From bc437d10202c015a5733f706dc44fa6bbf4d85b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Fri, 1 Jul 2016 18:03:31 +0200 Subject: fetch: reduce duplicate in ref update status lines with placeholder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index 2e1b2e4..7f6e58d 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -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..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 diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index 771dde5..9e42169 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -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 `` or `` 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; diff --git a/builtin/fetch.c b/builtin/fetch.c index 2bc609b..0a2eed1 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -451,6 +451,7 @@ fail: } 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); } diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index f50497e..6032776 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -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 -- cgit v0.10.2-6-g49f6