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
This commit is contained in:
commit
566fdaf611
@ -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
|
||||
|
@ -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
|
||||
--------
|
||||
|
197
builtin/fetch.c
197
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, ¬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 +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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user