Merge branch 'jt/format-patch-from-config'
authorJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:18 +0000 (12:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:18 +0000 (12:33 -0700)
"git format-patch" learned format.from configuration variable to
specify the default settings for its "--from" option.

* jt/format-patch-from-config:
format-patch: format.from gives the default for --from

1  2 
Documentation/config.txt
builtin/log.c
contrib/completion/git-completion.bash
t/t4014-format-patch.sh
diff --combined Documentation/config.txt
index bc1c433c4e0584a095f79196997426780521b377,cdc99dca26ceddff8818154f68afa3440282cbce..0bcb6790d6efbb2a812bc90b6f64e323cb3ec393
@@@ -150,34 -150,27 +150,34 @@@ integer:
         1024", "by 1024x1024", etc.
  
  color::
 -       The value for a variables that takes a color is a list of
 -       colors (at most two) and attributes (at most one), separated
 -       by spaces.  The colors accepted are `normal`, `black`,
 -       `red`, `green`, `yellow`, `blue`, `magenta`, `cyan` and
 -       `white`; the attributes are `bold`, `dim`, `ul`, `blink` and
 -       `reverse`.  The first color given is the foreground; the
 -       second is the background.  The position of the attribute, if
 -       any, doesn't matter. Attributes may be turned off specifically
 -       by prefixing them with `no` (e.g., `noreverse`, `noul`, etc).
 -+
 -Colors (foreground and background) may also be given as numbers between
 -0 and 255; these use ANSI 256-color mode (but note that not all
 -terminals may support this).  If your terminal supports it, you may also
 -specify 24-bit RGB values as hex, like `#ff0ab3`.
 -+
 -The attributes are meant to be reset at the beginning of each item
 -in the colored output, so setting color.decorate.branch to `black`
 -will paint that branch name in a plain `black`, even if the previous
 -thing on the same output line (e.g. opening parenthesis before the
 -list of branch names in `log --decorate` output) is set to be
 -painted with `bold` or some other attribute.
 +       The value for a variable that takes a color is a list of
 +       colors (at most two, one for foreground and one for background)
 +       and attributes (as many as you want), separated by spaces.
 ++
 +The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
 +`blue`, `magenta`, `cyan` and `white`.  The first color given is the
 +foreground; the second is the background.
 ++
 +Colors may also be given as numbers between 0 and 255; these use ANSI
 +256-color mode (but note that not all terminals may support this).  If
 +your terminal supports it, you may also specify 24-bit RGB values as
 +hex, like `#ff0ab3`.
 ++
 +The accepted attributes are `bold`, `dim`, `ul`, `blink`, `reverse`,
 +`italic`, and `strike` (for crossed-out or "strikethrough" letters).
 +The position of any attributes with respect to the colors
 +(before, after, or in between), doesn't matter. Specific attributes may
 +be turned off by prefixing them with `no` or `no-` (e.g., `noreverse`,
 +`no-ul`, etc).
 ++
 +For git's pre-defined color slots, the attributes are meant to be reset
 +at the beginning of each item in the colored output. So setting
 +`color.decorate.branch` to `black` will paint that branch name in a
 +plain `black`, even if the previous thing on the same output line (e.g.
 +opening parenthesis before the list of branch names in `log --decorate`
 +output) is set to be painted with `bold` or some other attribute.
 +However, custom log formats may do more complicated and layered
 +coloring, and the negated forms may be useful there.
  
  pathname::
        A variable that takes a pathname value can be given a
@@@ -412,11 -405,13 +412,11 @@@ file with mixed line endings would be r
  mechanism.
  
  core.autocrlf::
 -      Setting this variable to "true" is almost the same as setting
 -      the `text` attribute to "auto" on all files except that text
 -      files are not guaranteed to be normalized: files that contain
 -      `CRLF` in the repository will not be touched.  Use this
 -      setting if you want to have `CRLF` line endings in your
 -      working directory even though the repository does not have
 -      normalized line endings.  This variable can be set to 'input',
 +      Setting this variable to "true" is the same as setting
 +      the `text` attribute to "auto" on all files and core.eol to "crlf".
 +      Set to true if you want to have `CRLF` line endings in your
 +      working directory and the repository has LF line endings.
 +      This variable can be set to 'input',
        in which case no output conversion is performed.
  
  core.symlinks::
@@@ -448,13 -443,6 +448,13 @@@ specify that no proxy be used for a giv
  This is useful for excluding servers inside a firewall from
  proxy use, while defaulting to a common proxy for external domains.
  
 +core.sshCommand::
 +      If this variable is set, `git fetch` and `git push` will
 +      use the specified command instead of `ssh` when they need to
 +      connect to a remote system. The command is in the same form as
 +      the `GIT_SSH_COMMAND` environment variable and is overridden
 +      when the environment variable is set.
 +
  core.ignoreStat::
        If true, Git will avoid using lstat() calls to detect if files have
        changed by setting the "assume-unchanged" bit for those tracked files
@@@ -1201,15 -1189,6 +1201,15 @@@ difftool.<tool>.cmd:
  difftool.prompt::
        Prompt before each invocation of the diff tool.
  
 +fastimport.unpackLimit::
 +      If the number of objects imported by linkgit:git-fast-import[1]
 +      is below this limit, then the objects will be unpacked into
 +      loose object files.  However if the number of imported objects
 +      equals or exceeds this limit then the pack will be stored as a
 +      pack.  Storing the pack from a fast-import can make the import
 +      operation complete faster, especially on slow filesystems.  If
 +      not set, the value of `transfer.unpackLimit` is used instead.
 +
  fetch.recurseSubmodules::
        This option can be either set to a boolean value or to 'on-demand'.
        Setting it to a boolean changes the behavior of fetch and pull to
@@@ -1241,11 -1220,6 +1241,11 @@@ fetch.prune:
        If true, fetch will automatically behave as if the `--prune`
        option was given on the command line.  See also `remote.<name>.prune`.
  
 +fetch.output::
 +      Control how ref update status is printed. Valid values are
 +      `full` and `compact`. Default value is `full`. See section
 +      OUTPUT in linkgit:git-fetch[1] for detail.
 +
  format.attach::
        Enable multipart/mixed attachments as the default for
        'format-patch'.  The value can also be a double quoted string
        value as the boundary.  See the --attach option in
        linkgit:git-format-patch[1].
  
+ format.from::
+       Provides the default value for the `--from` option to format-patch.
+       Accepts a boolean value, or a name and email address.  If false,
+       format-patch defaults to `--no-from`, using commit authors directly in
+       the "From:" field of patch mails.  If true, format-patch defaults to
+       `--from`, using your committer identity in the "From:" field of patch
+       mails and including a "From:" field in the body of the patch mail if
+       different.  If set to a non-boolean value, format-patch uses that
+       value instead of your committer identity.  Defaults to false.
  format.numbered::
        A boolean which can enable or disable sequence numbers in patch
        subjects.  It defaults to "auto" which enables it only if there
@@@ -2427,13 -2411,8 +2437,13 @@@ rebase.instructionForma
  
  receive.advertiseAtomic::
        By default, git-receive-pack will advertise the atomic push
 -      capability to its clients. If you don't want to this capability
 -      to be advertised, set this variable to false.
 +      capability to its clients. If you don't want to advertise this
 +      capability, set this variable to false.
 +
 +receive.advertisePushOptions::
 +      By default, git-receive-pack will advertise the push options
 +      capability to its clients. If you don't want to advertise this
 +      capability, set this variable to false.
  
  receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
@@@ -2488,15 -2467,6 +2498,15 @@@ receive.fsck.skipList:
        can be safely ignored such as invalid committer email addresses.
        Note: corrupt objects cannot be skipped with this setting.
  
 +receive.keepAlive::
 +      After receiving the pack from the client, `receive-pack` may
 +      produce no output (if `--quiet` was specified) while processing
 +      the pack, causing some networks to drop the TCP connection.
 +      With this option set, if `receive-pack` does not transmit
 +      any data in this phase for `receive.keepAlive` seconds, it will
 +      send a short keepalive packet.  The default is 5 seconds; set
 +      to 0 to disable keepalives entirely.
 +
  receive.unpackLimit::
        If the number of objects received in a push is below this
        limit then the objects will be unpacked into loose object
@@@ -2923,21 -2893,6 +2933,21 @@@ uploadpack.keepAlive:
        `uploadpack.keepAlive` seconds. Setting this option to 0
        disables keepalive packets entirely. The default is 5 seconds.
  
 +uploadpack.packObjectsHook::
 +      If this option is set, when `upload-pack` would run
 +      `git pack-objects` to create a packfile for a client, it will
 +      run this shell command instead.  The `pack-objects` command and
 +      arguments it _would_ have run (including the `git pack-objects`
 +      at the beginning) are appended to the shell command. The stdin
 +      and stdout of the hook are treated as if `pack-objects` itself
 +      was run. I.e., `upload-pack` will feed input intended for
 +      `pack-objects` to the hook, and expects a completed packfile on
 +      stdout.
 ++
 +Note that this configuration variable is ignored if it is seen in the
 +repository-level config (this is a safety measure against fetching from
 +untrusted repositories).
 +
  url.<base>.insteadOf::
        Any URL that starts with this value will be rewritten to
        start, instead, with <base>. In cases where some site serves a
diff --combined builtin/log.c
index fd1652f52b32fc8bfb7bdc6e0191affafaa21c9c,13ebe7a757d800b2d2f3bcf0e7c0fec5b5577b1b..1f116bea8c8873d652f573db2047c558fa51bbd3
@@@ -33,7 -33,6 +33,7 @@@ static const char *default_date_mode = 
  static int default_abbrev_commit;
  static int default_show_root = 1;
  static int default_follow;
 +static int default_show_signature;
  static int decoration_style;
  static int decoration_given;
  static int use_mailmap_config;
@@@ -120,7 -119,6 +120,7 @@@ static void cmd_log_init_defaults(struc
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
 +      rev->show_signature = default_show_signature;
        DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
  
        if (default_date_mode)
@@@ -238,17 -236,16 +238,17 @@@ static void show_early_header(struct re
                if (rev->commit_format != CMIT_FMT_ONELINE)
                        putchar(rev->diffopt.line_termination);
        }
 -      printf(_("Final output: %d %s\n"), nr, stage);
 +      fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage);
  }
  
  static struct itimerval early_output_timer;
  
  static void log_show_early(struct rev_info *revs, struct commit_list *list)
  {
 -      int i = revs->early_output;
 +      int i = revs->early_output, close_file = revs->diffopt.close_file;
        int show_header = 1;
  
 +      revs->diffopt.close_file = 0;
        sort_in_topological_order(&list, revs->sort_order);
        while (list && i) {
                struct commit *commit = list->item;
                case commit_ignore:
                        break;
                case commit_error:
 +                      if (close_file)
 +                              fclose(revs->diffopt.file);
                        return;
                }
                list = list->next;
        }
  
        /* Did we already get enough commits for the early output? */
 -      if (!i)
 +      if (!i) {
 +              if (close_file)
 +                      fclose(revs->diffopt.file);
                return;
 +      }
  
        /*
         * ..if no, then repeat it twice a second until we
@@@ -339,7 -331,7 +339,7 @@@ static int cmd_log_walk(struct rev_inf
  {
        struct commit *commit;
        int saved_nrl = 0;
 -      int saved_dcctc = 0;
 +      int saved_dcctc = 0, close_file = rev->diffopt.close_file;
  
        if (rev->early_output)
                setup_early_output(rev);
         * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
         * retain that state information if replacing rev->diffopt in this loop
         */
 +      rev->diffopt.close_file = 0;
        while ((commit = get_revision(rev)) != NULL) {
                if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
                        /*
        }
        rev->diffopt.degraded_cc_to_c = saved_dcctc;
        rev->diffopt.needed_rename_limit = saved_nrl;
 +      if (close_file)
 +              fclose(rev->diffopt.file);
  
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
            DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
@@@ -420,10 -409,6 +420,10 @@@ static int git_log_config(const char *v
                use_mailmap_config = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "log.showsignature")) {
 +              default_show_signature = git_config_bool(var, value);
 +              return 0;
 +      }
  
        if (grep_config(var, value, cb) < 0)
                return -1;
@@@ -460,7 -445,7 +460,7 @@@ static void show_tagger(char *buf, int 
        pp.fmt = rev->commit_format;
        pp.date_mode = rev->date_mode;
        pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
 -      printf("%s", out.buf);
 +      fprintf(rev->diffopt.file, "%s", out.buf);
        strbuf_release(&out);
  }
  
@@@ -471,7 -456,7 +471,7 @@@ static int show_blob_object(const unsig
        char *buf;
        unsigned long size;
  
 -      fflush(stdout);
 +      fflush(rev->diffopt.file);
        if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
            !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
                return stream_blob_to_fd(1, sha1, NULL, 0);
@@@ -511,7 -496,7 +511,7 @@@ static int show_tag_object(const unsign
        }
  
        if (offset < size)
 -              fwrite(buf + offset, size - offset, 1, stdout);
 +              fwrite(buf + offset, size - offset, 1, rev->diffopt.file);
        free(buf);
        return 0;
  }
@@@ -520,8 -505,7 +520,8 @@@ static int show_tree_object(const unsig
                struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
  {
 -      printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
 +      FILE *file = context;
 +      fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
        return 0;
  }
  
@@@ -581,7 -565,7 +581,7 @@@ int cmd_show(int argc, const char **arg
  
                        if (rev.shown_one)
                                putchar('\n');
 -                      printf("%stag %s%s\n",
 +                      fprintf(rev.diffopt.file, "%stag %s%s\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                case OBJ_TREE:
                        if (rev.shown_one)
                                putchar('\n');
 -                      printf("%stree %s%s\n\n",
 +                      fprintf(rev.diffopt.file, "%stree %s%s\n\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        name,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                        read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
 -                                      show_tree_object, NULL);
 +                                      show_tree_object, rev.diffopt.file);
                        rev.shown_one = 1;
                        break;
                case OBJ_COMMIT:
@@@ -690,9 -674,9 +690,9 @@@ static int auto_number = 1
  
  static char *default_attach = NULL;
  
 -static struct string_list extra_hdr;
 -static struct string_list extra_to;
 -static struct string_list extra_cc;
 +static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_to = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
  
  static void add_header(const char *value)
  {
  static int thread;
  static int do_signoff;
  static int base_auto;
+ static char *from;
  static const char *signature = git_version_string;
  static const char *signature_file;
  static int config_cover_letter;
@@@ -807,14 -792,26 +808,25 @@@ static int git_format_config(const cha
                base_auto = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "format.from")) {
+               int b = git_config_maybe_bool(var, value);
+               free(from);
+               if (b < 0)
+                       from = xstrdup(value);
+               else if (b)
+                       from = xstrdup(git_committer_info(IDENT_NO_DATE));
+               else
+                       from = NULL;
+               return 0;
+       }
  
        return git_log_config(var, value, cb);
  }
  
 -static FILE *realstdout = NULL;
  static const char *output_directory = NULL;
  static int outdir_offset;
  
 -static int reopen_stdout(struct commit *commit, const char *subject,
 +static int open_next_file(struct commit *commit, const char *subject,
                         struct rev_info *rev, int quiet)
  {
        struct strbuf filename = STRBUF_INIT;
                fmt_output_subject(&filename, subject, rev);
  
        if (!quiet)
 -              fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
 +              printf("%s\n", filename.buf + outdir_offset);
  
 -      if (freopen(filename.buf, "w", stdout) == NULL)
 +      if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL)
                return error(_("Cannot open patch file %s"), filename.buf);
  
        strbuf_release(&filename);
@@@ -897,15 -894,15 +909,15 @@@ static void gen_message_id(struct rev_i
        info->message_id = strbuf_detach(&buf, NULL);
  }
  
 -static void print_signature(void)
 +static void print_signature(FILE *file)
  {
        if (!signature || !*signature)
                return;
  
 -      printf("-- \n%s", signature);
 +      fprintf(file, "-- \n%s", signature);
        if (signature[strlen(signature)-1] != '\n')
 -              putchar('\n');
 -      putchar('\n');
 +              putc('\n', file);
 +      putc('\n', file);
  }
  
  static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@@ -968,13 -965,13 +980,13 @@@ static void make_cover_letter(struct re
        struct pretty_print_context pp = {0};
        struct commit *head = list[0];
  
 -      if (rev->commit_format != CMIT_FMT_EMAIL)
 +      if (!cmit_fmt_is_mail(rev->commit_format))
                die(_("Cover letter needs email format"));
  
        committer = git_committer_info(0);
  
        if (!use_stdout &&
 -          reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
 +          open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
                return;
  
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
        pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
        pp_remainder(&pp, &msg, &sb, 0);
        add_branch_description(&sb, branch_name);
 -      printf("%s\n", sb.buf);
 +      fprintf(rev->diffopt.file, "%s\n", sb.buf);
  
        strbuf_release(&sb);
  
        log.wrap = 72;
        log.in1 = 2;
        log.in2 = 4;
 +      log.file = rev->diffopt.file;
        for (i = 0; i < nr; i++)
                shortlog_add_commit(&log, list[i]);
  
        diffcore_std(&opts);
        diff_flush(&opts);
  
 -      printf("\n");
 -      print_signature();
 +      fprintf(rev->diffopt.file, "\n");
 +      print_signature(rev->diffopt.file);
  }
  
  static const char *clean_message_id(const char *msg_id)
@@@ -1340,7 -1336,7 +1352,7 @@@ static void prepare_bases(struct base_t
        }
  }
  
 -static void print_bases(struct base_tree_info *bases)
 +static void print_bases(struct base_tree_info *bases, FILE *file)
  {
        int i;
  
                return;
  
        /* Show the base commit */
 -      printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
 +      fprintf(file, "base-commit: %s\n", oid_to_hex(&bases->base_commit));
  
        /* Show the prerequisite patches */
        for (i = bases->nr_patch_id - 1; i >= 0; i--)
 -              printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
 +              fprintf(file, "prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
  
        free(bases->patch_id);
        bases->nr_patch_id = 0;
@@@ -1384,7 -1380,6 +1396,6 @@@ int cmd_format_patch(int argc, const ch
        int quiet = 0;
        int reroll_count = -1;
        char *branch_name = NULL;
-       char *from = NULL;
        char *base_commit = NULL;
        struct base_tree_info bases;
  
                setup_pager();
  
        if (output_directory) {
 +              if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
 +                      rev.diffopt.use_color = GIT_COLOR_NEVER;
                if (use_stdout)
                        die(_("standard output, or directory, which one?"));
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
                get_patch_ids(&rev, &ids);
        }
  
 -      if (!use_stdout)
 -              realstdout = xfdopen(xdup(1), "w");
 -
        if (prepare_revision_walk(&rev))
                die(_("revision walk setup failed"));
        rev.boundary = 1;
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
                                  origin, nr, list, branch_name, quiet);
 -              print_bases(&bases);
 +              print_bases(&bases, rev.diffopt.file);
                total++;
                start_number--;
        }
                }
  
                if (!use_stdout &&
 -                  reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
 +                  open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free_commit_buffer(commit);
                        rev.shown_one = 0;
                if (shown) {
                        if (rev.mime_boundary)
 -                              printf("\n--%s%s--\n\n\n",
 +                              fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n",
                                       mime_boundary_leader,
                                       rev.mime_boundary);
                        else
 -                              print_signature();
 -                      print_bases(&bases);
 +                              print_signature(rev.diffopt.file);
 +                      print_bases(&bases, rev.diffopt.file);
                }
                if (!use_stdout)
 -                      fclose(stdout);
 +                      fclose(rev.diffopt.file);
        }
        free(list);
        free(branch_name);
