Merge branch 'ps/fetch-output-format'
"git fetch" learned the "--porcelain" option that emits what it did in a machine-parseable format. * ps/fetch-output-format: fetch: introduce machine-parseable "porcelain" output format fetch: move option related variables into main function fetch: lift up parsing of "fetch.output" config variable fetch: introduce `display_format` enum fetch: refactor calculation of the display table width fetch: print left-hand side when fetching HEAD:foo fetch: add a test to exercise invalid output formats fetch: split out tests for output format fetch: fix `--no-recurse-submodules` with multi-remote fetchesmaint
commit
15ba44f1b4
|
@ -78,6 +78,13 @@ linkgit:git-config[1].
|
|||
--dry-run::
|
||||
Show what would be done, without making any changes.
|
||||
|
||||
--porcelain::
|
||||
Print the output to standard output in an easy-to-parse format for
|
||||
scripts. See section OUTPUT in linkgit:git-fetch[1] for details.
|
||||
+
|
||||
This is incompatible with `--recurse-submodules=[yes|on-demand]` and takes
|
||||
precedence over the `fetch.output` config option.
|
||||
|
||||
ifndef::git-pull[]
|
||||
--[no-]write-fetch-head::
|
||||
Write the list of remote refs fetched in the `FETCH_HEAD`
|
||||
|
|
|
@ -204,6 +204,15 @@ representing the status of a single ref. Each line is of the form:
|
|||
<flag> <summary> <from> -> <to> [<reason>]
|
||||
-------------------------------
|
||||
|
||||
When using `--porcelain`, the output format is intended to be
|
||||
machine-parseable. In contrast to the human-readable output formats it
|
||||
thus prints to standard output instead of standard error. Each line is
|
||||
of the form:
|
||||
|
||||
-------------------------------
|
||||
<flag> <old-object-id> <new-object-id> <local-reference>
|
||||
-------------------------------
|
||||
|
||||
The status of up-to-date refs is shown only if the --verbose option is
|
||||
used.
|
||||
|
||||
|
|
490
builtin/fetch.c
490
builtin/fetch.c
|
@ -57,11 +57,18 @@ enum {
|
|||
TAGS_SET = 2
|
||||
};
|
||||
|
||||
enum display_format {
|
||||
DISPLAY_FORMAT_UNKNOWN = 0,
|
||||
DISPLAY_FORMAT_FULL,
|
||||
DISPLAY_FORMAT_COMPACT,
|
||||
DISPLAY_FORMAT_PORCELAIN,
|
||||
};
|
||||
|
||||
struct display_state {
|
||||
struct strbuf buf;
|
||||
|
||||
int refcol_width;
|
||||
int compact_format;
|
||||
enum display_format format;
|
||||
|
||||
char *url;
|
||||
int url_len, shown_url;
|
||||
|
@ -78,13 +85,12 @@ static int fetch_prune_tags_config = -1; /* unspecified */
|
|||
static int prune_tags = -1; /* unspecified */
|
||||
#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
|
||||
|
||||
static int all, append, dry_run, force, keep, multiple, update_head_ok;
|
||||
static int append, dry_run, force, keep, update_head_ok;
|
||||
static int write_fetch_head = 1;
|
||||
static int verbosity, deepen_relative, set_upstream, refetch;
|
||||
static int progress = -1;
|
||||
static int enable_auto_gc = 1;
|
||||
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
|
||||
static int max_jobs = -1, submodule_fetch_jobs_config = -1;
|
||||
static int tags = TAGS_DEFAULT, update_shallow, deepen;
|
||||
static int submodule_fetch_jobs_config = -1;
|
||||
static int fetch_parallel_config = 1;
|
||||
static int atomic_fetch;
|
||||
static enum transport_family family;
|
||||
|
@ -95,20 +101,20 @@ static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
|
|||
static struct strbuf default_rla = STRBUF_INIT;
|
||||
static struct transport *gtransport;
|
||||
static struct transport *gsecondary;
|
||||
static const char *submodule_prefix = "";
|
||||
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
|
||||
static int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
|
||||
static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
|
||||
static struct refspec refmap = REFSPEC_INIT_FETCH;
|
||||
static struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT;
|
||||
static struct string_list server_options = STRING_LIST_INIT_DUP;
|
||||
static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
|
||||
static int fetch_write_commit_graph = -1;
|
||||
static int stdin_refspecs = 0;
|
||||
static int negotiate_only;
|
||||
|
||||
struct fetch_config {
|
||||
enum display_format display_format;
|
||||
};
|
||||
|
||||
static int git_fetch_config(const char *k, const char *v, void *cb)
|
||||
{
|
||||
struct fetch_config *fetch_config = cb;
|
||||
|
||||
if (!strcmp(k, "fetch.prune")) {
|
||||
fetch_prune_config = git_config_bool(k, v);
|
||||
return 0;
|
||||
|
@ -147,6 +153,18 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(k, "fetch.output")) {
|
||||
if (!v)
|
||||
return config_error_nonbool(k);
|
||||
else if (!strcasecmp(v, "full"))
|
||||
fetch_config->display_format = DISPLAY_FORMAT_FULL;
|
||||
else if (!strcasecmp(v, "compact"))
|
||||
fetch_config->display_format = DISPLAY_FORMAT_COMPACT;
|
||||
else
|
||||
die(_("invalid value for '%s': '%s'"),
|
||||
"fetch.output", v);
|
||||
}
|
||||
|
||||
return git_default_config(k, v, cb);
|
||||
}
|
||||
|
||||
|
@ -163,92 +181,6 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct option builtin_fetch_options[] = {
|
||||
OPT__VERBOSITY(&verbosity),
|
||||
OPT_BOOL(0, "all", &all,
|
||||
N_("fetch from all remotes")),
|
||||
OPT_BOOL(0, "set-upstream", &set_upstream,
|
||||
N_("set upstream for git pull/fetch")),
|
||||
OPT_BOOL('a', "append", &append,
|
||||
N_("append to .git/FETCH_HEAD instead of overwriting")),
|
||||
OPT_BOOL(0, "atomic", &atomic_fetch,
|
||||
N_("use atomic transaction to update references")),
|
||||
OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
|
||||
N_("path to upload pack on remote end")),
|
||||
OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
|
||||
OPT_BOOL('m', "multiple", &multiple,
|
||||
N_("fetch from multiple remotes")),
|
||||
OPT_SET_INT('t', "tags", &tags,
|
||||
N_("fetch all tags and associated objects"), TAGS_SET),
|
||||
OPT_SET_INT('n', NULL, &tags,
|
||||
N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
|
||||
OPT_INTEGER('j', "jobs", &max_jobs,
|
||||
N_("number of submodules fetched in parallel")),
|
||||
OPT_BOOL(0, "prefetch", &prefetch,
|
||||
N_("modify the refspec to place all refs within refs/prefetch/")),
|
||||
OPT_BOOL('p', "prune", &prune,
|
||||
N_("prune remote-tracking branches no longer on remote")),
|
||||
OPT_BOOL('P', "prune-tags", &prune_tags,
|
||||
N_("prune local tags no longer on remote and clobber changed tags")),
|
||||
OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
|
||||
N_("control recursive fetching of submodules"),
|
||||
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
|
||||
OPT_BOOL(0, "dry-run", &dry_run,
|
||||
N_("dry run")),
|
||||
OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
|
||||
N_("write fetched references to the FETCH_HEAD file")),
|
||||
OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
|
||||
OPT_BOOL('u', "update-head-ok", &update_head_ok,
|
||||
N_("allow updating of HEAD ref")),
|
||||
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
|
||||
OPT_STRING(0, "depth", &depth, N_("depth"),
|
||||
N_("deepen history of shallow clone")),
|
||||
OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
|
||||
N_("deepen history of shallow repository based on time")),
|
||||
OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
|
||||
N_("deepen history of shallow clone, excluding rev")),
|
||||
OPT_INTEGER(0, "deepen", &deepen_relative,
|
||||
N_("deepen history of shallow clone")),
|
||||
OPT_SET_INT_F(0, "unshallow", &unshallow,
|
||||
N_("convert to a complete repository"),
|
||||
1, PARSE_OPT_NONEG),
|
||||
OPT_SET_INT_F(0, "refetch", &refetch,
|
||||
N_("re-fetch without negotiating common commits"),
|
||||
1, PARSE_OPT_NONEG),
|
||||
{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
|
||||
N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
|
||||
OPT_CALLBACK_F(0, "recurse-submodules-default",
|
||||
&recurse_submodules_default, N_("on-demand"),
|
||||
N_("default for recursive fetching of submodules "
|
||||
"(lower priority than config files)"),
|
||||
PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
|
||||
OPT_BOOL(0, "update-shallow", &update_shallow,
|
||||
N_("accept refs that update .git/shallow")),
|
||||
OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
|
||||
N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
|
||||
OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
|
||||
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
|
||||
TRANSPORT_FAMILY_IPV4),
|
||||
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
|
||||
TRANSPORT_FAMILY_IPV6),
|
||||
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
|
||||
N_("report that we have only objects reachable from this object")),
|
||||
OPT_BOOL(0, "negotiate-only", &negotiate_only,
|
||||
N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
|
||||
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
|
||||
OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
|
||||
N_("run 'maintenance --auto' after fetching")),
|
||||
OPT_BOOL(0, "auto-gc", &enable_auto_gc,
|
||||
N_("run 'maintenance --auto' after fetching")),
|
||||
OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
|
||||
N_("check for forced-updates on all updated branches")),
|
||||
OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
|
||||
N_("write the commit-graph after fetching")),
|
||||
OPT_BOOL(0, "stdin", &stdin_refspecs,
|
||||
N_("accept refspecs from stdin")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static void unlock_pack(unsigned int flags)
|
||||
{
|
||||
if (gtransport)
|
||||
|
@ -760,46 +692,56 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int refcol_width(const struct ref *ref, int compact_format)
|
||||
static int refcol_width(const struct ref *ref_map, int compact_format)
|
||||
{
|
||||
int max, rlen, llen, len;
|
||||
const struct ref *ref;
|
||||
int max, width = 10;
|
||||
|
||||
/* uptodate lines are only shown on high verbosity level */
|
||||
if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
|
||||
return 0;
|
||||
|
||||
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 = term_columns();
|
||||
if (compact_format)
|
||||
max = max * 2 / 3;
|
||||
}
|
||||
len = 21 /* flag and summary */ + rlen + 4 /* -> */ + llen;
|
||||
if (len >= max)
|
||||
return 0;
|
||||
|
||||
return rlen;
|
||||
for (ref = ref_map; ref; ref = ref->next) {
|
||||
int rlen, llen = 0, len;
|
||||
|
||||
if (ref->status == REF_STATUS_REJECT_SHALLOW ||
|
||||
!ref->peer_ref ||
|
||||
!strcmp(ref->name, "HEAD"))
|
||||
continue;
|
||||
|
||||
/* uptodate lines are only shown on high verbosity level */
|
||||
if (verbosity <= 0 && oideq(&ref->peer_ref->old_oid, &ref->old_oid))
|
||||
continue;
|
||||
|
||||
rlen = utf8_strwidth(prettify_refname(ref->name));
|
||||
if (!compact_format)
|
||||
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)
|
||||
continue;
|
||||
|
||||
if (width < rlen)
|
||||
width = rlen;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
static void display_state_init(struct display_state *display_state, struct ref *ref_map,
|
||||
const char *raw_url)
|
||||
const char *raw_url, enum display_format format)
|
||||
{
|
||||
struct ref *rm;
|
||||
const char *format = "full";
|
||||
int i;
|
||||
|
||||
memset(display_state, 0, sizeof(*display_state));
|
||||
|
||||
strbuf_init(&display_state->buf, 0);
|
||||
display_state->format = format;
|
||||
|
||||
if (raw_url)
|
||||
display_state->url = transport_anonymize_url(raw_url);
|
||||
|
@ -816,33 +758,17 @@ static void display_state_init(struct display_state *display_state, struct ref *
|
|||
if (verbosity < 0)
|
||||
return;
|
||||
|
||||
git_config_get_string_tmp("fetch.output", &format);
|
||||
if (!strcasecmp(format, "full"))
|
||||
display_state->compact_format = 0;
|
||||
else if (!strcasecmp(format, "compact"))
|
||||
display_state->compact_format = 1;
|
||||
else
|
||||
die(_("invalid value for '%s': '%s'"),
|
||||
"fetch.output", format);
|
||||
|
||||
display_state->refcol_width = 10;
|
||||
for (rm = ref_map; rm; rm = rm->next) {
|
||||
int width;
|
||||
|
||||
if (rm->status == REF_STATUS_REJECT_SHALLOW ||
|
||||
!rm->peer_ref ||
|
||||
!strcmp(rm->name, "HEAD"))
|
||||
continue;
|
||||
|
||||
width = refcol_width(rm, display_state->compact_format);
|
||||
|
||||
/*
|
||||
* Not precise calculation for compact mode because '*' can
|
||||
* appear on the left hand side of '->' and shrink the column
|
||||
* back.
|
||||
*/
|
||||
if (display_state->refcol_width < width)
|
||||
display_state->refcol_width = width;
|
||||
switch (display_state->format) {
|
||||
case DISPLAY_FORMAT_FULL:
|
||||
case DISPLAY_FORMAT_COMPACT:
|
||||
display_state->refcol_width = refcol_width(ref_map,
|
||||
display_state->format == DISPLAY_FORMAT_COMPACT);
|
||||
break;
|
||||
case DISPLAY_FORMAT_PORCELAIN:
|
||||
/* We don't need to precompute anything here. */
|
||||
break;
|
||||
default:
|
||||
BUG("unexpected display format %d", display_state->format);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -911,39 +837,61 @@ static void print_compact(struct display_state *display_state,
|
|||
static void display_ref_update(struct display_state *display_state, char code,
|
||||
const char *summary, const char *error,
|
||||
const char *remote, const char *local,
|
||||
const struct object_id *old_oid,
|
||||
const struct object_id *new_oid,
|
||||
int summary_width)
|
||||
{
|
||||
int width;
|
||||
FILE *f = stderr;
|
||||
|
||||
if (verbosity < 0)
|
||||
return;
|
||||
|
||||
strbuf_reset(&display_state->buf);
|
||||
|
||||
if (!display_state->shown_url) {
|
||||
strbuf_addf(&display_state->buf, _("From %.*s\n"),
|
||||
display_state->url_len, display_state->url);
|
||||
display_state->shown_url = 1;
|
||||
switch (display_state->format) {
|
||||
case DISPLAY_FORMAT_FULL:
|
||||
case DISPLAY_FORMAT_COMPACT: {
|
||||
int width;
|
||||
|
||||
if (!display_state->shown_url) {
|
||||
strbuf_addf(&display_state->buf, _("From %.*s\n"),
|
||||
display_state->url_len, display_state->url);
|
||||
display_state->shown_url = 1;
|
||||
}
|
||||
|
||||
width = (summary_width + strlen(summary) - gettext_width(summary));
|
||||
remote = prettify_refname(remote);
|
||||
local = prettify_refname(local);
|
||||
|
||||
strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
|
||||
|
||||
if (display_state->format != DISPLAY_FORMAT_COMPACT)
|
||||
print_remote_to_local(display_state, remote, local);
|
||||
else
|
||||
print_compact(display_state, remote, local);
|
||||
|
||||
if (error)
|
||||
strbuf_addf(&display_state->buf, " (%s)", error);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
width = (summary_width + strlen(summary) - gettext_width(summary));
|
||||
|
||||
strbuf_addf(&display_state->buf, " %c %-*s ", code, width, summary);
|
||||
if (!display_state->compact_format)
|
||||
print_remote_to_local(display_state, remote, prettify_refname(local));
|
||||
else
|
||||
print_compact(display_state, remote, prettify_refname(local));
|
||||
if (error)
|
||||
strbuf_addf(&display_state->buf, " (%s)", error);
|
||||
case DISPLAY_FORMAT_PORCELAIN:
|
||||
strbuf_addf(&display_state->buf, "%c %s %s %s", code,
|
||||
oid_to_hex(old_oid), oid_to_hex(new_oid), local);
|
||||
f = stdout;
|
||||
break;
|
||||
default:
|
||||
BUG("unexpected display format %d", display_state->format);
|
||||
};
|
||||
strbuf_addch(&display_state->buf, '\n');
|
||||
|
||||
fputs(display_state->buf.buf, stderr);
|
||||
fputs(display_state->buf.buf, f);
|
||||
}
|
||||
|
||||
static int update_local_ref(struct ref *ref,
|
||||
struct ref_transaction *transaction,
|
||||
struct display_state *display_state,
|
||||
const char *remote, const struct ref *remote_ref,
|
||||
const struct ref *remote_ref,
|
||||
int summary_width)
|
||||
{
|
||||
struct commit *current = NULL, *updated;
|
||||
|
@ -955,7 +903,8 @@ static int update_local_ref(struct ref *ref,
|
|||
if (oideq(&ref->old_oid, &ref->new_oid)) {
|
||||
if (verbosity > 0)
|
||||
display_ref_update(display_state, '=', _("[up to date]"), NULL,
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -968,7 +917,8 @@ static int update_local_ref(struct ref *ref,
|
|||
*/
|
||||
display_ref_update(display_state, '!', _("[rejected]"),
|
||||
_("can't fetch into checked-out branch"),
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -979,12 +929,14 @@ static int update_local_ref(struct ref *ref,
|
|||
r = s_update_ref("updating tag", ref, transaction, 0);
|
||||
display_ref_update(display_state, r ? '!' : 't', _("[tag update]"),
|
||||
r ? _("unable to update local ref") : NULL,
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
return r;
|
||||
} else {
|
||||
display_ref_update(display_state, '!', _("[rejected]"),
|
||||
_("would clobber existing tag"),
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -1017,7 +969,8 @@ static int update_local_ref(struct ref *ref,
|
|||
r = s_update_ref(msg, ref, transaction, 0);
|
||||
display_ref_update(display_state, r ? '!' : '*', what,
|
||||
r ? _("unable to update local ref") : NULL,
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -1040,7 +993,8 @@ static int update_local_ref(struct ref *ref,
|
|||
r = s_update_ref("fast-forward", ref, transaction, 1);
|
||||
display_ref_update(display_state, r ? '!' : ' ', quickref.buf,
|
||||
r ? _("unable to update local ref") : NULL,
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
strbuf_release(&quickref);
|
||||
return r;
|
||||
} else if (force || ref->force) {
|
||||
|
@ -1052,12 +1006,14 @@ static int update_local_ref(struct ref *ref,
|
|||
r = s_update_ref("forced-update", ref, transaction, 1);
|
||||
display_ref_update(display_state, r ? '!' : '+', quickref.buf,
|
||||
r ? _("unable to update local ref") : _("forced update"),
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
strbuf_release(&quickref);
|
||||
return r;
|
||||
} else {
|
||||
display_ref_update(display_state, '!', _("[rejected]"), _("non-fast-forward"),
|
||||
remote, ref->name, summary_width);
|
||||
remote_ref->name, ref->name,
|
||||
&ref->old_oid, &ref->new_oid, summary_width);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -1262,14 +1218,13 @@ static int store_updated_refs(struct display_state *display_state,
|
|||
if (!strcmp(rm->name, "HEAD")) {
|
||||
kind = "";
|
||||
what = "";
|
||||
}
|
||||
else if (skip_prefix(rm->name, "refs/heads/", &what))
|
||||
} else if (skip_prefix(rm->name, "refs/heads/", &what)) {
|
||||
kind = "branch";
|
||||
else if (skip_prefix(rm->name, "refs/tags/", &what))
|
||||
} else if (skip_prefix(rm->name, "refs/tags/", &what)) {
|
||||
kind = "tag";
|
||||
else if (skip_prefix(rm->name, "refs/remotes/", &what))
|
||||
} else if (skip_prefix(rm->name, "refs/remotes/", &what)) {
|
||||
kind = "remote-tracking branch";
|
||||
else {
|
||||
} else {
|
||||
kind = "";
|
||||
what = rm->name;
|
||||
}
|
||||
|
@ -1287,7 +1242,7 @@ static int store_updated_refs(struct display_state *display_state,
|
|||
display_state->url_len);
|
||||
|
||||
if (ref) {
|
||||
rc |= update_local_ref(ref, transaction, display_state, what,
|
||||
rc |= update_local_ref(ref, transaction, display_state,
|
||||
rm, summary_width);
|
||||
free(ref);
|
||||
} else if (write_fetch_head || dry_run) {
|
||||
|
@ -1298,8 +1253,10 @@ static int store_updated_refs(struct display_state *display_state,
|
|||
*/
|
||||
display_ref_update(display_state, '*',
|
||||
*kind ? kind : "branch", NULL,
|
||||
*what ? what : "HEAD",
|
||||
"FETCH_HEAD", summary_width);
|
||||
rm->name,
|
||||
"FETCH_HEAD",
|
||||
&rm->new_oid, &rm->old_oid,
|
||||
summary_width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1439,6 +1396,7 @@ static int prune_refs(struct display_state *display_state,
|
|||
for (ref = stale_refs; ref; ref = ref->next) {
|
||||
display_ref_update(display_state, '-', _("[deleted]"), NULL,
|
||||
_("(none)"), ref->name,
|
||||
&ref->new_oid, &ref->old_oid,
|
||||
summary_width);
|
||||
warn_dangling_symref(stderr, dangling_msg, ref->name);
|
||||
}
|
||||
|
@ -1596,7 +1554,8 @@ static int backfill_tags(struct display_state *display_state,
|
|||
}
|
||||
|
||||
static int do_fetch(struct transport *transport,
|
||||
struct refspec *rs)
|
||||
struct refspec *rs,
|
||||
enum display_format display_format)
|
||||
{
|
||||
struct ref_transaction *transaction = NULL;
|
||||
struct ref *ref_map = NULL;
|
||||
|
@ -1682,7 +1641,7 @@ static int do_fetch(struct transport *transport,
|
|||
if (retcode)
|
||||
goto cleanup;
|
||||
|
||||
display_state_init(&display_state, ref_map, transport->url);
|
||||
display_state_init(&display_state, ref_map, transport->url, display_format);
|
||||
|
||||
if (atomic_fetch) {
|
||||
transaction = ref_transaction_begin(&err);
|
||||
|
@ -1870,7 +1829,8 @@ static int add_remote_or_group(const char *name, struct string_list *list)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void add_options_to_argv(struct strvec *argv)
|
||||
static void add_options_to_argv(struct strvec *argv,
|
||||
enum display_format format)
|
||||
{
|
||||
if (dry_run)
|
||||
strvec_push(argv, "--dry-run");
|
||||
|
@ -1886,6 +1846,8 @@ static void add_options_to_argv(struct strvec *argv)
|
|||
strvec_push(argv, "--keep");
|
||||
if (recurse_submodules == RECURSE_SUBMODULES_ON)
|
||||
strvec_push(argv, "--recurse-submodules");
|
||||
else if (recurse_submodules == RECURSE_SUBMODULES_OFF)
|
||||
strvec_push(argv, "--no-recurse-submodules");
|
||||
else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
|
||||
strvec_push(argv, "--recurse-submodules=on-demand");
|
||||
if (tags == TAGS_SET)
|
||||
|
@ -1904,6 +1866,8 @@ static void add_options_to_argv(struct strvec *argv)
|
|||
strvec_push(argv, "--ipv6");
|
||||
if (!write_fetch_head)
|
||||
strvec_push(argv, "--no-write-fetch-head");
|
||||
if (format == DISPLAY_FORMAT_PORCELAIN)
|
||||
strvec_pushf(argv, "--porcelain");
|
||||
}
|
||||
|
||||
/* Fetch multiple remotes in parallel */
|
||||
|
@ -1912,6 +1876,7 @@ struct parallel_fetch_state {
|
|||
const char **argv;
|
||||
struct string_list *remotes;
|
||||
int next, result;
|
||||
enum display_format format;
|
||||
};
|
||||
|
||||
static int fetch_next_remote(struct child_process *cp,
|
||||
|
@ -1931,7 +1896,7 @@ static int fetch_next_remote(struct child_process *cp,
|
|||
strvec_push(&cp->args, remote);
|
||||
cp->git_cmd = 1;
|
||||
|
||||
if (verbosity >= 0)
|
||||
if (verbosity >= 0 && state->format != DISPLAY_FORMAT_PORCELAIN)
|
||||
printf(_("Fetching %s\n"), remote);
|
||||
|
||||
return 1;
|
||||
|
@ -1963,7 +1928,8 @@ static int fetch_finished(int result, struct strbuf *out,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fetch_multiple(struct string_list *list, int max_children)
|
||||
static int fetch_multiple(struct string_list *list, int max_children,
|
||||
enum display_format format)
|
||||
{
|
||||
int i, result = 0;
|
||||
struct strvec argv = STRVEC_INIT;
|
||||
|
@ -1981,10 +1947,10 @@ static int fetch_multiple(struct string_list *list, int max_children)
|
|||
strvec_pushl(&argv, "-c", "fetch.bundleURI=",
|
||||
"fetch", "--append", "--no-auto-gc",
|
||||
"--no-write-commit-graph", NULL);
|
||||
add_options_to_argv(&argv);
|
||||
add_options_to_argv(&argv, format);
|
||||
|
||||
if (max_children != 1 && list->nr != 1) {
|
||||
struct parallel_fetch_state state = { argv.v, list, 0, 0 };
|
||||
struct parallel_fetch_state state = { argv.v, list, 0, 0, format };
|
||||
const struct run_process_parallel_opts opts = {
|
||||
.tr2_category = "fetch",
|
||||
.tr2_label = "parallel/fetch",
|
||||
|
@ -2008,7 +1974,7 @@ static int fetch_multiple(struct string_list *list, int max_children)
|
|||
|
||||
strvec_pushv(&cmd.args, argv.v);
|
||||
strvec_push(&cmd.args, name);
|
||||
if (verbosity >= 0)
|
||||
if (verbosity >= 0 && format != DISPLAY_FORMAT_PORCELAIN)
|
||||
printf(_("Fetching %s\n"), name);
|
||||
cmd.git_cmd = 1;
|
||||
if (run_command(&cmd)) {
|
||||
|
@ -2063,7 +2029,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
|
|||
}
|
||||
|
||||
static int fetch_one(struct remote *remote, int argc, const char **argv,
|
||||
int prune_tags_ok, int use_stdin_refspecs)
|
||||
int prune_tags_ok, int use_stdin_refspecs,
|
||||
enum display_format display_format)
|
||||
{
|
||||
struct refspec rs = REFSPEC_INIT_FETCH;
|
||||
int i;
|
||||
|
@ -2130,7 +2097,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
|
|||
sigchain_push_common(unlock_pack_on_signal);
|
||||
atexit(unlock_pack_atexit);
|
||||
sigchain_push(SIGPIPE, SIG_IGN);
|
||||
exit_code = do_fetch(gtransport, &rs);
|
||||
exit_code = do_fetch(gtransport, &rs, display_format);
|
||||
sigchain_pop(SIGPIPE);
|
||||
refspec_clear(&rs);
|
||||
transport_disconnect(gtransport);
|
||||
|
@ -2140,12 +2107,113 @@ static int fetch_one(struct remote *remote, int argc, const char **argv,
|
|||
|
||||
int cmd_fetch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
int i;
|
||||
struct fetch_config config = {
|
||||
.display_format = DISPLAY_FORMAT_FULL,
|
||||
};
|
||||
const char *submodule_prefix = "";
|
||||
const char *bundle_uri;
|
||||
struct string_list list = STRING_LIST_INIT_DUP;
|
||||
struct remote *remote = NULL;
|
||||
int all = 0, multiple = 0;
|
||||
int result = 0;
|
||||
int prune_tags_ok = 1;
|
||||
int enable_auto_gc = 1;
|
||||
int unshallow = 0;
|
||||
int max_jobs = -1;
|
||||
int recurse_submodules_cli = RECURSE_SUBMODULES_DEFAULT;
|
||||
int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
|
||||
int fetch_write_commit_graph = -1;
|
||||
int stdin_refspecs = 0;
|
||||
int negotiate_only = 0;
|
||||
int porcelain = 0;
|
||||
int i;
|
||||
|
||||
struct option builtin_fetch_options[] = {
|
||||
OPT__VERBOSITY(&verbosity),
|
||||
OPT_BOOL(0, "all", &all,
|
||||
N_("fetch from all remotes")),
|
||||
OPT_BOOL(0, "set-upstream", &set_upstream,
|
||||
N_("set upstream for git pull/fetch")),
|
||||
OPT_BOOL('a', "append", &append,
|
||||
N_("append to .git/FETCH_HEAD instead of overwriting")),
|
||||
OPT_BOOL(0, "atomic", &atomic_fetch,
|
||||
N_("use atomic transaction to update references")),
|
||||
OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
|
||||
N_("path to upload pack on remote end")),
|
||||
OPT__FORCE(&force, N_("force overwrite of local reference"), 0),
|
||||
OPT_BOOL('m', "multiple", &multiple,
|
||||
N_("fetch from multiple remotes")),
|
||||
OPT_SET_INT('t', "tags", &tags,
|
||||
N_("fetch all tags and associated objects"), TAGS_SET),
|
||||
OPT_SET_INT('n', NULL, &tags,
|
||||
N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
|
||||
OPT_INTEGER('j', "jobs", &max_jobs,
|
||||
N_("number of submodules fetched in parallel")),
|
||||
OPT_BOOL(0, "prefetch", &prefetch,
|
||||
N_("modify the refspec to place all refs within refs/prefetch/")),
|
||||
OPT_BOOL('p', "prune", &prune,
|
||||
N_("prune remote-tracking branches no longer on remote")),
|
||||
OPT_BOOL('P', "prune-tags", &prune_tags,
|
||||
N_("prune local tags no longer on remote and clobber changed tags")),
|
||||
OPT_CALLBACK_F(0, "recurse-submodules", &recurse_submodules_cli, N_("on-demand"),
|
||||
N_("control recursive fetching of submodules"),
|
||||
PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
|
||||
OPT_BOOL(0, "dry-run", &dry_run,
|
||||
N_("dry run")),
|
||||
OPT_BOOL(0, "porcelain", &porcelain, N_("machine-readable output")),
|
||||
OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
|
||||
N_("write fetched references to the FETCH_HEAD file")),
|
||||
OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
|
||||
OPT_BOOL('u', "update-head-ok", &update_head_ok,
|
||||
N_("allow updating of HEAD ref")),
|
||||
OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
|
||||
OPT_STRING(0, "depth", &depth, N_("depth"),
|
||||
N_("deepen history of shallow clone")),
|
||||
OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
|
||||
N_("deepen history of shallow repository based on time")),
|
||||
OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
|
||||
N_("deepen history of shallow clone, excluding rev")),
|
||||
OPT_INTEGER(0, "deepen", &deepen_relative,
|
||||
N_("deepen history of shallow clone")),
|
||||
OPT_SET_INT_F(0, "unshallow", &unshallow,
|
||||
N_("convert to a complete repository"),
|
||||
1, PARSE_OPT_NONEG),
|
||||
OPT_SET_INT_F(0, "refetch", &refetch,
|
||||
N_("re-fetch without negotiating common commits"),
|
||||
1, PARSE_OPT_NONEG),
|
||||
{ OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
|
||||
N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
|
||||
OPT_CALLBACK_F(0, "recurse-submodules-default",
|
||||
&recurse_submodules_default, N_("on-demand"),
|
||||
N_("default for recursive fetching of submodules "
|
||||
"(lower priority than config files)"),
|
||||
PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules),
|
||||
OPT_BOOL(0, "update-shallow", &update_shallow,
|
||||
N_("accept refs that update .git/shallow")),
|
||||
OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"),
|
||||
N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg),
|
||||
OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")),
|
||||
OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"),
|
||||
TRANSPORT_FAMILY_IPV4),
|
||||
OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
|
||||
TRANSPORT_FAMILY_IPV6),
|
||||
OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"),
|
||||
N_("report that we have only objects reachable from this object")),
|
||||
OPT_BOOL(0, "negotiate-only", &negotiate_only,
|
||||
N_("do not fetch a packfile; instead, print ancestors of negotiation tips")),
|
||||
OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
|
||||
OPT_BOOL(0, "auto-maintenance", &enable_auto_gc,
|
||||
N_("run 'maintenance --auto' after fetching")),
|
||||
OPT_BOOL(0, "auto-gc", &enable_auto_gc,
|
||||
N_("run 'maintenance --auto' after fetching")),
|
||||
OPT_BOOL(0, "show-forced-updates", &fetch_show_forced_updates,
|
||||
N_("check for forced-updates on all updated branches")),
|
||||
OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
|
||||
N_("write the commit-graph after fetching")),
|
||||
OPT_BOOL(0, "stdin", &stdin_refspecs,
|
||||
N_("accept refspecs from stdin")),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
packet_trace_identity("fetch");
|
||||
|
||||
|
@ -2159,7 +2227,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||
free(anon);
|
||||
}
|
||||
|
||||
git_config(git_fetch_config, NULL);
|
||||
git_config(git_fetch_config, &config);
|
||||
if (the_repository->gitdir) {
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
|
@ -2198,6 +2266,26 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||
fetch_config_from_gitmodules(sfjc, rs);
|
||||
}
|
||||
|
||||
|
||||
if (porcelain) {
|
||||
switch (recurse_submodules_cli) {
|
||||
case RECURSE_SUBMODULES_OFF:
|
||||
case RECURSE_SUBMODULES_DEFAULT:
|
||||
/*
|
||||
* Reference updates in submodules would be ambiguous
|
||||
* in porcelain mode, so we reject this combination.
|
||||
*/
|
||||
recurse_submodules = RECURSE_SUBMODULES_OFF;
|
||||
break;
|
||||
|
||||
default:
|
||||
die(_("options '%s' and '%s' cannot be used together"),
|
||||
"--porcelain", "--recurse-submodules");
|
||||
}
|
||||
|
||||
config.display_format = DISPLAY_FORMAT_PORCELAIN;
|
||||
}
|
||||
|
||||
if (negotiate_only && !negotiation_tip.nr)
|
||||
die(_("--negotiate-only needs one or more --negotiation-tip=*"));
|
||||
|
||||
|
@ -2296,7 +2384,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||
} else if (remote) {
|
||||
if (filter_options.choice || repo_has_promisor_remote(the_repository))
|
||||
fetch_one_setup_partial(remote);
|
||||
result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
|
||||
result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs,
|
||||
config.display_format);
|
||||
} else {
|
||||
int max_children = max_jobs;
|
||||
|
||||
|
@ -2316,10 +2405,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||
max_children = fetch_parallel_config;
|
||||
|
||||
/* TODO should this also die if we have a previous partial-clone? */
|
||||
result = fetch_multiple(&list, max_children);
|
||||
result = fetch_multiple(&list, max_children, config.display_format);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is only needed after fetch_one(), which does not fetch
|
||||
* submodules by itself.
|
||||
|
@ -2338,7 +2426,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
|
|||
if (max_children < 0)
|
||||
max_children = fetch_parallel_config;
|
||||
|
||||
add_options_to_argv(&options);
|
||||
add_options_to_argv(&options, config.display_format);
|
||||
result = fetch_submodules(the_repository,
|
||||
&options,
|
||||
submodule_prefix,
|
||||
|
|
|
@ -1118,59 +1118,6 @@ 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 >actual 2>&1 &&
|
||||
grep -e "->" actual | cut -c 22- >../actual
|
||||
) &&
|
||||
cat >expect <<-\EOF &&
|
||||
main -> origin/main
|
||||
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 >actual 2>&1 &&
|
||||
grep -e "->" actual | cut -c 22- >../actual
|
||||
) &&
|
||||
cat >expect <<-\EOF &&
|
||||
main -> origin/*
|
||||
extraaa -> *
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--no-show-forced-updates' '
|
||||
mkdir forced-updates &&
|
||||
(
|
||||
cd forced-updates &&
|
||||
git init &&
|
||||
test_commit 1 &&
|
||||
test_commit 2
|
||||
) &&
|
||||
git clone forced-updates forced-update-clone &&
|
||||
git clone forced-updates no-forced-update-clone &&
|
||||
git -C forced-updates reset --hard HEAD~1 &&
|
||||
(
|
||||
cd forced-update-clone &&
|
||||
git fetch --show-forced-updates origin 2>output &&
|
||||
test_i18ngrep "(forced update)" output
|
||||
) &&
|
||||
(
|
||||
cd no-forced-update-clone &&
|
||||
git fetch --no-show-forced-updates origin 2>output &&
|
||||
test_i18ngrep ! "(forced update)" output
|
||||
)
|
||||
'
|
||||
|
||||
for section in fetch transfer
|
||||
do
|
||||
test_expect_success "$section.hideRefs affects connectivity check" '
|
||||
|
|
|
@ -1180,4 +1180,17 @@ test_expect_success 'fetch --all with --recurse-submodules with multiple' '
|
|||
test_line_count = 2 fetch-subs
|
||||
'
|
||||
|
||||
test_expect_success "fetch --all with --no-recurse-submodules only fetches superproject" '
|
||||
test_when_finished "rm -rf src_clone" &&
|
||||
|
||||
git clone --recurse-submodules src src_clone &&
|
||||
(
|
||||
cd src_clone &&
|
||||
git remote add secondary ../src &&
|
||||
git config submodule.recurse true &&
|
||||
git fetch --all --no-recurse-submodules 2>../fetch-log
|
||||
) &&
|
||||
! grep "Fetching submodule" fetch-log
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='git fetch output format'
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'fetch with invalid output format configuration' '
|
||||
test_when_finished "rm -rf clone" &&
|
||||
git clone . clone &&
|
||||
|
||||
test_must_fail git -C clone -c fetch.output fetch origin 2>actual.err &&
|
||||
cat >expect <<-EOF &&
|
||||
error: missing value for ${SQ}fetch.output${SQ}
|
||||
fatal: unable to parse ${SQ}fetch.output${SQ} from command-line config
|
||||
EOF
|
||||
test_cmp expect actual.err &&
|
||||
|
||||
test_must_fail git -C clone -c fetch.output= fetch origin 2>actual.err &&
|
||||
cat >expect <<-EOF &&
|
||||
fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}${SQ}
|
||||
EOF
|
||||
test_cmp expect actual.err &&
|
||||
|
||||
test_must_fail git -C clone -c fetch.output=garbage fetch origin 2>actual.err &&
|
||||
cat >expect <<-EOF &&
|
||||
fatal: invalid value for ${SQ}fetch.output${SQ}: ${SQ}garbage${SQ}
|
||||
EOF
|
||||
test_cmp expect actual.err
|
||||
'
|
||||
|
||||
test_expect_success 'fetch aligned output' '
|
||||
git clone . full-output &&
|
||||
test_commit looooooooooooong-tag &&
|
||||
(
|
||||
cd full-output &&
|
||||
git -c fetch.output=full fetch origin >actual 2>&1 &&
|
||||
grep -e "->" actual | cut -c 22- >../actual
|
||||
) &&
|
||||
cat >expect <<-\EOF &&
|
||||
main -> origin/main
|
||||
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 >actual 2>&1 &&
|
||||
grep -e "->" actual | cut -c 22- >../actual
|
||||
) &&
|
||||
cat >expect <<-\EOF &&
|
||||
main -> origin/*
|
||||
extraaa -> *
|
||||
EOF
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'fetch porcelain output' '
|
||||
test_when_finished "rm -rf porcelain" &&
|
||||
|
||||
# Set up a bunch of references that we can use to demonstrate different
|
||||
# kinds of flag symbols in the output format.
|
||||
MAIN_OLD=$(git rev-parse HEAD) &&
|
||||
git branch "fast-forward" &&
|
||||
git branch "deleted-branch" &&
|
||||
git checkout -b force-updated &&
|
||||
test_commit --no-tag force-update-old &&
|
||||
FORCE_UPDATED_OLD=$(git rev-parse HEAD) &&
|
||||
git checkout main &&
|
||||
|
||||
# Clone and pre-seed the repositories. We fetch references into two
|
||||
# namespaces so that we can test that rejected and force-updated
|
||||
# references are reported properly.
|
||||
refspecs="refs/heads/*:refs/unforced/* +refs/heads/*:refs/forced/*" &&
|
||||
git clone . porcelain &&
|
||||
git -C porcelain fetch origin $refspecs &&
|
||||
|
||||
# Now that we have set up the client repositories we can change our
|
||||
# local references.
|
||||
git branch new-branch &&
|
||||
git branch -d deleted-branch &&
|
||||
git checkout fast-forward &&
|
||||
test_commit --no-tag fast-forward-new &&
|
||||
FAST_FORWARD_NEW=$(git rev-parse HEAD) &&
|
||||
git checkout force-updated &&
|
||||
git reset --hard HEAD~ &&
|
||||
test_commit --no-tag force-update-new &&
|
||||
FORCE_UPDATED_NEW=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
- $MAIN_OLD $ZERO_OID refs/forced/deleted-branch
|
||||
- $MAIN_OLD $ZERO_OID refs/unforced/deleted-branch
|
||||
$MAIN_OLD $FAST_FORWARD_NEW refs/unforced/fast-forward
|
||||
! $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/unforced/force-updated
|
||||
* $ZERO_OID $MAIN_OLD refs/unforced/new-branch
|
||||
$MAIN_OLD $FAST_FORWARD_NEW refs/forced/fast-forward
|
||||
+ $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/forced/force-updated
|
||||
* $ZERO_OID $MAIN_OLD refs/forced/new-branch
|
||||
$MAIN_OLD $FAST_FORWARD_NEW refs/remotes/origin/fast-forward
|
||||
+ $FORCE_UPDATED_OLD $FORCE_UPDATED_NEW refs/remotes/origin/force-updated
|
||||
* $ZERO_OID $MAIN_OLD refs/remotes/origin/new-branch
|
||||
EOF
|
||||
|
||||
# Execute a dry-run fetch first. We do this to assert that the dry-run
|
||||
# and non-dry-run fetches produces the same output. Execution of the
|
||||
# fetch is expected to fail as we have a rejected reference update.
|
||||
test_must_fail git -C porcelain fetch \
|
||||
--porcelain --dry-run --prune origin $refspecs >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
# And now we perform a non-dry-run fetch.
|
||||
test_must_fail git -C porcelain fetch \
|
||||
--porcelain --prune origin $refspecs >actual 2>stderr &&
|
||||
test_cmp expect actual &&
|
||||
test_must_be_empty stderr
|
||||
'
|
||||
|
||||
test_expect_success 'fetch porcelain with multiple remotes' '
|
||||
test_when_finished "rm -rf porcelain" &&
|
||||
|
||||
git switch --create multiple-remotes &&
|
||||
git clone . porcelain &&
|
||||
git -C porcelain remote add second-remote "$PWD" &&
|
||||
git -C porcelain fetch second-remote &&
|
||||
|
||||
test_commit --no-tag multi-commit &&
|
||||
old_commit=$(git rev-parse HEAD~) &&
|
||||
new_commit=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
$old_commit $new_commit refs/remotes/origin/multiple-remotes
|
||||
$old_commit $new_commit refs/remotes/second-remote/multiple-remotes
|
||||
EOF
|
||||
|
||||
git -C porcelain fetch --porcelain --all >actual 2>stderr &&
|
||||
test_cmp expect actual &&
|
||||
test_must_be_empty stderr
|
||||
'
|
||||
|
||||
test_expect_success 'fetch porcelain refuses to work with submodules' '
|
||||
test_when_finished "rm -rf porcelain" &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
fatal: options ${SQ}--porcelain${SQ} and ${SQ}--recurse-submodules${SQ} cannot be used together
|
||||
EOF
|
||||
|
||||
git init porcelain &&
|
||||
test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=yes 2>stderr &&
|
||||
test_cmp expect stderr &&
|
||||
|
||||
test_must_fail git -C porcelain fetch --porcelain --recurse-submodules=on-demand 2>stderr &&
|
||||
test_cmp expect stderr
|
||||
'
|
||||
|
||||
test_expect_success 'fetch porcelain overrides fetch.output config' '
|
||||
test_when_finished "rm -rf porcelain" &&
|
||||
|
||||
git switch --create config-override &&
|
||||
git clone . porcelain &&
|
||||
test_commit new-commit &&
|
||||
old_commit=$(git rev-parse HEAD~) &&
|
||||
new_commit=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
$old_commit $new_commit refs/remotes/origin/config-override
|
||||
* $ZERO_OID $new_commit refs/tags/new-commit
|
||||
EOF
|
||||
|
||||
git -C porcelain -c fetch.output=compact fetch --porcelain >stdout 2>stderr &&
|
||||
test_must_be_empty stderr &&
|
||||
test_cmp expect stdout
|
||||
'
|
||||
|
||||
test_expect_success 'fetch --no-porcelain overrides previous --porcelain' '
|
||||
test_when_finished "rm -rf no-porcelain" &&
|
||||
|
||||
git switch --create no-porcelain &&
|
||||
git clone . no-porcelain &&
|
||||
test_commit --no-tag no-porcelain &&
|
||||
old_commit=$(git rev-parse --short HEAD~) &&
|
||||
new_commit=$(git rev-parse --short HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
From $(test-tool path-utils real_path .)/.
|
||||
$old_commit..$new_commit no-porcelain -> origin/no-porcelain
|
||||
EOF
|
||||
|
||||
git -C no-porcelain fetch --porcelain --no-porcelain >stdout 2>stderr &&
|
||||
test_cmp expect stderr &&
|
||||
test_must_be_empty stdout
|
||||
'
|
||||
|
||||
test_expect_success 'fetch output with HEAD' '
|
||||
test_when_finished "rm -rf head" &&
|
||||
git clone . head &&
|
||||
|
||||
git -C head fetch --dry-run origin HEAD >actual.out 2>actual.err &&
|
||||
cat >expect <<-EOF &&
|
||||
From $(test-tool path-utils real_path .)/.
|
||||
* branch HEAD -> FETCH_HEAD
|
||||
EOF
|
||||
test_must_be_empty actual.out &&
|
||||
test_cmp expect actual.err &&
|
||||
|
||||
git -C head fetch origin HEAD >actual.out 2>actual.err &&
|
||||
test_must_be_empty actual.out &&
|
||||
test_cmp expect actual.err &&
|
||||
|
||||
git -C head fetch --dry-run origin HEAD:foo >actual.out 2>actual.err &&
|
||||
cat >expect <<-EOF &&
|
||||
From $(test-tool path-utils real_path .)/.
|
||||
* [new ref] HEAD -> foo
|
||||
EOF
|
||||
test_must_be_empty actual.out &&
|
||||
test_cmp expect actual.err &&
|
||||
|
||||
git -C head fetch origin HEAD:foo >actual.out 2>actual.err &&
|
||||
test_must_be_empty actual.out &&
|
||||
test_cmp expect actual.err
|
||||
'
|
||||
|
||||
test_expect_success 'fetch porcelain output with HEAD' '
|
||||
test_when_finished "rm -rf head" &&
|
||||
git clone . head &&
|
||||
COMMIT_ID=$(git rev-parse HEAD) &&
|
||||
|
||||
git -C head fetch --porcelain --dry-run origin HEAD >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
* $ZERO_OID $COMMIT_ID FETCH_HEAD
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
git -C head fetch --porcelain origin HEAD >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
git -C head fetch --porcelain --dry-run origin HEAD:foo >actual &&
|
||||
cat >expect <<-EOF &&
|
||||
* $ZERO_OID $COMMIT_ID refs/heads/foo
|
||||
EOF
|
||||
test_cmp expect actual &&
|
||||
|
||||
git -C head fetch --porcelain origin HEAD:foo >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'fetch output with object ID' '
|
||||
test_when_finished "rm -rf object-id" &&
|
||||
git clone . object-id &&
|
||||
commit=$(git rev-parse HEAD) &&
|
||||
|
||||
git -C object-id fetch --dry-run origin $commit:object-id >actual.out 2>actual.err &&
|
||||
cat >expect <<-EOF &&
|
||||
From $(test-tool path-utils real_path .)/.
|
||||
* [new ref] $commit -> object-id
|
||||
EOF
|
||||
test_must_be_empty actual.out &&
|
||||
test_cmp expect actual.err &&
|
||||
|
||||
git -C object-id fetch origin $commit:object-id >actual.out 2>actual.err &&
|
||||
test_must_be_empty actual.out &&
|
||||
test_cmp expect actual.err
|
||||
'
|
||||
|
||||
test_expect_success '--no-show-forced-updates' '
|
||||
mkdir forced-updates &&
|
||||
(
|
||||
cd forced-updates &&
|
||||
git init &&
|
||||
test_commit 1 &&
|
||||
test_commit 2
|
||||
) &&
|
||||
git clone forced-updates forced-update-clone &&
|
||||
git clone forced-updates no-forced-update-clone &&
|
||||
git -C forced-updates reset --hard HEAD~1 &&
|
||||
(
|
||||
cd forced-update-clone &&
|
||||
git fetch --show-forced-updates origin 2>output &&
|
||||
test_i18ngrep "(forced update)" output
|
||||
) &&
|
||||
(
|
||||
cd no-forced-update-clone &&
|
||||
git fetch --no-show-forced-updates origin 2>output &&
|
||||
test_i18ngrep ! "(forced update)" output
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue