Merge branch 'nd/completion-more-parameters'
authorJunio C Hamano <gitster@pobox.com>
Thu, 7 Mar 2019 00:59:57 +0000 (09:59 +0900)
committerJunio C Hamano <gitster@pobox.com>
Thu, 7 Mar 2019 00:59:58 +0000 (09:59 +0900)
The command line completion (in contrib/) has been taught to
complete more subcommand parameters.

* nd/completion-more-parameters:
completion: add more parameter value completion

1  2 
builtin/am.c
builtin/commit.c
contrib/completion/git-completion.bash
diff.c
merge-recursive.c
pretty.c
ref-filter.c
submodule.c
diff --combined builtin/am.c
index 86e33495b039bd44d4b59625388d55cd624ccd26,e8522450cb3c6e884cead3b8a9ec6fc2acd2a03e..4fb107a9d1bdf36ff4c71d761abdda4d50d0e24b
@@@ -453,7 -453,6 +453,7 @@@ static int run_post_rewrite_hook(const 
  
        cp.in = xopen(am_path(state, "rewritten"), O_RDONLY);
        cp.stdout_to_stderr = 1;
 +      cp.trace2_hook_name = "post-rewrite";
  
        ret = run_command(&cp);
  
@@@ -1580,7 -1579,6 +1580,7 @@@ static void do_commit(const struct am_s
        }
  
        author = fmt_ident(state->author_name, state->author_email,
 +              WANT_AUTHOR_IDENT,
                        state->ignore_date ? NULL : state->author_date,
                        IDENT_STRICT);
  
@@@ -2121,6 -2119,10 +2121,10 @@@ static int parse_opt_patchformat(const 
                *opt_value = PATCH_FORMAT_HG;
        else if (!strcmp(arg, "mboxrd"))
                *opt_value = PATCH_FORMAT_MBOXRD;
+       /*
+        * Please update $__git_patchformat in git-completion.bash
+        * when you add new options
+        */
        else
                return error(_("Invalid value for --patch-format: %s"), arg);
        return 0;
diff --combined builtin/commit.c
index 8d7a613fda3595380a84d97bbaa7c203954e8dd0,b9e396f5e3c3be5e735d46746f8176c623f2e536..f17537474a9d0ed9672393fc5dc3845932dd4dd4
@@@ -609,8 -609,7 +609,8 @@@ static void determine_author_info(struc
                set_ident_var(&date, strbuf_detach(&date_buf, NULL));
        }
  
 -      strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
 +      strbuf_addstr(author_ident, fmt_ident(name, email, WANT_AUTHOR_IDENT, date,
 +                              IDENT_STRICT));
        assert_split_ident(&author, author_ident);
        export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
        export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
@@@ -1039,6 -1038,10 +1039,10 @@@ static void handle_untracked_files_arg(
                s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
        else if (!strcmp(untracked_files_arg, "all"))
                s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+       /*
+        * Please update $__git_untracked_file_modes in
+        * git-completion.bash when you add new options
+        */
        else
                die(_("Invalid untracked files mode '%s'"), untracked_files_arg);
  }
@@@ -1180,6 -1183,10 +1184,10 @@@ static int parse_and_validate_options(i
        else if (!strcmp(cleanup_arg, "scissors"))
                cleanup_mode = use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
                                            COMMIT_MSG_CLEANUP_SPACE;
+       /*
+        * Please update _git_commit() in git-completion.bash when you
+        * add new options.
+        */
        else
                die(_("Invalid cleanup mode %s"), cleanup_arg);
  
index de56879960d5224437e60d81c3e990ac7ed97a18,907855184c0ca19ff2d580e6d0de88c979cbf9a3..976e4a6548fdedf630b2f5e4539df1f43d83809f
@@@ -853,6 -853,11 +853,11 @@@ __git_compute_merge_strategies (
        __git_merge_strategies=$(__git_list_merge_strategies)
  }
  
+ __git_merge_strategy_options="ours theirs subtree subtree= patience
+       histogram diff-algorithm= ignore-space-change ignore-all-space
+       ignore-space-at-eol renormalize no-renormalize no-renames
+       find-renames find-renames= rename-threshold="
  __git_complete_revlist_file ()
  {
        local dequoted_word pfx ls ref cur_="$cur"
@@@ -996,12 -1001,21 +1001,21 @@@ __git_complete_strategy (
        -s|--strategy)
                __gitcomp "$__git_merge_strategies"
                return 0
+               ;;
+       -X)
+               __gitcomp "$__git_merge_strategy_options"
+               return 0
+               ;;
        esac
        case "$cur" in
        --strategy=*)
                __gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
                return 0
                ;;
+       --strategy-option=*)
+               __gitcomp "$__git_merge_strategy_options" "" "${cur##--strategy-option=}"
+               return 0
+               ;;
        esac
        return 1
  }