@@@ -1809,15 -1805,15 +1820,15 @@@ static const char * const cherry_usage[
  };
  
  static void print_commit(char sign, struct commit *commit, int verbose,
 -                       int abbrev)
 +                       int abbrev, FILE *file)
  {
        if (!verbose) {
 -              printf("%c %s\n", sign,
 +              fprintf(file, "%c %s\n", sign,
                       find_unique_abbrev(commit->object.oid.hash, abbrev));
        } else {
                struct strbuf buf = STRBUF_INIT;
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
 -              printf("%c %s %s\n", sign,
 +              fprintf(file, "%c %s %s\n", sign,
                       find_unique_abbrev(commit->object.oid.hash, abbrev),
                       buf.buf);
                strbuf_release(&buf);
@@@ -1898,7 -1894,7 +1909,7 @@@ int cmd_cherry(int argc, const char **a
                commit = list->item;
                if (has_commit_patch_id(commit, &ids))
                        sign = '-';
 -              print_commit(sign, commit, verbose, abbrev);
 +              print_commit(sign, commit, verbose, abbrev, revs.diffopt.file);
                list = list->next;
        }
  
index 6a187bc11bc9b8b141f70f81ae256c27921ff00d,2bd600fff1d51056805ba6851550dc04120e2eed..85bb8b579b92e05cfb20008aea61aac4950e046b
@@@ -803,50 -803,6 +803,50 @@@ __git_find_on_cmdline (
        done
  }
  
 +# Echo the value of an option set on the command line or config
 +#
 +# $1: short option name
 +# $2: long option name including =
 +# $3: list of possible values
 +# $4: config string (optional)
 +#
 +# example:
 +# result="$(__git_get_option_value "-d" "--do-something=" \
 +#     "yes no" "core.doSomething")"
 +#
 +# result is then either empty (no option set) or "yes" or "no"
 +#
 +# __git_get_option_value requires 3 arguments
 +__git_get_option_value ()
 +{
 +      local c short_opt long_opt val
 +      local result= values config_key word
 +
 +      short_opt="$1"
 +      long_opt="$2"
 +      values="$3"
 +      config_key="$4"
 +
 +      ((c = $cword - 1))
 +      while [ $c -ge 0 ]; do
 +              word="${words[c]}"
 +              for val in $values; do
 +                      if [ "$short_opt$val" = "$word" ] ||
 +                         [ "$long_opt$val"  = "$word" ]; then
 +                              result="$val"
 +                              break 2
 +                      fi
 +              done
 +              ((c--))
 +      done
 +
 +      if [ -n "$config_key" ] && [ -z "$result" ]; then
 +              result="$(git --git-dir="$(__gitdir)" config "$config_key")"
 +      fi
 +
 +      echo "$result"
 +}
 +
  __git_has_doubledash ()
  {
        local c=1
@@@ -1136,15 -1092,12 +1136,15 @@@ _git_clone (
                        --depth
                        --single-branch
                        --branch
 +                      --recurse-submodules
                        "
                return
                ;;
        esac
  }
  
 +__git_untracked_file_modes="all no normal"
 +
  _git_commit ()
  {
        case "$prev" in
                return
                ;;
        --untracked-files=*)
 -              __gitcomp "all no normal" "" "${cur##--untracked-files=}"
 +              __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
                return
                ;;
        --*)
@@@ -1827,56 -1780,6 +1827,56 @@@ _git_stage (
        _git_add
  }
  
 +_git_status ()
 +{
 +      local complete_opt
 +      local untracked_state
 +
 +      case "$cur" in
 +      --ignore-submodules=*)
 +              __gitcomp "none untracked dirty all" "" "${cur##--ignore-submodules=}"
 +              return
 +              ;;
 +      --untracked-files=*)
 +              __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
 +              return
 +              ;;
 +      --column=*)
 +              __gitcomp "
 +                      always never auto column row plain dense nodense
 +                      " "" "${cur##--column=}"
 +              return
 +              ;;
 +      --*)
 +              __gitcomp "
 +                      --short --branch --porcelain --long --verbose
 +                      --untracked-files= --ignore-submodules= --ignored
 +                      --column= --no-column
 +                      "
 +              return
 +              ;;
 +      esac
 +
 +      untracked_state="$(__git_get_option_value "-u" "--untracked-files=" \
 +              "$__git_untracked_file_modes" "status.showUntrackedFiles")"
 +
 +      case "$untracked_state" in
 +      no)
 +              # --ignored option does not matter
 +              complete_opt=
 +              ;;
 +      all|normal|*)
 +              complete_opt="--cached --directory --no-empty-directory --others"
 +
 +              if [ -n "$(__git_find_on_cmdline "--ignored")" ]; then
 +                      complete_opt="$complete_opt --ignored --exclude=*"
 +              fi
 +              ;;
 +      esac
 +
 +      __git_complete_index_file "$complete_opt"
 +}
 +
  __git_config_get_set_variables ()
  {
        local prevword word config_file= c=$cword
@@@ -2182,6 -2085,7 +2182,7 @@@ _git_config (
                format.attach
                format.cc
                format.coverLetter
+               format.from
                format.headers
                format.numbered
                format.pretty
@@@ -2692,32 -2596,6 +2693,32 @@@ _git_whatchanged (
        _git_log
  }
  
 +_git_worktree ()
 +{
 +      local subcommands="add list lock prune unlock"
 +      local subcommand="$(__git_find_on_cmdline "$subcommands")"
 +      if [ -z "$subcommand" ]; then
 +              __gitcomp "$subcommands"
 +      else
 +              case "$subcommand,$cur" in
 +              add,--*)
 +                      __gitcomp "--detach"
 +                      ;;
 +              list,--*)
 +                      __gitcomp "--porcelain"
 +                      ;;
 +              lock,--*)
 +                      __gitcomp "--reason"
 +                      ;;
 +              prune,--*)
 +                      __gitcomp "--dry-run --expire --verbose"
 +                      ;;
 +              *)
 +                      ;;
 +              esac
 +      fi
 +}
 +
  __git_main ()
  {
        local i c=1 command __git_dir
diff --combined t/t4014-format-patch.sh
index 1206c48392c36db04ef422813e76b370eeef20b2,8109e46dc746164ebacefa2a04e9582eb0cd2d39..b0579dd45242f64d129d3c10e8ae1c3de6cc5458
@@@ -229,6 -229,46 +229,46 @@@ check_patch () 
        grep -e "^Subject:" "$1"
  }
  
