summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Junio C Hamano <gitster@pobox.com>2016-07-19 13:22:21 -0700
committerLibravatar Junio C Hamano <gitster@pobox.com>2016-07-19 13:22:21 -0700
commit566fdaf611f44724120412a43132c07b020fc4f1 (patch)
tree4b8d056b29834761c79fd250a5c38ab3bfd1ab0d
parentMerge branch 'jk/test-match-signal' (diff)
parentfetch: reduce duplicate in ref update status lines with placeholder (diff)
downloadtgif-566fdaf611f44724120412a43132c07b020fc4f1.tar.xz
Merge branch 'nd/fetch-ref-summary'
Improve the look of the way "git fetch" reports what happened to each ref that was fetched. * nd/fetch-ref-summary: fetch: reduce duplicate in ref update status lines with placeholder fetch: align all "remote -> local" output fetch: change flag code for displaying tag update and deleted ref fetch: refactor ref update status formatting code git-fetch.txt: document fetch output
-rw-r--r--Documentation/config.txt5
-rw-r--r--Documentation/git-fetch.txt51
-rw-r--r--builtin/fetch.c197
-rwxr-xr-xt/t5510-fetch.sh30
4 files changed, 243 insertions, 40 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 226c12a154..e455faeb23 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1243,6 +1243,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
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index efe56e0808..9e4216999d 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -99,6 +99,57 @@ The latter use of the `remote.<repository>.fetch` values can be
overridden by giving the `--refmap=<refspec>` 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:
+
+-------------------------------
+ <flag> <summary> <from> -> <to> [<reason>]
+-------------------------------
+
+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;
+`+`;; for a successful forced 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.
+
+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 `<old>..<new>` in most cases, and
+ `<old>...<new>` for forced non-fast-forward updates).
+
+from::
+ The name of the remote ref being fetched from, minus its
+ `refs/<type>/` 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/<type>/` 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
--------
diff --git a/builtin/fetch.c b/builtin/fetch.c
index f896aa1f88..d9ea6f392a 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 [<options>] [<repository> [<refspec>...]]"),
@@ -449,7 +450,132 @@ fail:
: STORE_REF_ERROR_OTHER;
}
-#define REFCOL_WIDTH 10
+static int refcol_width = 10;
+static int compact_format;
+
+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)
+ */
+ 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;
+}
+
+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 ||
+ !rm->peer_ref ||
+ !strcmp(rm->name, "HEAD"))
+ continue;
+
+ adjust_refcol_width(rm);
+ }
+}
+
+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));
+ if (!compact_format)
+ print_remote_to_local(display, remote, local);
+ else
+ print_compact(display, remote, local);
+ if (error)
+ strbuf_addf(display, " (%s)", error);
+}
static int update_local_ref(struct ref *ref,
const char *remote,
@@ -467,9 +593,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 +606,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 +616,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 ? '!' : 't', _("[tag update]"),
+ r ? _("unable to update local ref") : NULL,
+ remote, pretty_ref);
return r;
}
@@ -527,11 +649,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 +665,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 +680,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;
}
}
@@ -620,6 +734,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
@@ -714,11 +830,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
rc |= update_local_ref(ref, what, rm, &note);
free(ref);
} else
- strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
- TRANSPORT_SUMMARY_WIDTH,
- *kind ? kind : "branch",
- REFCOL_WIDTH,
- *what ? what : "HEAD");
+ format_display(&note, '*',
+ *kind ? kind : "branch", NULL,
+ *what ? what : "HEAD",
+ "FETCH_HEAD");
if (note.len) {
if (verbosity >= 0 && !shown_url) {
fprintf(stderr, _("From %.*s\n"),
@@ -812,13 +927,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, '-', _("[deleted]"), NULL,
+ _("(none)"), prettify_refname(ref->name));
+ fprintf(stderr, " %s\n",sb.buf);
+ strbuf_release(&sb);
warn_dangling_symref(stderr, dangling_msg, ref->name);
}
}
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 88076da1e6..6bd4853079 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -688,4 +688,34 @@ 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 -c fetch.output=full 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_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