Code

gitweb: Add support for hash_parent_base parameter for blobdiffs
[git.git] / diff.c
diff --git a/diff.c b/diff.c
index 8b44756136dbb040c7c82e07ca535be0b66ffb30..7a238d023336d4340efd67534dae4ed23dd5d3c0 100644 (file)
--- a/diff.c
+++ b/diff.c
 
 static int use_size_cache;
 
-static int diff_detect_rename_default = 0;
+static int diff_detect_rename_default;
 static int diff_rename_limit_default = -1;
-static int diff_use_color_default = 0;
-
-enum color_diff {
-       DIFF_RESET = 0,
-       DIFF_PLAIN = 1,
-       DIFF_METAINFO = 2,
-       DIFF_FRAGINFO = 3,
-       DIFF_FILE_OLD = 4,
-       DIFF_FILE_NEW = 5,
-};
+static int diff_use_color_default;
 
 /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
 static char diff_colors[][24] = {
@@ -33,7 +24,8 @@ static char diff_colors[][24] = {
        "\033[1m",      /* bold */
        "\033[36m",     /* cyan */
        "\033[31m",     /* red */
-       "\033[32m"      /* green */
+       "\033[32m",     /* green */
+       "\033[33m"      /* yellow */
 };
 
 static int parse_diff_color_slot(const char *var, int ofs)
@@ -48,6 +40,8 @@ static int parse_diff_color_slot(const char *var, int ofs)
                return DIFF_FILE_OLD;
        if (!strcasecmp(var+ofs, "new"))
                return DIFF_FILE_NEW;
+       if (!strcasecmp(var+ofs, "commit"))
+               return DIFF_COMMIT;
        die("bad config variable '%s'", var);
 }
 