@@@ -1163,6 -1177,7 +1177,7 @@@ __git_count_arguments (
  }
  
  __git_whitespacelist="nowarn warn error error-all fix"
+ __git_patchformat="mbox stgit stgit-series hg mboxrd"
  __git_am_inprogress_options="--skip --continue --resolved --abort --quit --show-current-patch"
  
  _git_am ()
                __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
                return
                ;;
+       --patch-format=*)
+               __gitcomp "$__git_patchformat" "" "${cur##--patch-format=}"
+               return
+               ;;
        --*)
                __gitcomp_builtin am "" \
                        "$__git_am_inprogress_options"
@@@ -1200,6 -1219,10 +1219,10 @@@ _git_apply (
  _git_add ()
  {
        case "$cur" in
+       --chmod=*)
+               __gitcomp "+x -x" "" "${cur##--chmod=}"
+               return
+               ;;
        --*)
                __gitcomp_builtin add
                return
@@@ -1260,6 -1283,8 +1283,8 @@@ _git_bisect (
        esac
  }
  
+ __git_ref_fieldlist="refname objecttype objectsize objectname upstream push HEAD symref"
  _git_branch ()
  {
        local i c=1 only_local_ref="n" has_r="n"
@@@ -1343,6 -1368,9 +1368,9 @@@ _git_cherry_pick (
                __gitcomp "$__git_cherry_pick_inprogress_options"
                return
        fi
+       __git_complete_strategy && return
        case "$cur" in
        --*)
                __gitcomp_builtin cherry-pick "" \
@@@ -1506,6 -1534,10 +1534,10 @@@ _git_fetch (
                __gitcomp "$__git_fetch_recurse_submodules" "" "${cur##--recurse-submodules=}"
                return
                ;;
+       --filter=*)
+               __gitcomp "blob:none blob:limit= sparse:oid= sparse:path=" "" "${cur##--filter=}"
+               return
+               ;;
        --*)
                __gitcomp_builtin fetch
                return
@@@ -1702,8 -1734,8 +1734,8 @@@ __git_log_shortlog_options=
        --all-match --invert-grep
  "
  
- __git_log_pretty_formats="oneline short medium full fuller email raw format:"
- __git_log_date_formats="relative iso8601 rfc2822 short local default raw"
+ __git_log_pretty_formats="oneline short medium full fuller email raw format: mboxrd"
+ __git_log_date_formats="relative iso8601 iso8601-strict rfc2822 short local default raw unix format:"
  
  _git_log ()
  {
@@@ -2221,7 -2253,7 +2253,7 @@@ _git_config (
                return
                ;;
        diff.submodule)
-               __gitcomp "log short"
+               __gitcomp "$__git_diff_submodule_formats"
                return
                ;;
        help.format)
@@@ -2388,6 -2420,10 +2420,10 @@@ _git_remote (
  _git_replace ()
  {
        case "$cur" in
+       --format=*)
+               __gitcomp "short medium long" "" "${cur##--format=}"
+               return
+               ;;
        --*)
                __gitcomp_builtin replace
                return
@@@ -2429,6 -2465,7 +2465,7 @@@ _git_revert (
                __gitcomp "$__git_revert_inprogress_options"
                return
        fi
+       __git_complete_strategy && return
        case "$cur" in
        --*)
                __gitcomp_builtin revert "" \
