Change xdl_merge to generate output even for null merges
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 6881cf4efaf004e939aef8789bc43526f87d10d7..6fea3c03476ac1817453a61fad0cda93a954df9a 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -189,6 +189,11 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                                        return config_error_nonbool(var);
                                return parse_funcname_pattern(var, ep, value,
                                        0);
+                       } else if (!strcmp(ep, ".xfuncname")) {
+                               if (!value)
+                                       return config_error_nonbool(var);
+                               return parse_funcname_pattern(var, ep, value,
+                                       REG_EXTENDED);
                        }
                }
        }
@@ -1394,20 +1399,23 @@ static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
 }
 
 static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
-       { "java", "!^[  ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
-                       "new\\|return\\|switch\\|throw\\|while\\)\n"
-                       "^[     ]*\\(\\([       ]*"
-                       "[A-Za-z_][A-Za-z_0-9]*\\)\\{2,\\}"
-                       "[      ]*([^;]*\\)$", 0 },
-       { "pascal", "^\\(\\(procedure\\|function\\|constructor\\|"
-                       "destructor\\|interface\\|implementation\\|"
-                       "initialization\\|finalization\\)[ \t]*.*\\)$"
-                       "\\|"
-                       "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$",
-                       0 },
-       { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$", 0 },
-       { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$", 0 },
-       { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$", 0 },
+       { "java",
+         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
+         "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
+         REG_EXTENDED },
+       { "pascal",
+         "^((procedure|function|constructor|destructor|interface|"
+               "implementation|initialization|finalization)[ \t]*.*)$"
+         "|"
+         "^(.*=[ \t]*(class|record).*)$",
+         REG_EXTENDED },
+       { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+         REG_EXTENDED },
+       { "tex",
+         "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
+         REG_EXTENDED },
+       { "ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+         REG_EXTENDED },
 };
 
 static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
@@ -1457,6 +1465,10 @@ static void builtin_diff(const char *name_a,
        const char *set = diff_get_color_opt(o, DIFF_METAINFO);
        const char *reset = diff_get_color_opt(o, DIFF_RESET);
 
+       /* Never use a non-valid filename anywhere if at all possible */
+       name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
+       name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
+
        a_one = quote_two(o->a_prefix, name_a + (*name_a == '/'));
        b_two = quote_two(o->b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
@@ -2084,16 +2096,86 @@ static const char *external_diff_attr(const char *name)
        return NULL;
 }
 
+static int similarity_index(struct diff_filepair *p)
+{
+       return p->score * 100 / MAX_SCORE;
+}
+
+static void fill_metainfo(struct strbuf *msg,
+                         const char *name,
+                         const char *other,
+                         struct diff_filespec *one,
+                         struct diff_filespec *two,
+                         struct diff_options *o,
+                         struct diff_filepair *p)
+{
+       strbuf_init(msg, PATH_MAX * 2 + 300);
+       switch (p->status) {
+       case DIFF_STATUS_COPIED:
+               strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
+               strbuf_addstr(msg, "\ncopy from ");
+               quote_c_style(name, msg, NULL, 0);
+               strbuf_addstr(msg, "\ncopy to ");
+               quote_c_style(other, msg, NULL, 0);
+               strbuf_addch(msg, '\n');
+               break;
+       case DIFF_STATUS_RENAMED:
+               strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
+               strbuf_addstr(msg, "\nrename from ");
+               quote_c_style(name, msg, NULL, 0);
+               strbuf_addstr(msg, "\nrename to ");
+               quote_c_style(other, msg, NULL, 0);
+               strbuf_addch(msg, '\n');
+               break;
+       case DIFF_STATUS_MODIFIED:
+               if (p->score) {
+                       strbuf_addf(msg, "dissimilarity index %d%%\n",
+                                   similarity_index(p));
+                       break;
+               }
+               /* fallthru */
+       default:
+               /* nothing */
+               ;
+       }
+       if (one && two && hashcmp(one->sha1, two->sha1)) {
+               int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+
+               if (DIFF_OPT_TST(o, BINARY)) {
+                       mmfile_t mf;
+                       if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
+                           (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
+                               abbrev = 40;
+               }
+               strbuf_addf(msg, "index %.*s..%.*s",
+                           abbrev, sha1_to_hex(one->sha1),
+                           abbrev, sha1_to_hex(two->sha1));
+               if (one->mode == two->mode)
+                       strbuf_addf(msg, " %06o", one->mode);
+               strbuf_addch(msg, '\n');
+       }
+       if (msg->len)
+               strbuf_setlen(msg, msg->len - 1);
+}
+
 static void run_diff_cmd(const char *pgm,
                         const char *name,
                         const char *other,
                         const char *attr_path,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
-                        const char *xfrm_msg,
+                        struct strbuf *msg,
                         struct diff_options *o,
-                        int complete_rewrite)
+                        struct diff_filepair *p)
 {
+       const char *xfrm_msg = NULL;
+       int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
+
+       if (msg) {
+               fill_metainfo(msg, name, other, one, two, o, p);
+               xfrm_msg = msg->len ? msg->buf : NULL;
+       }
+
        if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
                pgm = NULL;
        else {
@@ -2133,11 +2215,6 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
                hashclr(one->sha1);
 }
 
-static int similarity_index(struct diff_filepair *p)
-{
-       return p->score * 100 / MAX_SCORE;
-}
-
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 {
        /* Strip the prefix but do not molest /dev/null and absolute paths */
@@ -2151,13 +2228,11 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 {
        const char *pgm = external_diff();
        struct strbuf msg;
-       char *xfrm_msg;
        struct diff_filespec *one = p->one;
        struct diff_filespec *two = p->two;
        const char *name;
        const char *other;
        const char *attr_path;
-       int complete_rewrite = 0;
 
        name  = p->one->path;
        other = (strcmp(name, p->two->path) ? p->two->path : NULL);
@@ -2167,83 +2242,34 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 
        if (DIFF_PAIR_UNMERGED(p)) {
                run_diff_cmd(pgm, name, NULL, attr_path,
-                            NULL, NULL, NULL, o, 0);
+                            NULL, NULL, NULL, o, p);
                return;
        }
 
        diff_fill_sha1_info(one);
        diff_fill_sha1_info(two);
 
-       strbuf_init(&msg, PATH_MAX * 2 + 300);
-       switch (p->status) {
-       case DIFF_STATUS_COPIED:
-               strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
-               strbuf_addstr(&msg, "\ncopy from ");
-               quote_c_style(name, &msg, NULL, 0);
-               strbuf_addstr(&msg, "\ncopy to ");
-               quote_c_style(other, &msg, NULL, 0);
-               strbuf_addch(&msg, '\n');
-               break;
-       case DIFF_STATUS_RENAMED:
-               strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
-               strbuf_addstr(&msg, "\nrename from ");
-               quote_c_style(name, &msg, NULL, 0);
-               strbuf_addstr(&msg, "\nrename to ");
-               quote_c_style(other, &msg, NULL, 0);
-               strbuf_addch(&msg, '\n');
-               break;
-       case DIFF_STATUS_MODIFIED:
-               if (p->score) {
-                       strbuf_addf(&msg, "dissimilarity index %d%%\n",
-                                       similarity_index(p));
-                       complete_rewrite = 1;
-                       break;
-               }
-               /* fallthru */
-       default:
-               /* nothing */
-               ;
-       }
-
-       if (hashcmp(one->sha1, two->sha1)) {
-               int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
-
-               if (DIFF_OPT_TST(o, BINARY)) {
-                       mmfile_t mf;
-                       if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
-                           (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
-                               abbrev = 40;
-               }
-               strbuf_addf(&msg, "index %.*s..%.*s",
-                               abbrev, sha1_to_hex(one->sha1),
-                               abbrev, sha1_to_hex(two->sha1));
-               if (one->mode == two->mode)
-                       strbuf_addf(&msg, " %06o", one->mode);
-               strbuf_addch(&msg, '\n');
-       }
-
-       if (msg.len)
-               strbuf_setlen(&msg, msg.len - 1);
-       xfrm_msg = msg.len ? msg.buf : NULL;
-
        if (!pgm &&
            DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
            (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
-               /* a filepair that changes between file and symlink
+               /*
+                * a filepair that changes between file and symlink
                 * needs to be split into deletion and creation.
                 */
                struct diff_filespec *null = alloc_filespec(two->path);
                run_diff_cmd(NULL, name, other, attr_path,
-                            one, null, xfrm_msg, o, 0);
+                            one, null, &msg, o, p);
                free(null);
+               strbuf_release(&msg);
+
                null = alloc_filespec(one->path);
                run_diff_cmd(NULL, name, other, attr_path,
-                            null, two, xfrm_msg, o, 0);
+                            null, two, &msg, o, p);
                free(null);
        }
        else
                run_diff_cmd(pgm, name, other, attr_path,
-                            one, two, xfrm_msg, o, complete_rewrite);
+                            one, two, &msg, o, p);
 
        strbuf_release(&msg);
 }
@@ -2404,13 +2430,6 @@ int diff_setup_done(struct diff_options *options)
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
 
-       /*
-        * If we postprocess in diffcore, we cannot simply return
-        * upon the first hit.  We need to run diff as usual.
-        */
-       if (options->pickaxe || options->filter)
-               DIFF_OPT_CLR(options, QUIET);
-
        return 0;
 }
 
@@ -3402,10 +3421,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
 
 void diffcore_std(struct diff_options *options)
 {
-       if (DIFF_OPT_TST(options, QUIET))
-               return;
-
-       if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+       if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);