It is only possible to configure the output format that git-fetch(1) uses by setting it via a config key. While this interface may be fine as long as we only have the current "full" and "compact" output formats, where it is unlikely that the user will have to change them regularly. But we're about to introduce a new machine-parseable interface where the current mechanism feels a little bit indirect and rigid. Introduce a new `--output-format` option that allows the user to change the desired format more directly. Signed-off-by: Patrick Steinhardt --- Documentation/fetch-options.txt | 5 +++ builtin/fetch.c | 48 ++++++++++++++++++---- t/t5574-fetch-output.sh | 72 +++++++++++++++++++++++++++------ 3 files changed, 106 insertions(+), 19 deletions(-) diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 622bd84768..654f96f79d 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -78,6 +78,11 @@ linkgit:git-config[1]. --dry-run:: Show what would be done, without making any changes. +--output-format:: + 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. + ifndef::git-pull[] --[no-]write-fetch-head:: Write the list of remote refs fetched in the `FETCH_HEAD` diff --git a/builtin/fetch.c b/builtin/fetch.c index 97a510649c..30099b2ac3 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -52,6 +52,13 @@ enum display_format { DISPLAY_FORMAT_UNKNOWN = 0, DISPLAY_FORMAT_FULL, DISPLAY_FORMAT_COMPACT, + DISPLAY_FORMAT_MAX, +}; + +static const char * const display_formats[DISPLAY_FORMAT_MAX] = { + NULL, + "full", + "compact", }; struct display_state { @@ -1879,7 +1886,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; @@ -1894,6 +1902,9 @@ static int fetch_multiple(struct string_list *list, int max_children) "--no-write-commit-graph", NULL); add_options_to_argv(&argv); + if (format != DISPLAY_FORMAT_UNKNOWN) + strvec_pushf(&argv, "--output-format=%s", display_formats[format]); + if (max_children != 1 && list->nr != 1) { struct parallel_fetch_state state = { argv.v, list, 0, 0 }; const struct run_process_parallel_opts opts = { @@ -2050,6 +2061,29 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, return exit_code; } +static enum display_format parse_display_format(const char *format) +{ + for (int i = 0; i < ARRAY_SIZE(display_formats); i++) + if (display_formats[i] && !strcmp(display_formats[i], format)) + return i; + return DISPLAY_FORMAT_UNKNOWN; +} + +static int opt_parse_output_format(const struct option *opt, const char *arg, int unset) +{ + enum display_format *format = opt->value, parsed; + + if (unset || !arg) + return 1; + + parsed = parse_display_format(arg); + if (parsed == DISPLAY_FORMAT_UNKNOWN) + return error(_("unsupported output format '%s'"), arg); + *format = parsed; + + return 0; +} + int cmd_fetch(int argc, const char **argv, const char *prefix) { const char *bundle_uri; @@ -2102,6 +2136,8 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules), OPT_BOOL(0, "dry-run", &dry_run, N_("dry run")), + OPT_CALLBACK(0, "output-format", &display_format, N_("format"), N_("output format"), + opt_parse_output_format), 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")), @@ -2181,11 +2217,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) const char *format = "full"; git_config_get_string_tmp("fetch.output", &format); - if (!strcasecmp(format, "full")) - display_format = DISPLAY_FORMAT_FULL; - else if (!strcasecmp(format, "compact")) - display_format = DISPLAY_FORMAT_COMPACT; - else + + display_format = parse_display_format(format); + if (display_format == DISPLAY_FORMAT_UNKNOWN) die(_("invalid value for '%s': '%s'"), "fetch.output", format); } @@ -2339,7 +2373,7 @@ 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, display_format); } diff --git a/t/t5574-fetch-output.sh b/t/t5574-fetch-output.sh index b9dcdade63..662c960f94 100755 --- a/t/t5574-fetch-output.sh +++ b/t/t5574-fetch-output.sh @@ -24,14 +24,37 @@ test_expect_success 'fetch with invalid output format configuration' ' test_cmp expect actual ' +test_expect_success 'fetch with invalid output format via command line' ' + test_must_fail git fetch --output-format >actual 2>&1 && + cat >expect <<-EOF && + error: option \`output-format${SQ} requires a value + EOF + test_cmp expect actual && + + test_must_fail git fetch --output-format= origin >actual 2>&1 && + cat >expect <<-EOF && + error: unsupported output format ${SQ}${SQ} + EOF + test_cmp expect actual && + + test_must_fail git fetch --output-format=garbage origin >actual 2>&1 && + cat >expect <<-EOF && + error: unsupported output format ${SQ}garbage${SQ} + EOF + test_cmp expect actual +' + test_expect_success 'fetch aligned output' ' - git clone . full-output && + test_when_finished "rm -rf full-cfg full-cli" && + git clone . full-cfg && + git clone . full-cli && test_commit looooooooooooong-tag && - ( - cd full-output && - git -c fetch.output=full fetch origin >actual 2>&1 && - grep -e "->" actual | cut -c 22- >../actual - ) && + + git -C full-cfg -c fetch.output=full fetch origin >actual-cfg 2>&1 && + git -C full-cli fetch --output-format=full origin >actual-cli 2>&1 && + test_cmp actual-cfg actual-cli && + + grep -e "->" actual-cfg | cut -c 22- >actual && cat >expect <<-\EOF && main -> origin/main looooooooooooong-tag -> looooooooooooong-tag @@ -40,13 +63,16 @@ test_expect_success 'fetch aligned output' ' ' test_expect_success 'fetch compact output' ' - git clone . compact && + test_when_finished "rm -rf compact-cfg compact-cli" && + git clone . compact-cli && + git clone . compact-cfg && test_commit extraaa && - ( - cd compact && - git -c fetch.output=compact fetch origin >actual 2>&1 && - grep -e "->" actual | cut -c 22- >../actual - ) && + + git -C compact-cfg -c fetch.output=compact fetch origin >actual-cfg 2>&1 && + git -C compact-cli fetch --output-format=compact origin >actual-cli 2>&1 && + test_cmp actual-cfg actual-cli && + + grep -e "->" actual-cfg | cut -c 22- >actual && cat >expect <<-\EOF && main -> origin/* extraaa -> * @@ -54,6 +80,28 @@ test_expect_success 'fetch compact output' ' test_cmp expect actual ' +test_expect_success 'fetch compact output with multiple remotes' ' + test_when_finished "rm -rf compact-cfg compact-cli" && + + git clone . compact-cli && + git -C compact-cli remote add second-remote "$PWD" && + git clone . compact-cfg && + git -C compact-cfg remote add second-remote "$PWD" && + test_commit multi-commit && + + git -C compact-cfg -c fetch.output=compact fetch --all >actual-cfg 2>&1 && + git -C compact-cli fetch --output-format=compact --all >actual-cli 2>&1 && + test_cmp actual-cfg actual-cli && + + grep -e "->" actual-cfg | cut -c 22- >actual && + cat >expect <<-\EOF && + main -> origin/* + multi-commit -> * + main -> second-remote/* + EOF + test_cmp expect actual +' + test_expect_success 'fetch output with HEAD and --dry-run' ' test_when_finished "rm -rf head" && git clone . head && -- 2.40.1