@@ -181,7 +175,7 @@ int git_diff_ui_config(const char *var, const char *value)
                        diff_use_color_default = 1; /* bool */
                else if (!strcasecmp(value, "auto")) {
                        diff_use_color_default = 0;
-                       if (isatty(1) || pager_in_use) {
+                       if (isatty(1) || (pager_in_use && pager_use_color)) {
                                char *term = getenv("TERM");
                                if (term && strcmp(term, "dumb"))
                                        diff_use_color_default = 1;
@@ -364,13 +358,153 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
        return 0;
 }
 
+struct diff_words_buffer {
+       mmfile_t text;
+       long alloc;
+       long current; /* output pointer */
+       int suppressed_newline;
+};
+
+static void diff_words_append(char *line, unsigned long len,
+               struct diff_words_buffer *buffer)
+{
+       if (buffer->text.size + len > buffer->alloc) {
+               buffer->alloc = (buffer->text.size + len) * 3 / 2;
+               buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
+       }
+       line++;
+       len--;
+       memcpy(buffer->text.ptr + buffer->text.size, line, len);
+       buffer->text.size += len;
+}
+
+struct diff_words_data {
+       struct xdiff_emit_state xm;
+       struct diff_words_buffer minus, plus;
+};
+
+static void print_word(struct diff_words_buffer *buffer, int len, int color,
+               int suppress_newline)
+{
+       const char *ptr;
+       int eol = 0;
+
+       if (len == 0)
+               return;
+
+       ptr  = buffer->text.ptr + buffer->current;
+       buffer->current += len;
+
+       if (ptr[len - 1] == '\n') {
+               eol = 1;
+               len--;
+       }
+
+       fputs(diff_get_color(1, color), stdout);
+       fwrite(ptr, len, 1, stdout);
+       fputs(diff_get_color(1, DIFF_RESET), stdout);
+
+       if (eol) {
+               if (suppress_newline)
+                       buffer->suppressed_newline = 1;
+               else
+                       putchar('\n');
+       }
+}
+
+static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
+{
+       struct diff_words_data *diff_words = priv;
+
+       if (diff_words->minus.suppressed_newline) {
+               if (line[0] != '+')
+                       putchar('\n');
+               diff_words->minus.suppressed_newline = 0;
+       }
+
+       len--;
+       switch (line[0]) {
+               case '-':
+                       print_word(&diff_words->minus, len, DIFF_FILE_OLD, 1);
+                       break;
+               case '+':
+                       print_word(&diff_words->plus, len, DIFF_FILE_NEW, 0);
+                       break;
+               case ' ':
+                       print_word(&diff_words->plus, len, DIFF_PLAIN, 0);
+                       diff_words->minus.current += len;
+                       break;
+       }
+}
+
+/* this executes the word diff on the accumulated buffers */
+static void diff_words_show(struct diff_words_data *diff_words)
+{
+       xpparam_t xpp;
+       xdemitconf_t xecfg;
+       xdemitcb_t ecb;
+       mmfile_t minus, plus;
+       int i;
+
+       minus.size = diff_words->minus.text.size;
+       minus.ptr = xmalloc(minus.size);
+       memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
+       for (i = 0; i < minus.size; i++)
+               if (isspace(minus.ptr[i]))
+                       minus.ptr[i] = '\n';
+       diff_words->minus.current = 0;
+
+       plus.size = diff_words->plus.text.size;
+       plus.ptr = xmalloc(plus.size);
+       memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
+       for (i = 0; i < plus.size; i++)
+               if (isspace(plus.ptr[i]))
+                       plus.ptr[i] = '\n';
+       diff_words->plus.current = 0;
+
+       xpp.flags = XDF_NEED_MINIMAL;
+       xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
+       xecfg.flags = 0;
+       ecb.outf = xdiff_outf;
+       ecb.priv = diff_words;
+       diff_words->xm.consume = fn_out_diff_words_aux;
+       xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+
+       free(minus.ptr);
+       free(plus.ptr);
+       diff_words->minus.text.size = diff_words->plus.text.size = 0;
+
+       if (diff_words->minus.suppressed_newline) {
+               putchar('\n');
+               diff_words->minus.suppressed_newline = 0;
+       }
+}
+
 struct emit_callback {
        struct xdiff_emit_state xm;
        int nparents, color_diff;
        const char **label_path;
+       struct diff_words_data *diff_words;
 };
 
-static inline const char *get_color(int diff_use_color, enum color_diff ix)
+static void free_diff_words_data(struct emit_callback *ecbdata)
+{
+       if (ecbdata->diff_words) {
+               /* flush buffers */
+               if (ecbdata->diff_words->minus.text.size ||
+                               ecbdata->diff_words->plus.text.size)
+                       diff_words_show(ecbdata->diff_words);
+
+               if (ecbdata->diff_words->minus.text.ptr)
+                       free (ecbdata->diff_words->minus.text.ptr);
+               if (ecbdata->diff_words->plus.text.ptr)
+                       free (ecbdata->diff_words->plus.text.ptr);
+               free(ecbdata->diff_words);
+               ecbdata->diff_words = NULL;
+       }
+}
+
+const char *diff_get_color(int diff_use_color, enum color_diff ix)
 {
        if (diff_use_color)
                return diff_colors[ix];
@@ -381,8 +515,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
        int i;
        struct emit_callback *ecbdata = priv;
-       const char *set = get_color(ecbdata->color_diff, DIFF_METAINFO);
-       const char *reset = get_color(ecbdata->color_diff, DIFF_RESET);
+       const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
+       const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
 
        if (ecbdata->label_path[0]) {
                printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
@@ -397,20 +531,39 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                ;
        if (2 <= i && i < len && line[i] == ' ') {
                ecbdata->nparents = i - 1;
-               set = get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+               set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        }
        else if (len < ecbdata->nparents)
                set = reset;
        else {
                int nparents = ecbdata->nparents;
                int color = DIFF_PLAIN;
-               for (i = 0; i < nparents && len; i++) {
-                       if (line[i] == '-')
-                               color = DIFF_FILE_OLD;
-                       else if (line[i] == '+')
-                               color = DIFF_FILE_NEW;
-               }
-               set = get_color(ecbdata->color_diff, color);
+               if (ecbdata->diff_words && nparents != 1)
+                       /* fall back to normal diff */
+                       free_diff_words_data(ecbdata);
+               if (ecbdata->diff_words) {
+                       if (line[0] == '-') {
+                               diff_words_append(line, len,
+                                               &ecbdata->diff_words->minus);
+                               return;
+                       } else if (line[0] == '+') {
+                               diff_words_append(line, len,
+                                               &ecbdata->diff_words->plus);
+                               return;
+                       }
+                       if (ecbdata->diff_words->minus.text.size ||
+                                       ecbdata->diff_words->plus.text.size)
+                               diff_words_show(ecbdata->diff_words);
+                       line++;
+                       len--;
+               } else
+                       for (i = 0; i < nparents && len; i++) {
+                               if (line[i] == '-')
+                                       color = DIFF_FILE_OLD;
+                               else if (line[i] == '+')
+                                       color = DIFF_FILE_NEW;
+                       }
+               set = diff_get_color(ecbdata->color_diff, color);
        }
        if (len > 0 && line[len-1] == '\n')
                len--;
@@ -751,9 +904,7 @@ static int mmfile_is_binary(mmfile_t *mf)
        long sz = mf->size;
        if (FIRST_FEW_BYTES < sz)
                sz = FIRST_FEW_BYTES;
-       if (memchr(mf->ptr, 0, sz))
-               return 1;
-       return 0;
+       return !!memchr(mf->ptr, 0, sz);
 }
 
 static void builtin_diff(const char *name_a,
@@ -767,8 +918,8 @@ static void builtin_diff(const char *name_a,
        mmfile_t mf1, mf2;
        const char *lbl[2];
        char *a_one, *b_two;
-       const char *set = get_color(o->color_diff, DIFF_METAINFO);
-       const char *reset = get_color(o->color_diff, DIFF_RESET);
+       const char *set = diff_get_color(o->color_diff, DIFF_METAINFO);
+       const char *reset = diff_get_color(o->color_diff, DIFF_RESET);
 
        a_one = quote_two("a/", name_a);
        b_two = quote_two("b/", name_b);
@@ -842,7 +993,12 @@ static void builtin_diff(const char *name_a,
                ecb.outf = xdiff_outf;
                ecb.priv = &ecbdata;
                ecbdata.xm.consume = fn_out_consume;
+               if (o->color_diff_words)
+                       ecbdata.diff_words =
+                               xcalloc(1, sizeof(struct diff_words_data));
                xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+               if (o->color_diff_words)
+                       free_diff_words_data(&ecbdata);
        }
 
  free_ab_and_return:
@@ -946,7 +1102,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
        if (mode) {
                spec->mode = canon_mode(mode);
                memcpy(spec->sha1, sha1, 20);
-               spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
+               spec->sha1_valid = !is_null_sha1(sha1);
        }
 }
 
@@ -1521,10 +1677,21 @@ void diff_setup(struct diff_options *options)
 
 int diff_setup_done(struct diff_options *options)
 {
-       if ((options->find_copies_harder &&
-            options->detect_rename != DIFF_DETECT_COPY) ||
-           (0 <= options->rename_limit && !options->detect_rename))
-               return -1;
+       int count = 0;
+
+       if (options->output_format & DIFF_FORMAT_NAME)
+               count++;
+       if (options->output_format & DIFF_FORMAT_NAME_STATUS)
+               count++;
+       if (options->output_format & DIFF_FORMAT_CHECKDIFF)
+               count++;
+       if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
+               count++;
+       if (count > 1)
+               die("--name-only, --name-status, --check and -s are mutually exclusive");
+
+       if (options->find_copies_harder)
+               options->detect_rename = DIFF_DETECT_COPY;
 
        if (options->output_format & (DIFF_FORMAT_NAME |
                                      DIFF_FORMAT_NAME_STATUS |
@@ -1705,6 +1872,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->xdl_opts |= XDF_IGNORE_WHITESPACE;
        else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+       else if (!strcmp(arg, "--color-words"))
+               options->color_diff = options->color_diff_words = 1;
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
        else
@@ -1792,13 +1961,9 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
                                 struct diff_filespec *one,
                                 struct diff_filespec *two)
 {
-       struct diff_filepair *dp = xmalloc(sizeof(*dp));
+       struct diff_filepair *dp = xcalloc(1, sizeof(*dp));
        dp->one = one;
        dp->two = two;
-       dp->score = 0;
-       dp->status = 0;
-       dp->source_stays = 0;
-       dp->broken_pair = 0;
        if (queue)
                diff_q(queue, dp);
        return dp;