Merge branch 'gb/1.7.0-diff-whitespace-only-output'
authorJunio C Hamano <gitster@pobox.com>
Sat, 26 Dec 2009 22:03:18 +0000 (14:03 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 26 Dec 2009 22:03:18 +0000 (14:03 -0800)
* gb/1.7.0-diff-whitespace-only-output:
No diff -b/-w output for all-whitespace changes

1  2 
diff.c
diff --combined diff.c
index 57aa72c6c575d1014cbb716d3993f600bdc1f2c5,108de236114ab98bf4eb03680d66b22e604cba32..d14a575e8e2670247bf8c296e28cf0146e3e03bb
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -39,7 -39,6 +39,7 @@@ static char diff_colors[][COLOR_MAXLEN
        GIT_COLOR_GREEN,        /* NEW */
        GIT_COLOR_YELLOW,       /* COMMIT */
        GIT_COLOR_BG_RED,       /* WHITESPACE */
 +      GIT_COLOR_NORMAL,       /* FUNCINFO */
  };
  
  static void diff_filespec_load_driver(struct diff_filespec *one);
@@@ -61,9 -60,7 +61,9 @@@ static int parse_diff_color_slot(const 
                return DIFF_COMMIT;
        if (!strcasecmp(var+ofs, "whitespace"))
                return DIFF_WHITESPACE;
 -      die("bad config variable '%s'", var);
 +      if (!strcasecmp(var+ofs, "func"))
 +              return DIFF_FUNCINFO;
 +      return -1;
  }
  
  static int git_config_rename(const char *var, const char *value)
@@@ -122,8 -119,6 +122,8 @@@ int git_diff_basic_config(const char *v
  
        if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
 +              if (slot < 0)
 +                      return 0;
                if (!value)
                        return config_error_nonbool(var);
                color_parse(value, var, diff_colors[slot]);
@@@ -194,6 -189,7 +194,7 @@@ struct emit_callback 
        struct diff_words_data *diff_words;
        int *found_changesp;
        FILE *file;
+       struct strbuf *header;
  };
  
  static int count_lines(const char *data, int size)
@@@ -300,13 -296,12 +301,13 @@@ static void emit_line_0(FILE *file, con
                nofirst = 0;
        }
  
 -      fputs(set, file);
 -
 -      if (!nofirst)
 -              fputc(first, file);
 -      fwrite(line, len, 1, file);
 -      fputs(reset, file);
 +      if (len || !nofirst) {
 +              fputs(set, file);
 +              if (!nofirst)
 +                      fputc(first, file);
 +              fwrite(line, len, 1, file);
 +              fputs(reset, file);
 +      }
        if (has_trailing_carriage_return)
                fputc('\r', file);
        if (has_trailing_newline)
@@@ -350,42 -345,6 +351,42 @@@ static void emit_add_line(const char *r
        }
  }
  
 +static void emit_hunk_header(struct emit_callback *ecbdata,
 +                           const char *line, int len)
 +{
 +      const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 +      const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
 +      const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
 +      const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
 +      static const char atat[2] = { '@', '@' };
 +      const char *cp, *ep;
 +
 +      /*
 +       * As a hunk header must begin with "@@ -<old>, +<new> @@",
 +       * it always is at least 10 bytes long.
 +       */
 +      if (len < 10 ||
 +          memcmp(line, atat, 2) ||
 +          !(ep = memmem(line + 2, len - 2, atat, 2))) {
 +              emit_line(ecbdata->file, plain, reset, line, len);
 +              return;
 +      }
 +      ep += 2; /* skip over @@ */
 +
 +      /* The hunk header in fraginfo color */
 +      emit_line(ecbdata->file, frag, reset, line, ep - line);
 +
 +      /* blank before the func header */
 +      for (cp = ep; ep - line < len; ep++)
 +              if (*ep != ' ' && *ep != '\t')
 +                      break;
 +      if (ep != cp)
 +              emit_line(ecbdata->file, plain, reset, cp, ep - cp);
 +
 +      if (ep < line + len)
 +              emit_line(ecbdata->file, func, reset, ep, line + len - ep);
 +}
 +
  static struct diff_tempfile *claim_diff_tempfile(void) {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
@@@ -797,6 -756,11 +798,11 @@@ static void fn_out_consume(void *priv, 
        const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
  
+       if (ecbdata->header) {
+               fprintf(ecbdata->file, "%s", ecbdata->header->buf);
+               strbuf_reset(ecbdata->header);
+               ecbdata->header = NULL;
+       }
        *(ecbdata->found_changesp) = 1;
  
        if (ecbdata->label_path[0]) {
                        diff_words_flush(ecbdata);
                len = sane_truncate_line(ecbdata, line, len);
                find_lno(line, ecbdata);
 -              emit_line(ecbdata->file,
 -                        diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
 -                        reset, line, len);
 +              emit_hunk_header(ecbdata, line, len);
                if (line[len-1] != '\n')
                        putc('\n', ecbdata->file);
                return;
@@@ -1601,6 -1567,7 +1607,7 @@@ static void builtin_diff(const char *na
        const char *reset = diff_get_color_opt(o, DIFF_RESET);
        const char *a_prefix, *b_prefix;
        const char *textconv_one = NULL, *textconv_two = NULL;
+       struct strbuf header = STRBUF_INIT;
  
        if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
                        (!one->mode || S_ISGITLINK(one->mode)) &&
        b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
-       fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
+       strbuf_addf(&header, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
        if (lbl[0][0] == '/') {
                /* /dev/null */
-               fprintf(o->file, "%snew file mode %06o%s\n", set, two->mode, reset);
+               strbuf_addf(&header, "%snew file mode %06o%s\n", set, two->mode, reset);
                if (xfrm_msg && xfrm_msg[0])
-                       fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
+                       strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
        }
        else if (lbl[1][0] == '/') {
-               fprintf(o->file, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
+               strbuf_addf(&header, "%sdeleted file mode %06o%s\n", set, one->mode, reset);
                if (xfrm_msg && xfrm_msg[0])
-                       fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
+                       strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
        }
        else {
                if (one->mode != two->mode) {
-                       fprintf(o->file, "%sold mode %06o%s\n", set, one->mode, reset);
-                       fprintf(o->file, "%snew mode %06o%s\n", set, two->mode, reset);
+                       strbuf_addf(&header, "%sold mode %06o%s\n", set, one->mode, reset);
+                       strbuf_addf(&header, "%snew mode %06o%s\n", set, two->mode, reset);
                }
                if (xfrm_msg && xfrm_msg[0])
-                       fprintf(o->file, "%s%s%s\n", set, xfrm_msg, reset);
+                       strbuf_addf(&header, "%s%s%s\n", set, xfrm_msg, reset);
                /*
                 * we do not run diff between different kind
                 * of objects.
                if (complete_rewrite &&
                    (textconv_one || !diff_filespec_is_binary(one)) &&
                    (textconv_two || !diff_filespec_is_binary(two))) {
+                       fprintf(o->file, "%s", header.buf);
+                       strbuf_reset(&header);
                        emit_rewrite_diff(name_a, name_b, one, two,
                                                textconv_one, textconv_two, o);
                        o->found_changes = 1;
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
                        goto free_ab_and_return;
+               fprintf(o->file, "%s", header.buf);
+               strbuf_reset(&header);
                if (DIFF_OPT_TST(o, BINARY))
                        emit_binary_diff(o->file, &mf1, &mf2);
                else
                struct emit_callback ecbdata;
                const struct userdiff_funcname *pe;
  
+               if (!DIFF_XDL_TST(o, WHITESPACE_FLAGS)) {
+                       fprintf(o->file, "%s", header.buf);
+                       strbuf_reset(&header);
+               }
                if (textconv_one) {
                        size_t size;
                        mf1.ptr = run_textconv(textconv_one, one, &size);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
                ecbdata.file = o->file;
+               ecbdata.header = header.len ? &header : NULL;
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.interhunkctxlen = o->interhunkcontext;
        }
  
   free_ab_and_return:
+       strbuf_release(&header);
        diff_free_filespec_data(one);
        diff_free_filespec_data(two);
        free(a_one);
@@@ -2551,20 -2530,6 +2570,20 @@@ int diff_setup_done(struct diff_option
        if (count > 1)
                die("--name-only, --name-status, --check and -s are mutually exclusive");
  
 +      /*
 +       * Most of the time we can say "there are changes"
 +       * only by checking if there are changed paths, but
 +       * --ignore-whitespace* options force us to look
 +       * inside contents.
 +       */
 +
 +      if (DIFF_XDL_TST(options, IGNORE_WHITESPACE) ||
 +          DIFF_XDL_TST(options, IGNORE_WHITESPACE_CHANGE) ||
 +          DIFF_XDL_TST(options, IGNORE_WHITESPACE_AT_EOL))
 +              DIFF_OPT_SET(options, DIFF_FROM_CONTENTS);
 +      else
 +              DIFF_OPT_CLR(options, DIFF_FROM_CONTENTS);
 +
        if (DIFF_OPT_TST(options, FIND_COPIES_HARDER))
                options->detect_rename = DIFF_DETECT_COPY;
  
         * to have found.  It does not make sense not to return with
         * exit code in such a case either.
         */
 -      if (DIFF_OPT_TST(options, QUIET)) {
 +      if (DIFF_OPT_TST(options, QUICK)) {
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
@@@ -2816,7 -2781,7 +2835,7 @@@ int diff_opt_parse(struct diff_options 
        else if (!strcmp(arg, "--exit-code"))
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        else if (!strcmp(arg, "--quiet"))
 -              DIFF_OPT_SET(options, QUIET);
 +              DIFF_OPT_SET(options, QUICK);
        else if (!strcmp(arg, "--ext-diff"))
                DIFF_OPT_SET(options, ALLOW_EXTERNAL);
        else if (!strcmp(arg, "--no-ext-diff"))
@@@ -3523,18 -3488,6 +3542,18 @@@ free_queue
        q->nr = q->alloc = 0;
        if (options->close_file)
                fclose(options->file);
 +
 +      /*
 +       * Report the content-level differences with HAS_CHANGES;
 +       * diff_addremove/diff_change does not set the bit when
 +       * DIFF_FROM_CONTENTS is in effect (e.g. with -w).
 +       */
 +      if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
 +              if (options->found_changes)
 +                      DIFF_OPT_SET(options, HAS_CHANGES);
 +              else
 +                      DIFF_OPT_CLR(options, HAS_CHANGES);
 +      }
  }
  
  static void diffcore_apply_filter(const char *filter)
@@@ -3671,7 -3624,7 +3690,7 @@@ void diffcore_std(struct diff_options *
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
  
 -      if (diff_queued_diff.nr)
 +      if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
                DIFF_OPT_SET(options, HAS_CHANGES);
        else
                DIFF_OPT_CLR(options, HAS_CHANGES);
@@@ -3731,8 -3684,7 +3750,8 @@@ void diff_addremove(struct diff_option
                fill_filespec(two, sha1, mode);
  
        diff_queue(&diff_queued_diff, one, two);
 -      DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
 +              DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
  void diff_change(struct diff_options *options,
        fill_filespec(two, new_sha1, new_mode);
  
        diff_queue(&diff_queued_diff, one, two);
 -      DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
 +              DIFF_OPT_SET(options, HAS_CHANGES);
  }
  
  void diff_unmerge(struct diff_options *options,