@@@ -2573,7 -2610,7 +2610,7 @@@ _git_submodule (
  {
        __git_has_doubledash && return
  
 -      local subcommands="add status init deinit update summary foreach sync"
 +      local subcommands="add status init deinit update summary foreach sync absorbgitdirs"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                case "$cur" in
diff --combined diff.c
index 62c7e5b7780eb35968eb96407e87cc1d60588496,a1f301b66ce3919dfb970f64ce6aae513a638ee2..70f25c4db4f26b4eef0a995c54da708c122e314a
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -23,7 -23,6 +23,7 @@@
  #include "argv-array.h"
  #include "graph.h"
  #include "packfile.h"
 +#include "parse-options.h"
  #include "help.h"
  
  #ifdef NO_FAST_WORKING_DIRECTORY
@@@ -179,6 -178,10 +179,10 @@@ static int parse_submodule_params(struc
                options->submodule_format = DIFF_SUBMODULE_SHORT;
        else if (!strcmp(value, "diff"))
                options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
+       /*
+        * Please update $__git_diff_submodule_formats in
+        * git-completion.bash when you add new formats.
+        */
        else
                return -1;
        return 0;
@@@ -205,6 -208,10 +209,10 @@@ long parse_algorithm_value(const char *
                return XDF_PATIENCE_DIFF;
        else if (!strcasecmp(value, "histogram"))
                return XDF_HISTOGRAM_DIFF;
+       /*
+        * Please update $__git_diff_algorithms in git-completion.bash
+        * when you add new algorithms.
+        */
        return -1;
  }
  
@@@ -1614,7 -1621,8 +1622,7 @@@ static int new_blank_line_at_eof(struc
        return ws_blank_line(line, len, ecbdata->ws_rule);
  }
  
 -static void emit_add_line(const char *reset,
 -                        struct emit_callback *ecbdata,
 +static void emit_add_line(struct emit_callback *ecbdata,
                          const char *line, int len)
  {
        unsigned flags = WSEH_NEW | ecbdata->ws_rule;
        emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_PLUS, line, len, flags);
  }
  
 -static void emit_del_line(const char *reset,
 -                        struct emit_callback *ecbdata,
 +static void emit_del_line(struct emit_callback *ecbdata,
                          const char *line, int len)
  {
        unsigned flags = WSEH_OLD | ecbdata->ws_rule;
        emit_diff_symbol(ecbdata->opt, DIFF_SYMBOL_MINUS, line, len, flags);
  }
  
 -static void emit_context_line(const char *reset,
 -                            struct emit_callback *ecbdata,
 +static void emit_context_line(struct emit_callback *ecbdata,
                              const char *line, int len)
  {
        unsigned flags = WSEH_CONTEXT | ecbdata->ws_rule;
@@@ -1740,6 -1750,7 +1748,6 @@@ static void emit_rewrite_lines(struct e
                               int prefix, const char *data, int size)
  {
        const char *endp = NULL;
 -      const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
  
        while (0 < size) {
                int len;
                len = endp ? (endp - data + 1) : size;
                if (prefix != '+') {
                        ecb->lno_in_preimage++;
 -                      emit_del_line(reset, ecb, data, len);
 +                      emit_del_line(ecb, data, len);
                } else {
                        ecb->lno_in_postimage++;
 -                      emit_add_line(reset, ecb, data, len);
 +                      emit_add_line(ecb, data, len);
                }
                size -= len;
                data += len;
@@@ -2288,7 -2299,7 +2296,7 @@@ const char *diff_line_prefix(struct dif
        return msgbuf->buf;
  }
  
 -static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len)
 +static unsigned long sane_truncate_line(char *line, unsigned long len)
  {
        const char *cp;
        unsigned long allot;
@@@ -2322,6 -2333,7 +2330,6 @@@ static void find_lno(const char *line, 
  static void fn_out_consume(void *priv, char *line, unsigned long len)
  {
        struct emit_callback *ecbdata = priv;
 -      const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        struct diff_options *o = ecbdata->opt;
  
        o->found_changes = 1;
        if (line[0] == '@') {
                if (ecbdata->diff_words)
                        diff_words_flush(ecbdata);
 -              len = sane_truncate_line(ecbdata, line, len);
 +              len = sane_truncate_line(line, len);
                find_lno(line, ecbdata);
                emit_hunk_header(ecbdata, line, len);
                return;
        switch (line[0]) {
        case '+':
                ecbdata->lno_in_postimage++;
 -              emit_add_line(reset, ecbdata, line + 1, len - 1);
 +              emit_add_line(ecbdata, line + 1, len - 1);
                break;
        case '-':
                ecbdata->lno_in_preimage++;
 -              emit_del_line(reset, ecbdata, line + 1, len - 1);
 +              emit_del_line(ecbdata, line + 1, len - 1);
                break;
        case ' ':
                ecbdata->lno_in_postimage++;
                ecbdata->lno_in_preimage++;
 -              emit_context_line(reset, ecbdata, line + 1, len - 1);
 +              emit_context_line(ecbdata, line + 1, len - 1);
                break;
        default:
                /* incomplete line at the end */
@@@ -4179,6 -4191,7 +4187,6 @@@ static void run_external_diff(const cha
                              struct diff_filespec *one,
                              struct diff_filespec *two,
                              const char *xfrm_msg,
 -                            int complete_rewrite,
                              struct diff_options *o)
  {
        struct argv_array argv = ARGV_ARRAY_INIT;
@@@ -4336,7 -4349,8 +4344,7 @@@ static void run_diff_cmd(const char *pg
        }
  
        if (pgm) {
 -              run_external_diff(pgm, name, other, one, two, xfrm_msg,
 -                                complete_rewrite, o);
 +              run_external_diff(pgm, name, other, one, two, xfrm_msg, o);
                return;
        }
        if (one && two)
@@@ -4485,8 -4499,6 +4493,8 @@@ static void run_checkdiff(struct diff_f
        builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
  }
  
 +static void prep_parse_options(struct diff_options *options);
 +
  void repo_diff_setup(struct repository *r, struct diff_options *options)
  {
        memcpy(options, &default_diff_options, sizeof(*options));
  
        options->color_moved = diff_color_moved_default;
        options->color_moved_ws_handling = diff_color_moved_ws_default;
 +
 +      prep_parse_options(options);
  }
  
  void diff_setup_done(struct diff_options *options)
  
        if (!options->use_color || external_diff())
                options->color_moved = 0;
 +
 +      FREE_AND_NULL(options->parseopts);
  }
  
  static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@@ -4926,47 -4934,6 +4934,47 @@@ static int parse_objfind_opt(struct dif
        return 1;
  }
  
 +static int diff_opt_unified(const struct option *opt,
 +                          const char *arg, int unset)
 +{
 +      struct diff_options *options = opt->value;
 +      char *s;
 +
 +      BUG_ON_OPT_NEG(unset);
 +
 +      options->context = strtol(arg, &s, 10);
 +      if (*s)
 +              return error(_("%s expects a numerical value"), "--unified");
 +      enable_patch_output(&options->output_format);
 +
 +      return 0;
 +}
 +
 +static void prep_parse_options(struct diff_options *options)
 +{
 +      struct option parseopts[] = {
 +              OPT_GROUP(N_("Diff output format options")),
 +              OPT_BITOP('p', "patch", &options->output_format,
 +                        N_("generate patch"),
 +                        DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
 +              OPT_BITOP('u', NULL, &options->output_format,
 +                        N_("generate patch"),
 +                        DIFF_FORMAT_PATCH, DIFF_FORMAT_NO_OUTPUT),
 +              OPT_CALLBACK_F('U', "unified", options, N_("<n>"),
 +                             N_("generate diffs with <n> lines context"),
 +                             PARSE_OPT_NONEG, diff_opt_unified),
 +              OPT_BOOL('W', "function-context", &options->flags.funccontext,
 +                       N_("generate diffs with <n> lines context")),
 +              OPT_BIT_F(0, "raw", &options->output_format,
 +                        N_("generate the diff in raw format"),
 +                        DIFF_FORMAT_RAW, PARSE_OPT_NONEG),
 +              OPT_END()
 +      };
 +
 +      ALLOC_ARRAY(options->parseopts, ARRAY_SIZE(parseopts));
 +      memcpy(options->parseopts, parseopts, sizeof(parseopts));
 +}
 +
  int diff_opt_parse(struct diff_options *options,
                   const char **av, int ac, const char *prefix)
  {
        if (!prefix)
                prefix = "";
  
 +      ac = parse_options(ac, av, prefix, options->parseopts, NULL,
 +                         PARSE_OPT_KEEP_DASHDASH |
 +                         PARSE_OPT_KEEP_UNKNOWN |
 +                         PARSE_OPT_NO_INTERNAL_HELP |
 +                         PARSE_OPT_ONE_SHOT |
 +                         PARSE_OPT_STOP_AT_NON_OPTION);
 +
 +      if (ac)
 +              return ac;
 +
        /* Output format options */
 -      if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")
 -          || opt_arg(arg, 'U', "unified", &options->context))
 -              enable_patch_output(&options->output_format);
 -      else if (!strcmp(arg, "--raw"))
 -              options->output_format |= DIFF_FORMAT_RAW;
 -      else if (!strcmp(arg, "--patch-with-raw")) {
 +      if (!strcmp(arg, "--patch-with-raw")) {
                enable_patch_output(&options->output_format);
                options->output_format |= DIFF_FORMAT_RAW;
        } else if (!strcmp(arg, "--numstat"))
        else if (opt_arg(arg, '\0', "inter-hunk-context",
                         &options->interhunkcontext))
                ;
 -      else if (!strcmp(arg, "-W"))
 -              options->flags.funccontext = 1;
 -      else if (!strcmp(arg, "--function-context"))
 -              options->flags.funccontext = 1;
 -      else if (!strcmp(arg, "--no-function-context"))
 -              options->flags.funccontext = 0;
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
                char *path = prefix_filename(prefix, optarg);
                options->file = xfopen(path, "w");
@@@ -6264,7 -6232,7 +6272,7 @@@ static int diffnamecmp(const void *a_, 
        return strcmp(name_a, name_b);
  }
  
 -void diffcore_fix_diff_index(struct diff_options *options)
 +void diffcore_fix_diff_index(void)
  {
        struct diff_queue_struct *q = &diff_queued_diff;
        QSORT(q->queue, q->nr, diffnamecmp);
diff --combined merge-recursive.c
index f270fa66f3b54ca4a8eaf9358f4e53631c853f70,28b36c08f2a8dff017bfb8cbfec5b98ea2e04093..6c40c61c4728d9224006768599d7bf2ded744bb1
@@@ -1402,7 -1402,8 +1402,7 @@@ static int merge_mode_and_contents(stru
  
  static int handle_rename_via_dir(struct merge_options *o,
                                 struct diff_filepair *pair,
 -                               const char *rename_branch,
 -                               const char *other_branch)
 +                               const char *rename_branch)
  {
        /*
         * Handle file adds that need to be renamed due to directory rename
@@@ -2212,7 -2213,8 +2212,7 @@@ static void handle_directory_level_conf
        remove_hashmap_entries(dir_re_merge, &remove_from_merge);
  }
  
 -static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs,
 -                                           struct tree *tree)
 +static struct hashmap *get_directory_renames(struct diff_queue_struct *pairs)
  {
        struct hashmap *dir_renames;
        struct hashmap_iter iter;
@@@ -2458,7 -2460,8 +2458,7 @@@ static void apply_directory_rename_modi
                                                 struct tree *o_tree,
                                                 struct tree *a_tree,
                                                 struct tree *b_tree,
 -                                               struct string_list *entries,
 -                                               int *clean)
 +                                               struct string_list *entries)
  {
        struct string_list_item *item;
        int stage = (tree == a_tree ? 2 : 3);
@@@ -2629,7 -2632,8 +2629,7 @@@ static struct string_list *get_renames(
                        apply_directory_rename_modifications(o, pair, new_path,
                                                             re, tree, o_tree,
                                                             a_tree, b_tree,
 -                                                           entries,
 -                                                           clean_merge);
 +                                                           entries);
        }
  
        hashmap_iter_init(&collisions, &iter);
@@@ -2940,8 -2944,8 +2940,8 @@@ static int detect_and_process_renames(s
        merge_pairs = get_diffpairs(o, common, merge);
  
        if (o->detect_directory_renames) {
 -              dir_re_head = get_directory_renames(head_pairs, head);
 -              dir_re_merge = get_directory_renames(merge_pairs, merge);
 +              dir_re_head = get_directory_renames(head_pairs);
 +              dir_re_merge = get_directory_renames(merge_pairs);
  
                handle_directory_level_conflicts(o,
                                                 dir_re_head, head,
@@@ -3264,7 -3268,8 +3264,7 @@@ static int process_entry(struct merge_o
                        clean_merge = 1;
                        if (handle_rename_via_dir(o,
                                                  conflict_info->pair1,
 -                                                conflict_info->branch1,
 -                                                conflict_info->branch2))
 +                                                conflict_info->branch1))
                                clean_merge = -1;
                        break;
                case RENAME_ADD:
@@@ -3759,6 -3764,10 +3759,10 @@@ int parse_merge_opt(struct merge_option
                        return -1;
                o->merge_detect_rename = 1;
        }
+       /*
+        * Please update $__git_merge_strategy_options in
+        * git-completion.bash when you add new options
+        */
        else
                return -1;
        return 0;
diff --combined pretty.c
index d2995b13edf4c64965321882677f28847f47e21c,55739a03374322049b75d80f6009d5a6f2a9a85f..f496f0f1284db4ea7c6a20022311381bd9dc1b64
+++ b/pretty.c
@@@ -98,6 -98,10 +98,10 @@@ static void setup_commit_formats(void
                { "fuller",     CMIT_FMT_FULLER,        0,      8 },
                { "full",       CMIT_FMT_FULL,          0,      8 },
                { "oneline",    CMIT_FMT_ONELINE,       1,      0 }
+               /*
+                * Please update $__git_log_pretty_formats in
+                * git-completion.bash when you add new formats.
+                */
        };
        commit_formats_len = ARRAY_SIZE(builtin_formats);
        builtin_formats_len = commit_formats_len;
@@@ -1057,26 -1061,13 +1061,26 @@@ static size_t parse_padding_placeholder
        return 0;
  }
  
 -static int match_placeholder_arg(const char *to_parse, const char *candidate,
 -                               const char **end)
 +static int match_placeholder_arg_value(const char *to_parse, const char *candidate,
 +                                     const char **end, const char **valuestart,
 +                                     size_t *valuelen)
  {
        const char *p;
  
        if (!(skip_prefix(to_parse, candidate, &p)))
                return 0;
 +      if (valuestart) {
 +              if (*p == '=') {
 +                      *valuestart = p + 1;
 +                      *valuelen = strcspn(*valuestart, ",)");
 +                      p = *valuestart + *valuelen;
 +              } else {
 +                      if (*p != ',' && *p != ')')
 +                              return 0;
 +                      *valuestart = NULL;
 +                      *valuelen = 0;
 +              }
 +      }
        if (*p == ',') {
                *end = p + 1;
                return 1;
        return 0;
  }
  
 +static int match_placeholder_bool_arg(const char *to_parse, const char *candidate,
 +                                    const char **end, int *val)
 +{
 +      const char *argval;
 +      char *strval;
 +      size_t arglen;
 +      int v;
 +
 +      if (!match_placeholder_arg_value(to_parse, candidate, end, &argval, &arglen))
 +              return 0;
 +
 +      if (!argval) {
 +              *val = 1;
 +              return 1;
 +      }
 +
 +      strval = xstrndup(argval, arglen);
 +      v = git_parse_maybe_bool(strval);
 +      free(strval);
 +
 +      if (v == -1)
 +              return 0;
 +
 +      *val = v;
 +
 +      return 1;
 +}
 +
 +static int format_trailer_match_cb(const struct strbuf *key, void *ud)
 +{
 +      const struct string_list *list = ud;
 +      const struct string_list_item *item;
 +
 +      for_each_string_list_item (item, list) {
 +              if (key->len == (uintptr_t)item->util &&
 +                  !strncasecmp(item->string, key->buf, key->len))
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
  static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                                const char *placeholder,
                                void *context)
        const char *msg = c->message;
        struct commit_list *p;
        const char *arg;
 -      int ch;
 +      size_t res;
        char **slot;
  
        /* these are independent of the commit */
 +      res = strbuf_expand_literal_cb(sb, placeholder, NULL);
 +      if (res)
 +              return res;
 +
        switch (placeholder[0]) {
        case 'C':
                if (starts_with(placeholder + 1, "(auto)")) {
                         */
                        return ret;
                }
 -      case 'n':               /* newline */
 -              strbuf_addch(sb, '\n');
 -              return 1;
 -      case 'x':
 -              /* %x00 == NUL, %x0a == LF, etc. */
 -              ch = hex2chr(placeholder + 1);
 -              if (ch < 0)
 -                      return 0;
 -              strbuf_addch(sb, ch);
 -              return 3;
        case 'w':
                if (placeholder[1] == '(') {
                        unsigned long width = 0, indent1 = 0, indent2 = 0;
  
        if (skip_prefix(placeholder, "(trailers", &arg)) {
                struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
 +              struct string_list filter_list = STRING_LIST_INIT_NODUP;
 +              struct strbuf sepbuf = STRBUF_INIT;
 +              size_t ret = 0;
  
                opts.no_divider = 1;
  
                if (*arg == ':') {
                        arg++;
                        for (;;) {
 -                              if (match_placeholder_arg(arg, "only", &arg))
 +                              const char *argval;
 +                              size_t arglen;
 +
 +                              if (match_placeholder_arg_value(arg, "key", &arg, &argval, &arglen)) {
 +                                      uintptr_t len = arglen;
 +
 +                                      if (!argval)
 +                                              goto trailer_out;
 +
 +                                      if (len && argval[len - 1] == ':')
 +                                              len--;
 +                                      string_list_append(&filter_list, argval)->util = (char *)len;
 +
 +                                      opts.filter = format_trailer_match_cb;
 +                                      opts.filter_data = &filter_list;
                                        opts.only_trailers = 1;
 -                              else if (match_placeholder_arg(arg, "unfold", &arg))
 -                                      opts.unfold = 1;
 -                              else
 +                              } else if (match_placeholder_arg_value(arg, "separator", &arg, &argval, &arglen)) {
 +                                      char *fmt;
 +
 +                                      strbuf_reset(&sepbuf);
 +                                      fmt = xstrndup(argval, arglen);
 +                                      strbuf_expand(&sepbuf, fmt, strbuf_expand_literal_cb, NULL);
 +                                      free(fmt);
 +                                      opts.separator = &sepbuf;
 +                              } else if (!match_placeholder_bool_arg(arg, "only", &arg, &opts.only_trailers) &&
 +                                         !match_placeholder_bool_arg(arg, "unfold", &arg, &opts.unfold) &&
 +                                         !match_placeholder_bool_arg(arg, "valueonly", &arg, &opts.value_only))
                                        break;
                        }
                }
                if (*arg == ')') {
                        format_trailers_from_commit(sb, msg + c->subject_off, &opts);
 -                      return arg - placeholder + 1;
 +                      ret = arg - placeholder + 1;
                }
 +      trailer_out:
 +              string_list_clear(&filter_list, 0);
 +              strbuf_release(&sepbuf);
 +              return ret;
        }
  
        return 0;       /* unknown placeholder */
diff --combined ref-filter.c
index 09b5fb439f472f3ba541b63dd01c192f689c6735,cf80d4d69b75d778df90d2e16f7d5762ef86da4f..3aca105307863f5342699babfbea2ac3035fdacb
@@@ -485,6 -485,10 +485,10 @@@ static struct 
        { "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
        { "then", SOURCE_NONE },
        { "else", SOURCE_NONE },
+       /*
+        * Please update $__git_ref_fieldlist in git-completion.bash
+        * when you add new atoms
+        */
  };
  
  #define REF_FORMATTING_STATE_INIT  { 0, NULL }
@@@ -913,7 -917,7 +917,7 @@@ static void grab_common_values(struct a
  }
  
  /* See grab_values */
 -static void grab_tag_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 +static void grab_tag_values(struct atom_value *val, int deref, struct object *obj)
  {
        int i;
        struct tag *tag = (struct tag *) obj;
  }
  
  /* See grab_values */
 -static void grab_commit_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 +static void grab_commit_values(struct atom_value *val, int deref, struct object *obj)
  {
        int i;
        struct commit *commit = (struct commit *) obj;
        }
  }
  
 -static const char *find_wholine(const char *who, int wholen, const char *buf, unsigned long sz)
 +static const char *find_wholine(const char *who, int wholen, const char *buf)
  {
        const char *eol;
        while (*buf) {
@@@ -1064,7 -1068,7 +1068,7 @@@ static void grab_date(const char *buf, 
  }
  
  /* See grab_values */
 -static void grab_person(const char *who, struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 +static void grab_person(const char *who, struct atom_value *val, int deref, void *buf)
  {
        int i;
        int wholen = strlen(who);
                    !starts_with(name + wholen, "date"))
                        continue;
                if (!wholine)
 -                      wholine = find_wholine(who, wholen, buf, sz);
 +                      wholine = find_wholine(who, wholen, buf);
                if (!wholine)
                        return; /* no point looking for it */
                if (name[wholen] == 0)
        if (strcmp(who, "tagger") && strcmp(who, "committer"))
                return; /* "author" for commit object is not wanted */
        if (!wholine)
 -              wholine = find_wholine(who, wholen, buf, sz);
 +              wholine = find_wholine(who, wholen, buf);
        if (!wholine)
                return;
        for (i = 0; i < used_atom_cnt; i++) {
        }
  }
  
 -static void find_subpos(const char *buf, unsigned long sz,
 +static void find_subpos(const char *buf,
                        const char **sub, unsigned long *sublen,
                        const char **body, unsigned long *bodylen,
                        unsigned long *nonsiglen,
@@@ -1192,7 -1196,7 +1196,7 @@@ static void append_lines(struct strbuf 
  }
  
  /* See grab_values */
 -static void grab_sub_body_contents(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 +static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
  {
        int i;
        const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
                    !starts_with(name, "contents"))
                        continue;
                if (!subpos)
 -                      find_subpos(buf, sz,
 +                      find_subpos(buf,
                                    &subpos, &sublen,
                                    &bodypos, &bodylen, &nonsiglen,
                                    &sigpos, &siglen);
@@@ -1265,19 -1269,19 +1269,19 @@@ static void fill_missing_values(struct 
   * pointed at by the ref itself; otherwise it is the object the
   * ref (which is a tag) refers to.
   */
 -static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz)
 +static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf)
  {
        switch (obj->type) {
        case OBJ_TAG:
 -              grab_tag_values(val, deref, obj, buf, sz);
 -              grab_sub_body_contents(val, deref, obj, buf, sz);
 -              grab_person("tagger", val, deref, obj, buf, sz);
 +              grab_tag_values(val, deref, obj);
 +              grab_sub_body_contents(val, deref, buf);
 +              grab_person("tagger", val, deref, buf);
                break;
        case OBJ_COMMIT:
 -              grab_commit_values(val, deref, obj, buf, sz);
 -              grab_sub_body_contents(val, deref, obj, buf, sz);
 -              grab_person("author", val, deref, obj, buf, sz);
 -              grab_person("committer", val, deref, obj, buf, sz);
 +              grab_commit_values(val, deref, obj);
 +              grab_sub_body_contents(val, deref, buf);
 +              grab_person("author", val, deref, buf);
 +              grab_person("committer", val, deref, buf);
                break;
        case OBJ_TREE:
                /* grab_tree_values(val, deref, obj, buf, sz); */
@@@ -1516,7 -1520,7 +1520,7 @@@ static int get_object(struct ref_array_
                        return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"),
                                               oid_to_hex(&oi->oid), ref->refname);
                }
 -              grab_values(ref->value, deref, *obj, oi->content, oi->size);
 +              grab_values(ref->value, deref, *obj, oi->content);
        }
  
        grab_common_values(ref->value, deref, oi);
diff --combined submodule.c
index 174003a847aa9a8c6de666fc24ece9213ccb0921,b251a81f8efa3f0e2ec9fa435dffb70ac023e1bf..21cf50ca15e124edb67b4aa76ae87577816eb1a5
@@@ -432,6 -432,10 +432,10 @@@ void handle_ignore_submodules_arg(struc
                diffopt->flags.ignore_dirty_submodules = 1;
        else if (strcmp(arg, "none"))
                die("bad --ignore-submodules argument: %s", arg);
+       /*
+        * Please update _git_status() in git-completion.bash when you
+        * add new options
+        */
  }
  
  static int prepare_submodule_summary(struct rev_info *rev, const char *path,
@@@ -1609,12 -1613,11 +1613,12 @@@ int fetch_populated_submodules(struct r
  
        calculate_changed_submodule_paths(r, &spf.changed_submodule_names);
        string_list_sort(&spf.changed_submodule_names);
 -      run_processes_parallel(max_parallel_jobs,
 -                             get_next_submodule,
 -                             fetch_start_failure,
 -                             fetch_finish,
 -                             &spf);
 +      run_processes_parallel_tr2(max_parallel_jobs,
 +                                 get_next_submodule,
 +                                 fetch_start_failure,
 +                                 fetch_finish,
 +                                 &spf,
 +                                 "submodule", "parallel/fetch");
  
        argv_array_clear(&spf.args);
  out: