Merge branch 'nd/format-patch-cover-letter-stat-width'
authorJunio C Hamano <gitster@pobox.com>
Mon, 19 Nov 2018 07:24:40 +0000 (16:24 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 19 Nov 2018 07:24:40 +0000 (16:24 +0900)
"git format-patch --stat=<width>" can be used to specify the width
used by the diffstat (shown in the cover letter).

* nd/format-patch-cover-letter-stat-width:
format-patch: respect --stat in cover letter's diffstat

1  2 
builtin/log.c
diff --combined builtin/log.c
index 9f2d9872946dfd7fe527a7f3822d9be23aa5ec83,e0ae6aad4356dcdd1995853498aa43b9f24ab96d..0fe6f9ba1e94ec496f2e083ca964592272d898f2
@@@ -31,9 -31,6 +31,9 @@@
  #include "progress.h"
  #include "commit-slab.h"
  #include "repository.h"
 +#include "commit-reach.h"
 +#include "interdiff.h"
 +#include "range-diff.h"
  
  #define MAIL_DEFAULT_WRAP 72
  
@@@ -107,8 -104,6 +107,8 @@@ static int log_line_range_callback(cons
  {
        struct line_opt_callback_data *data = option->value;
  
 +      BUG_ON_OPT_NEG(unset);
 +
        if (!arg)
                return -1;
  
  
  static void init_log_defaults(void)
  {
 -      init_grep_defaults();
 +      init_grep_defaults(the_repository);
        init_diff_ui_defaults();
  
        decoration_style = auto_decoration_style();
@@@ -472,7 -467,7 +472,7 @@@ int cmd_whatchanged(int argc, const cha
        init_log_defaults();
        git_config(git_log_config, NULL);
  
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        rev.diff = 1;
        rev.simplify_history = 0;
        memset(&opt, 0, sizeof(opt));
@@@ -512,8 -507,7 +512,8 @@@ static int show_blob_object(const struc
                                 &oidc, &obj_context))
                die(_("Not a valid object name %s"), obj_name);
        if (!obj_context.path ||
 -          !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
 +          !textconv_object(the_repository, obj_context.path,
 +                           obj_context.mode, &oidc, 1, &buf, &size)) {
                free(obj_context.path);
                return stream_blob_to_fd(1, oid, NULL, 0);
        }
@@@ -590,7 -584,7 +590,7 @@@ int cmd_show(int argc, const char **arg
        git_config(git_log_config, NULL);
  
        memset(&match_all, 0, sizeof(match_all));
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        rev.diff = 1;
        rev.always_show_header = 1;
        rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
@@@ -670,7 -664,7 +670,7 @@@ int cmd_log_reflog(int argc, const cha
        init_log_defaults();
        git_config(git_log_config, NULL);
  
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        init_reflog_walk(&rev.reflog_info);
        rev.verbose_header = 1;
        memset(&opt, 0, sizeof(opt));
@@@ -709,7 -703,7 +709,7 @@@ int cmd_log(int argc, const char **argv
        init_log_defaults();
        git_config(git_log_config, NULL);
  
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        rev.always_show_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
@@@ -919,10 -913,10 +919,10 @@@ static void get_patch_ids(struct rev_in
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
                die(_("Not a range."));
  
 -      init_patch_ids(ids);
 +      init_patch_ids(the_repository, ids);
  
        /* given a range a..b get all patch ids for b..a */
 -      init_revisions(&check_rev, rev->prefix);
 +      repo_init_revisions(the_repository, &check_rev, rev->prefix);
        check_rev.max_parents = 1;
        o1->flags ^= UNINTERESTING;
        o2->flags ^= UNINTERESTING;
@@@ -998,32 -992,12 +998,30 @@@ static char *find_branch_name(struct re
        tip_oid = &rev->cmdline.rev[positive].item->oid;
        if (dwim_ref(ref, strlen(ref), &branch_oid, &full_ref) &&
            skip_prefix(full_ref, "refs/heads/", &v) &&
 -          !oidcmp(tip_oid, &branch_oid))
 +          oideq(tip_oid, &branch_oid))
                branch = xstrdup(v);
        free(full_ref);
        return branch;
  }
  
-       opts.stat_width = MAIL_DEFAULT_WRAP;
 +static void show_diffstat(struct rev_info *rev,
 +                        struct commit *origin, struct commit *head)
 +{
 +      struct diff_options opts;
 +
 +      memcpy(&opts, &rev->diffopt, sizeof(opts));
 +      opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
 +      diff_setup_done(&opts);
 +
 +      diff_tree_oid(get_commit_tree_oid(origin),
 +                    get_commit_tree_oid(head),
 +                    "", &opts);
 +      diffcore_std(&opts);
 +      diff_flush(&opts);
 +
 +      fprintf(rev->diffopt.file, "\n");
 +}
 +
  static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              struct commit *origin,
                              int nr, struct commit **list,
        struct strbuf sb = STRBUF_INIT;
        int i;
        const char *encoding = "UTF-8";
 -      struct diff_options opts;
        int need_8bit_cte = 0;
        struct pretty_print_context pp = {0};
        struct commit *head = list[0];
  
        shortlog_output(&log);
  
 -      /*
 -       * We can only do diffstat with a unique reference point
 -       */
 -      if (!origin)
 -              return;
 -
 -      memcpy(&opts, &rev->diffopt, sizeof(opts));
 -      opts.output_format = DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
 -      diff_setup_done(&opts);
 +      /* We can only do diffstat with a unique reference point */
 +      if (origin)
 +              show_diffstat(rev, origin, head);
  
 -      diff_tree_oid(get_commit_tree_oid(origin),
 -                    get_commit_tree_oid(head),
 -                    "", &opts);
 -      diffcore_std(&opts);
 -      diff_flush(&opts);
 +      if (rev->idiff_oid1) {
 +              fprintf_ln(rev->diffopt.file, "%s", rev->idiff_title);
 +              show_interdiff(rev, 0);
 +      }
  
 -      fprintf(rev->diffopt.file, "\n");
 +      if (rev->rdiff1) {
 +              fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title);
 +              show_range_diff(rev->rdiff1, rev->rdiff2,
 +                              rev->creation_factor, 1, &rev->diffopt);
 +      }
  }
  
  static const char *clean_message_id(const char *msg_id)
@@@ -1153,8 -1131,6 +1151,8 @@@ static int keep_subject = 0
  
  static int keep_callback(const struct option *opt, const char *arg, int unset)
  {
 +      BUG_ON_OPT_NEG(unset);
 +      BUG_ON_OPT_ARG(arg);
        ((struct rev_info *)opt->value)->total = -1;
        keep_subject = 1;
        return 0;
@@@ -1165,7 -1141,6 +1163,7 @@@ static int subject_prefix = 0
  static int subject_prefix_callback(const struct option *opt, const char *arg,
                            int unset)
  {
 +      BUG_ON_OPT_NEG(unset);
        subject_prefix = 1;
        ((struct rev_info *)opt->value)->subject_prefix = arg;
        return 0;
  
  static int rfc_callback(const struct option *opt, const char *arg, int unset)
  {
 +      BUG_ON_OPT_NEG(unset);
 +      BUG_ON_OPT_ARG(arg);
        return subject_prefix_callback(opt, "RFC PATCH", unset);
  }
  
@@@ -1183,7 -1156,6 +1181,7 @@@ static int numbered_cmdline_opt = 0
  static int numbered_callback(const struct option *opt, const char *arg,
                             int unset)
  {
 +      BUG_ON_OPT_ARG(arg);
        *(int *)opt->value = numbered_cmdline_opt = unset ? 0 : 1;
        if (unset)
                auto_number =  0;
  static int no_numbered_callback(const struct option *opt, const char *arg,
                                int unset)
  {
 +      BUG_ON_OPT_NEG(unset);
        return numbered_callback(opt, arg, 1);
  }
  
@@@ -1201,7 -1172,6 +1199,7 @@@ static int output_directory_callback(co
                              int unset)
  {
        const char **dir = (const char **)opt->value;
 +      BUG_ON_OPT_NEG(unset);
        if (*dir)
                die(_("Two output directories?"));
        *dir = arg;
@@@ -1388,13 -1358,13 +1386,13 @@@ static void prepare_bases(struct base_t
                return;
  
        init_commit_base(&commit_base);
 -      diff_setup(&diffopt);
 +      repo_diff_setup(the_repository, &diffopt);
        diffopt.flags.recursive = 1;
        diff_setup_done(&diffopt);
  
        oidcpy(&bases->base_commit, &base->object.oid);
  
 -      init_revisions(&revs, NULL);
 +      repo_init_revisions(the_repository, &revs, NULL);
        revs.max_parents = 1;
        revs.topo_order = 1;
        for (i = 0; i < total; i++) {
@@@ -1447,36 -1417,6 +1445,36 @@@ static void print_bases(struct base_tre
        oidclr(&bases->base_commit);
  }
  
 +static const char *diff_title(struct strbuf *sb, int reroll_count,
 +                     const char *generic, const char *rerolled)
 +{
 +      if (reroll_count <= 0)
 +              strbuf_addstr(sb, generic);
 +      else /* RFC may be v0, so allow -v1 to diff against v0 */
 +              strbuf_addf(sb, rerolled, reroll_count - 1);
 +      return sb->buf;
 +}
 +
 +static void infer_range_diff_ranges(struct strbuf *r1,
 +                                  struct strbuf *r2,
 +                                  const char *prev,
 +                                  struct commit *origin,
 +                                  struct commit *head)
 +{
 +      const char *head_oid = oid_to_hex(&head->object.oid);
 +
 +      if (!strstr(prev, "..")) {
 +              strbuf_addf(r1, "%s..%s", head_oid, prev);
 +              strbuf_addf(r2, "%s..%s", prev, head_oid);
 +      } else if (!origin) {
 +              die(_("failed to infer range-diff ranges"));
 +      } else {
 +              strbuf_addstr(r1, prev);
 +              strbuf_addf(r2, "%s..%s",
 +                          oid_to_hex(&origin->object.oid), head_oid);
 +      }
 +}
 +
  int cmd_format_patch(int argc, const char **argv, const char *prefix)
  {
        struct commit *commit;
        struct base_tree_info bases;
        int show_progress = 0;
        struct progress *progress = NULL;
 +      struct oid_array idiff_prev = OID_ARRAY_INIT;
 +      struct strbuf idiff_title = STRBUF_INIT;
 +      const char *rdiff_prev = NULL;
 +      struct strbuf rdiff1 = STRBUF_INIT;
 +      struct strbuf rdiff2 = STRBUF_INIT;
 +      struct strbuf rdiff_title = STRBUF_INIT;
 +      int creation_factor = -1;
  
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            PARSE_OPT_NOARG, numbered_callback },
                { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
                            N_("use [PATCH] even with multiple patches"),
 -                          PARSE_OPT_NOARG, no_numbered_callback },
 +                          PARSE_OPT_NOARG | PARSE_OPT_NONEG, no_numbered_callback },
                OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
                OPT_BOOL(0, "stdout", &use_stdout,
                            N_("print patches to standard out")),
                OPT__QUIET(&quiet, N_("don't print the patch filenames")),
                OPT_BOOL(0, "progress", &show_progress,
                         N_("show progress while generating patches")),
 +              OPT_CALLBACK(0, "interdiff", &idiff_prev, N_("rev"),
 +                           N_("show changes against <rev> in cover letter or single patch"),
 +                           parse_opt_object_name),
 +              OPT_STRING(0, "range-diff", &rdiff_prev, N_("refspec"),
 +                         N_("show changes against <refspec> in cover letter or single patch")),
 +              OPT_INTEGER(0, "creation-factor", &creation_factor,
 +                          N_("percentage by which creation is weighted")),
                OPT_END()
        };
  
        extra_cc.strdup_strings = 1;
        init_log_defaults();
        git_config(git_format_config, NULL);
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.expand_tabs_in_log_default = 0;
        rev.verbose_header = 1;
                /* Don't say anything if head and upstream are the same. */
                if (rev.pending.nr == 2) {
                        struct object_array_entry *o = rev.pending.objects;
 -                      if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0)
 -                              return 0;
 +                      if (oideq(&o[0].item->oid, &o[1].item->oid))
 +                              goto done;
                }
                get_patch_ids(&rev, &ids);
        }
        }
        if (nr == 0)
                /* nothing to do */
 -              return 0;
 +              goto done;
        total = nr;
        if (cover_letter == -1) {
                if (config_cover_letter == COVER_AUTO)
        if (numbered)
                rev.total = total + start_number - 1;
  
 +      if (idiff_prev.nr) {
 +              if (!cover_letter && total != 1)
 +                      die(_("--interdiff requires --cover-letter or single patch"));
 +              rev.idiff_oid1 = &idiff_prev.oid[idiff_prev.nr - 1];
 +              rev.idiff_oid2 = get_commit_tree_oid(list[0]);
 +              rev.idiff_title = diff_title(&idiff_title, reroll_count,
 +                                           _("Interdiff:"),
 +                                           _("Interdiff against v%d:"));
 +      }
 +
 +      if (creation_factor < 0)
 +              creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
 +      else if (!rdiff_prev)
 +              die(_("--creation-factor requires --range-diff"));
 +
 +      if (rdiff_prev) {
 +              if (!cover_letter && total != 1)
 +                      die(_("--range-diff requires --cover-letter or single patch"));
 +
 +              infer_range_diff_ranges(&rdiff1, &rdiff2, rdiff_prev,
 +                                      origin, list[0]);
 +              rev.rdiff1 = rdiff1.buf;
 +              rev.rdiff2 = rdiff2.buf;
 +              rev.creation_factor = creation_factor;
 +              rev.rdiff_title = diff_title(&rdiff_title, reroll_count,
 +                                           _("Range-diff:"),
 +                                           _("Range-diff against v%d:"));
 +      }
 +
        if (!signature) {
                ; /* --no-signature inhibits all signatures */
        } else if (signature && signature != git_version_string) {
                print_signature(rev.diffopt.file);
                total++;
                start_number--;
 +              /* interdiff/range-diff in cover-letter; omit from patches */
 +              rev.idiff_oid1 = NULL;
 +              rev.rdiff1 = NULL;
        }
        rev.add_signoff = do_signoff;
  
        string_list_clear(&extra_hdr, 0);
        if (ignore_if_in_upstream)
                free_patch_ids(&ids);
 +
 +done:
 +      oid_array_clear(&idiff_prev);
 +      strbuf_release(&idiff_title);
 +      strbuf_release(&rdiff1);
 +      strbuf_release(&rdiff2);
 +      strbuf_release(&rdiff_title);
        return 0;
  }
  
@@@ -2049,7 -1936,7 +2047,7 @@@ int cmd_cherry(int argc, const char **a
                }
        }
  
 -      init_revisions(&revs, prefix);
 +      repo_init_revisions(the_repository, &revs, prefix);
        revs.max_parents = 1;
  
        if (add_pending_commit(head, &revs, 0))
        /* Don't say anything if head and upstream are the same. */
        if (revs.pending.nr == 2) {
                struct object_array_entry *o = revs.pending.objects;
 -              if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0)
 +              if (oideq(&o[0].item->oid, &o[1].item->oid))
                        return 0;
        }