Log ref changes made by git-merge and git-pull.
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 6d04be49dea83d319c68257d25dde6587179bdc5..287a927ce3131fd3a5c9151e5ea1e560842767a1 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -13,6 +13,7 @@
 
 static int use_size_cache;
 
+static int diff_detect_rename_default = 0;
 static int diff_rename_limit_default = -1;
 static int diff_use_color_default = 0;
 
@@ -43,12 +44,12 @@ enum color_diff {
 #define COLOR_WHITE   "\033[37m"
 
 static const char *diff_colors[] = {
-       [DIFF_RESET]    = COLOR_RESET,
-       [DIFF_PLAIN]    = COLOR_NORMAL,
-       [DIFF_METAINFO] = COLOR_BOLD,
-       [DIFF_FRAGINFO] = COLOR_CYAN,
-       [DIFF_FILE_OLD] = COLOR_RED,
-       [DIFF_FILE_NEW] = COLOR_GREEN,
+       COLOR_RESET,
+       COLOR_NORMAL,
+       COLOR_BOLD,
+       COLOR_CYAN,
+       COLOR_RED,
+       COLOR_GREEN
 };
 
 static int parse_diff_color_slot(const char *var, int ofs)
@@ -101,7 +102,13 @@ static const char *parse_diff_color_value(const char *value, const char *var)
        die("bad config value '%s' for variable '%s'", value, var);
 }
 
-int git_diff_config(const char *var, const char *value)
+/*
+ * These are to give UI layer defaults.
+ * The core-level commands such as git-diff-files should
+ * never be affected by the setting of diff.renames
+ * the user happens to have in the configuration file.
+ */
+int git_diff_ui_config(const char *var, const char *value)
 {
        if (!strcmp(var, "diff.renamelimit")) {
                diff_rename_limit_default = git_config_int(var, value);
@@ -110,8 +117,14 @@ int git_diff_config(const char *var, const char *value)
        if (!strcmp(var, "diff.color")) {
                if (!value)
                        diff_use_color_default = 1; /* bool */
-               else if (!strcasecmp(value, "auto"))
-                       diff_use_color_default = isatty(1);
+               else if (!strcasecmp(value, "auto")) {
+                       diff_use_color_default = 0;
+                       if (isatty(1) || pager_in_use) {
+                               char *term = getenv("TERM");
+                               if (term && strcmp(term, "dumb"))
+                                       diff_use_color_default = 1;
+                       }
+               }
                else if (!strcasecmp(value, "never"))
                        diff_use_color_default = 0;
                else if (!strcasecmp(value, "always"))
@@ -120,6 +133,16 @@ int git_diff_config(const char *var, const char *value)
                        diff_use_color_default = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.renames")) {
+               if (!value)
+                       diff_detect_rename_default = DIFF_DETECT_RENAME;
+               else if (!strcasecmp(value, "copies") ||
+                        !strcasecmp(value, "copy"))
+                       diff_detect_rename_default = DIFF_DETECT_COPY;
+               else if (git_config_bool(var,value))
+                       diff_detect_rename_default = DIFF_DETECT_RENAME;
+               return 0;
+       }
        if (!strncmp(var, "diff.color.", 11)) {
                int slot = parse_diff_color_slot(var, 11);
                diff_colors[slot] = parse_diff_color_value(value, var);
@@ -329,7 +352,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        }
        if (len > 0 && line[len-1] == '\n')
                len--;
-       printf("%s%.*s%s\n", set, (int) len, line, reset);
+       fputs (set, stdout);
+       fwrite (line, len, 1, stdout);
+       puts (reset);
 }
 
 static char *pprint_rename(const char *a, const char *b)
@@ -583,7 +608,7 @@ static unsigned char *deflate_it(char *data,
        z_stream stream;
 
        memset(&stream, 0, sizeof(stream));
-       deflateInit(&stream, Z_BEST_COMPRESSION);
+       deflateInit(&stream, zlib_compression_level);
        bound = deflateBound(&stream, size);
        deflated = xmalloc(bound);
        stream.next_out = deflated;
@@ -721,7 +746,7 @@ static void builtin_diff(const char *name_a,
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
 
-       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+       if (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) {
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1424,11 +1449,12 @@ void diff_setup(struct diff_options *options)
        options->break_opt = -1;
        options->rename_limit = -1;
        options->context = 3;
-       options->msg_sep = "\n";
+       options->msg_sep = "";
 
        options->change = diff_change;
        options->add_remove = diff_addremove;
        options->color_diff = diff_use_color_default;
+       options->detect_rename = diff_detect_rename_default;
 }
 
 int diff_setup_done(struct diff_options *options)
@@ -1455,6 +1481,11 @@ int diff_setup_done(struct diff_options *options)
                                      DIFF_FORMAT_DIFFSTAT |
                                      DIFF_FORMAT_CHECKDIFF))
                options->recursive = 1;
+       /*
+        * Also pickaxe would not work very well if you do not say recursive
+        */
+       if (options->pickaxe)
+               options->recursive = 1;
 
        if (options->detect_rename && options->rename_limit < 0)
                options->rename_limit = diff_rename_limit_default;
@@ -1554,6 +1585,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_PATCH;
                options->full_index = options->binary = 1;
        }
+       else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) {
+               options->text = 1;
+       }
        else if (!strcmp(arg, "--name-only"))
                options->output_format |= DIFF_FORMAT_NAME;
        else if (!strcmp(arg, "--name-status"))
@@ -1603,10 +1637,14 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if (!strcmp(arg, "--color"))
                options->color_diff = 1;