+ test_expect_success 'format.from=false' '
+       git -c format.from=false format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: C O Mitter <committer@example.com>\$" patch
+ '
+ test_expect_success 'format.from=true' '
+       git -c format.from=true format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       grep "^From: C O Mitter <committer@example.com>\$" patch
+ '
+ test_expect_success 'format.from with address' '
+       git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       grep "^From: F R Om <from@example.com>\$" patch
+ '
+ test_expect_success '--no-from overrides format.from' '
+       git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: F R Om <from@example.com>\$" patch
+ '
+ test_expect_success '--from overrides format.from' '
+       git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side |
+       sed -e "/^\$/q" >patch &&
+       check_patch patch &&
+       ! grep "^From: F R Om <from@example.com>\$" patch
+ '
  test_expect_success '--no-to overrides config.to' '
  
        git config --replace-all format.to \
@@@ -1565,45 -1605,4 +1605,45 @@@ test_expect_success 'format-patch --bas
        test_cmp expected actual
  '
  
 +test_expect_success 'format-patch --pretty=mboxrd' '
 +      sp=" " &&
 +      cat >msg <<-INPUT_END &&
 +      mboxrd should escape the body
 +
 +      From could trip up a loose mbox parser
 +      >From extra escape for reversibility
 +      >>From extra escape for reversibility 2
 +      from lower case not escaped
 +      Fromm bad speling not escaped
 +       From with leading space not escaped
 +
 +      F
 +      From
 +      From$sp
 +      From    $sp
 +      From    $sp
 +      INPUT_END
 +
 +      cat >expect <<-INPUT_END &&
 +      >From could trip up a loose mbox parser
 +      >>From extra escape for reversibility
 +      >>>From extra escape for reversibility 2
 +      from lower case not escaped
 +      Fromm bad speling not escaped
 +       From with leading space not escaped
 +
 +      F
 +      From
 +      From
 +      From
 +      From
 +      INPUT_END
 +
 +      C=$(git commit-tree HEAD^^{tree} -p HEAD <msg) &&
 +      git format-patch --pretty=mboxrd --stdout -1 $C~1..$C >patch &&
 +      git grep -h --no-index -A11 \
 +              "^>From could trip up a loose mbox parser" patch >actual &&
 +      test_cmp expect actual
 +'
 +
  test_done