branch: fix segfault when resolving an invalid HEAD
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 6bd456ed42e76a41c62908e5bb0d7df2b329b032..b28933f870d7dd11596fd3715280e1488ac2430f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -382,6 +382,7 @@ struct emit_callback {
        int nparents, color_diff;
        const char **label_path;
        struct diff_words_data *diff_words;
+       int *found_changesp;
 };
 
 static void free_diff_words_data(struct emit_callback *ecbdata)
@@ -501,6 +502,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
 
+       *(ecbdata->found_changesp) = 1;
+
        if (ecbdata->label_path[0]) {
                const char *name_a_tab, *name_b_tab;
 
@@ -1098,6 +1101,7 @@ static void builtin_diff(const char *name_a,
                if (complete_rewrite) {
                        emit_rewrite_diff(name_a, name_b, one, two,
                                        o->color_diff);
+                       o->found_changes = 1;
                        goto free_ab_and_return;
                }
        }
@@ -1115,6 +1119,7 @@ static void builtin_diff(const char *name_a,
                else
                        printf("Binary files %s and %s differ\n",
                               lbl[0], lbl[1]);
+               o->found_changes = 1;
        }
        else {
                /* Crazy xdl interfaces.. */
@@ -1127,6 +1132,7 @@ static void builtin_diff(const char *name_a,
                memset(&ecbdata, 0, sizeof(ecbdata));
                ecbdata.label_path = lbl;
                ecbdata.color_diff = o->color_diff;
+               ecbdata.found_changesp = &o->found_changes;
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1358,6 +1364,32 @@ static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
        return e;
 }
 
+static int populate_from_stdin(struct diff_filespec *s)
+{
+#define INCREMENT 1024
+       char *buf;
+       unsigned long size;
+       int got;
+
+       size = 0;
+       buf = NULL;
+       while (1) {
+               buf = xrealloc(buf, size + INCREMENT);
+               got = xread(0, buf + size, INCREMENT);
+               if (!got)
+                       break; /* EOF */
+               if (got < 0)
+                       return error("error while reading from stdin %s",
+                                    strerror(errno));
+               size += got;
+       }
+       s->should_munmap = 0;
+       s->data = buf;
+       s->size = size;
+       s->should_free = 1;
+       return 0;
+}
+
 /*
  * While doing rename detection and pickaxe operation, we may need to
  * grab the data for the blob (or file) for our own in-core comparison.
@@ -1383,6 +1415,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                char *buf;
                unsigned long size;
 
+               if (!strcmp(s->path, "-"))
+                       return populate_from_stdin(s);
+
                if (lstat(s->path, &st) < 0) {
                        if (errno == ENOENT) {
                        err_empty:
@@ -1393,7 +1428,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                                return err;
                        }
                }
-               s->size = st.st_size;
+               s->size = xsize_t(st.st_size);
                if (!s->size)
                        goto empty;
                if (size_only)
@@ -1430,20 +1465,22 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                }
        }
        else {
-               char type[20];
+               enum object_type type;
                struct sha1_size_cache *e;
 
+               if (size_only && use_size_cache &&
+                   (e = locate_size_cache(s->sha1, 1, 0)) != NULL) {
+                       s->size = e->size;
+                       return 0;
+               }
+
                if (size_only) {
-                       e = locate_size_cache(s->sha1, 1, 0);
-                       if (e) {
-                               s->size = e->size;
-                               return 0;
-                       }
-                       if (!sha1_object_info(s->sha1, type, &s->size))
+                       type = sha1_object_info(s->sha1, &s->size);
+                       if (use_size_cache && 0 < type)
                                locate_size_cache(s->sha1, 0, s->size);
                }
                else {
-                       s->data = read_sha1_file(s->sha1, type, &s->size);
+                       s->data = read_sha1_file(s->sha1, &type, &s->size);
                        s->should_free = 1;
                }
        }
@@ -1508,12 +1545,13 @@ static void prepare_temp_file(const char *name,
                if (S_ISLNK(st.st_mode)) {
                        int ret;
                        char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */
+                       size_t sz = xsize_t(st.st_size);
                        if (sizeof(buf) <= st.st_size)
                                die("symlink too long: %s", name);
-                       ret = readlink(name, buf, st.st_size);
+                       ret = readlink(name, buf, sz);
                        if (ret < 0)
                                die("readlink(%s)", name);
-                       prep_temp_blob(temp, buf, st.st_size,
+                       prep_temp_blob(temp, buf, sz,
                                       (one->sha1_valid ?
                                        one->sha1 : null_sha1),
                                       (one->sha1_valid ?
@@ -1682,6 +1720,10 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
        if (DIFF_FILE_VALID(one)) {
                if (!one->sha1_valid) {
                        struct stat st;
+                       if (!strcmp(one->path, "-")) {
+                               hashcpy(one->sha1, null_sha1);
+                               return;
+                       }
                        if (lstat(one->path, &st) < 0)
                                die("stat %s", one->path);
                        if (index_path(one->sha1, one->path, &st, 0))
@@ -1917,6 +1959,23 @@ int diff_setup_done(struct diff_options *options)
        if (options->abbrev <= 0 || 40 < options->abbrev)
                options->abbrev = 40; /* full */
 
+       /*
+        * It does not make sense to show the first hit we happened
+        * to have found.  It does not make sense not to return with
+        * exit code in such a case either.
+        */
+       if (options->quiet) {
+               options->output_format = DIFF_FORMAT_NO_OUTPUT;
+               options->exit_with_status = 1;
+       }
+
+       /*
+        * 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)
+               options->quiet = 0;
+
        return 0;
 }
 
@@ -2093,6 +2152,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->color_diff = options->color_diff_words = 1;
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
+       else if (!strcmp(arg, "--exit-code"))
+               options->exit_with_status = 1;
+       else if (!strcmp(arg, "--quiet"))
+               options->quiet = 1;
        else
                return 0;
        return 1;
@@ -2131,7 +2194,7 @@ static int parse_num(const char **cp_p)
        /* user says num divided by scale and we say internally that
         * is MAX_SCORE * num / scale.
         */
-       return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale);
+       return (int)((num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale));
 }
 
 int diff_scoreopt_parse(const char *opt)
@@ -2857,6 +2920,8 @@ static void diffcore_apply_filter(const char *filter)
 
 void diffcore_std(struct diff_options *options)
 {
+       if (options->quiet)
+               return;
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);
        if (options->detect_rename)
@@ -2869,18 +2934,11 @@ void diffcore_std(struct diff_options *options)
                diffcore_order(options->orderfile);
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
-}
-
 
-void diffcore_std_no_resolve(struct diff_options *options)
-{
-       if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
-       if (options->orderfile)
-               diffcore_order(options->orderfile);
-       diffcore_apply_filter(options->filter);
+       options->has_changes = !!diff_queued_diff.nr;
 }
 
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
@@ -2916,6 +2974,7 @@ void diff_addremove(struct diff_options *options,
                fill_filespec(two, sha1, mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_change(struct diff_options *options,
@@ -2941,6 +3000,7 @@ void diff_change(struct diff_options *options,
        fill_filespec(two, new_sha1, new_mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_unmerge(struct diff_options *options,