+       else if (!strcmp(arg, "--no-color"))
+               options->color_diff = 0;
        else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
                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, "--no-renames"))
+               options->detect_rename = 0;
        else
                return 0;
        return 1;
@@ -2090,6 +2128,145 @@ static void diff_summary(struct diff_filepair *p)
        }
 }
 
+struct patch_id_t {
+       struct xdiff_emit_state xm;
+       SHA_CTX *ctx;
+       int patchlen;
+};
+
+static int remove_space(char *line, int len)
+{
+       int i;
+        char *dst = line;
+        unsigned char c;
+
+        for (i = 0; i < len; i++)
+                if (!isspace((c = line[i])))
+                        *dst++ = c;
+
+        return dst - line;
+}
+
+static void patch_id_consume(void *priv, char *line, unsigned long len)
+{
+       struct patch_id_t *data = priv;
+       int new_len;
+
+       /* Ignore line numbers when computing the SHA1 of the patch */
+       if (!strncmp(line, "@@ -", 4))
+               return;
+
+       new_len = remove_space(line, len);
+
+       SHA1_Update(data->ctx, line, new_len);
+       data->patchlen += new_len;
+}
+
+/* returns 0 upon success, and writes result into sha1 */
+static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       int i;
+       SHA_CTX ctx;
+       struct patch_id_t data;
+       char buffer[PATH_MAX * 4 + 20];
+
+       SHA1_Init(&ctx);
+       memset(&data, 0, sizeof(struct patch_id_t));
+       data.ctx = &ctx;
+       data.xm.consume = patch_id_consume;
+
+       for (i = 0; i < q->nr; i++) {
+               xpparam_t xpp;
+               xdemitconf_t xecfg;
+               xdemitcb_t ecb;
+               mmfile_t mf1, mf2;
+               struct diff_filepair *p = q->queue[i];
+               int len1, len2;
+
+               if (p->status == 0)
+                       return error("internal diff status error");
+               if (p->status == DIFF_STATUS_UNKNOWN)
+                       continue;
+               if (diff_unmodified_pair(p))
+                       continue;
+               if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+                   (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+                       continue;
+               if (DIFF_PAIR_UNMERGED(p))
+                       continue;
+
+               diff_fill_sha1_info(p->one);
+               diff_fill_sha1_info(p->two);
+               if (fill_mmfile(&mf1, p->one) < 0 ||
+                               fill_mmfile(&mf2, p->two) < 0)
+                       return error("unable to read files to diff");
+
+               /* Maybe hash p->two? into the patch id? */
+               if (mmfile_is_binary(&mf2))
+                       continue;
+
+               len1 = remove_space(p->one->path, strlen(p->one->path));
+               len2 = remove_space(p->two->path, strlen(p->two->path));
+               if (p->one->mode == 0)
+                       len1 = snprintf(buffer, sizeof(buffer),
+                                       "diff--gita/%.*sb/%.*s"
+                                       "newfilemode%06o"
+                                       "---/dev/null"
+                                       "+++b/%.*s",
+                                       len1, p->one->path,
+                                       len2, p->two->path,
+                                       p->two->mode,
+                                       len2, p->two->path);
+               else if (p->two->mode == 0)
+                       len1 = snprintf(buffer, sizeof(buffer),
+                                       "diff--gita/%.*sb/%.*s"
+                                       "deletedfilemode%06o"
+                                       "---a/%.*s"
+                                       "+++/dev/null",
+                                       len1, p->one->path,
+                                       len2, p->two->path,
+                                       p->one->mode,
+                                       len1, p->one->path);
+               else
+                       len1 = snprintf(buffer, sizeof(buffer),
+                                       "diff--gita/%.*sb/%.*s"
+                                       "---a/%.*s"
+                                       "+++b/%.*s",
+                                       len1, p->one->path,
+                                       len2, p->two->path,
+                                       len1, p->one->path,
+                                       len2, p->two->path);
+               SHA1_Update(&ctx, buffer, len1);
+
+               xpp.flags = XDF_NEED_MINIMAL;
+               xecfg.ctxlen = 3;
+               xecfg.flags = XDL_EMIT_FUNCNAMES;
+               ecb.outf = xdiff_outf;
+               ecb.priv = &data;
+               xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+       }
+
+       SHA1_Final(sha1, &ctx);
+       return 0;
+}
+
+int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       int i;
+       int result = diff_get_patch_id(options, sha1);
+
+       for (i = 0; i < q->nr; i++)
+               diff_free_filepair(q->queue[i]);
+
+       free(q->queue);
+       q->queue = NULL;
+       q->nr = q->alloc = 0;
+
+       return result;
+}
+
 static int is_summary_empty(const struct diff_queue_struct *q)
 {
        int i;
@@ -2143,9 +2320,6 @@ void diff_flush(struct diff_options *options)
        if (output_format & DIFF_FORMAT_DIFFSTAT) {
                struct diffstat_t diffstat;
 
-               if (separator++)
-                       putchar('\n');
-
                memset(&diffstat, 0, sizeof(struct diffstat_t));
                diffstat.xm.consume = diffstat_consume;
                for (i = 0; i < q->nr; i++) {
@@ -2154,14 +2328,13 @@ void diff_flush(struct diff_options *options)
                                diff_flush_stat(p, options, &diffstat);
                }
                show_stats(&diffstat);
+               separator++;
        }
 
        if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
-               if (separator++)
-                       putchar('\n');
-
                for (i = 0; i < q->nr; i++)
                        diff_summary(q->queue[i]);
+               separator++;
        }
 
        if (output_format & DIFF_FORMAT_PATCH) {