gitweb: link to 7-char+ SHA-1s, not only 8-char+
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index fa78fc189cd7225767817a2d24ef6c3b19bef9d8..7a4309397cb7fd6819d6f643b259c2bc3abe202f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -354,7 +354,6 @@ struct emit_callback {
        const char **label_path;
        struct diff_words_data *diff_words;
        struct diff_options *opt;
-       int *found_changesp;
        struct strbuf *header;
 };
 
@@ -722,7 +721,6 @@ static void emit_rewrite_diff(const char *name_a,
 
        memset(&ecbdata, 0, sizeof(ecbdata));
        ecbdata.color_diff = want_color(o->use_color);
-       ecbdata.found_changesp = &o->found_changes;
        ecbdata.ws_rule = whitespace_rule(name_b);
        ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
@@ -951,7 +949,8 @@ static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
 {
        if (word_regex && *begin < buffer->size) {
                regmatch_t match[1];
-               if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+               if (!regexec_buf(word_regex, buffer->ptr + *begin,
+                                buffer->size - *begin, 1, match, 0)) {
                        char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
                                        '\n', match[0].rm_eo - match[0].rm_so);
                        *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
@@ -1216,12 +1215,13 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        struct diff_options *o = ecbdata->opt;
        const char *line_prefix = diff_line_prefix(o);
 
+       o->found_changes = 1;
+
        if (ecbdata->header) {
-               fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
+               fprintf(o->file, "%s", ecbdata->header->buf);
                strbuf_reset(ecbdata->header);
                ecbdata->header = NULL;
        }
-       *(ecbdata->found_changesp) = 1;
 
        if (ecbdata->label_path[0]) {
                const char *name_a_tab, *name_b_tab;
@@ -1229,9 +1229,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
                name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-               fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
+               fprintf(o->file, "%s%s--- %s%s%s\n",
                        line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
-               fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
+               fprintf(o->file, "%s%s+++ %s%s%s\n",
                        line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
@@ -1249,15 +1249,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                find_lno(line, ecbdata);
                emit_hunk_header(ecbdata, line, len);
                if (line[len-1] != '\n')
-                       putc('\n', ecbdata->opt->file);
-               return;
-       }
-
-       if (len < 1) {
-               emit_line(ecbdata->opt, reset, reset, line, len);
-               if (ecbdata->diff_words
-                   && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
-                       fputs("~\n", ecbdata->opt->file);
+                       putc('\n', o->file);
                return;
        }
 
@@ -1282,8 +1274,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
-                       emit_line(ecbdata->opt, context, reset, line, len);
-                       fputs("~\n", ecbdata->opt->file);
+                       emit_line(o, context, reset, line, len);
+                       fputs("~\n", o->file);
                } else {
                        /*
                         * Skip the prefix character, if any.  With
@@ -1294,7 +1286,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                              line++;
                              len--;
                        }
-                       emit_line(ecbdata->opt, context, reset, line, len);
+                       emit_line(o, context, reset, line, len);
                }
                return;
        }
@@ -1316,8 +1308,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        default:
                /* incomplete line at the end */
                ecbdata->lno_in_preimage++;
-               emit_line(ecbdata->opt,
-                         diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
+               emit_line(o, diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
                          reset, line, len);
                break;
        }
@@ -1933,8 +1924,8 @@ static void show_dirstat(struct diff_options *options)
 
                name = p->two->path ? p->two->path : p->one->path;
 
-               if (p->one->sha1_valid && p->two->sha1_valid)
-                       content_changed = hashcmp(p->one->sha1, p->two->sha1);
+               if (p->one->oid_valid && p->two->oid_valid)
+                       content_changed = oidcmp(&p->one->oid, &p->two->oid);
                else
                        content_changed = 1;
 
@@ -2306,7 +2297,8 @@ static void builtin_diff(const char *name_a,
                const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
                show_submodule_summary(o->file, one->path ? one->path : two->path,
                                line_prefix,
-                               one->sha1, two->sha1, two->dirty_submodule,
+                               one->oid.hash, two->oid.hash,
+                               two->dirty_submodule,
                                meta, del, add, reset);
                return;
        }
@@ -2384,7 +2376,7 @@ static void builtin_diff(const char *name_a,
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
                    !DIFF_OPT_TST(o, BINARY)) {
-                       if (!hashcmp(one->sha1, two->sha1)) {
+                       if (!oidcmp(&one->oid, &two->oid)) {
                                if (must_show_header)
                                        fprintf(o->file, "%s", header.buf);
                                goto free_ab_and_return;
@@ -2437,7 +2429,6 @@ static void builtin_diff(const char *name_a,
                memset(&ecbdata, 0, sizeof(ecbdata));
                ecbdata.label_path = lbl;
                ecbdata.color_diff = want_color(o->use_color);
-               ecbdata.found_changesp = &o->found_changes;
                ecbdata.ws_rule = whitespace_rule(name_b);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
@@ -2505,7 +2496,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
                return;
        }
 
-       same_contents = !hashcmp(one->sha1, two->sha1);
+       same_contents = !oidcmp(&one->oid, &two->oid);
 
        if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
                data->is_binary = 1;
@@ -2638,8 +2629,8 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
 {
        if (mode) {
                spec->mode = canon_mode(mode);
-               hashcpy(spec->sha1, sha1);
-               spec->sha1_valid = sha1_valid;
+               hashcpy(spec->oid.hash, sha1);
+               spec->oid_valid = sha1_valid;
        }
 }
 
@@ -2682,6 +2673,13 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
        if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
                return 0;
 
+       /*
+        * Similarly, if we'd have to convert the file contents anyway, that
+        * makes the optimization not worthwhile.
+        */
+       if (!want_file && would_convert_to_git(name))
+               return 0;
+
        len = strlen(name);
        pos = cache_name_pos(name, len);
        if (pos < 0)
@@ -2721,7 +2719,8 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
        if (s->dirty_submodule)
                dirty = "-dirty";
 
-       strbuf_addf(&buf, "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
+       strbuf_addf(&buf, "Subproject commit %s%s\n",
+                   oid_to_hex(&s->oid), dirty);
        s->size = buf.len;
        if (size_only) {
                s->data = NULL;
@@ -2764,8 +2763,8 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
        if (S_ISGITLINK(s->mode))
                return diff_populate_gitlink(s, size_only);
 
-       if (!s->sha1_valid ||
-           reuse_worktree_file(s->path, s->sha1, 0)) {
+       if (!s->oid_valid ||
+           reuse_worktree_file(s->path, s->oid.hash, 0)) {
                struct strbuf buf = STRBUF_INIT;
                struct stat st;
                int fd;
@@ -2822,9 +2821,10 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
        else {
                enum object_type type;
                if (size_only || (flags & CHECK_BINARY)) {
-                       type = sha1_object_info(s->sha1, &s->size);
+                       type = sha1_object_info(s->oid.hash, &s->size);
                        if (type < 0)
-                               die("unable to read %s", sha1_to_hex(s->sha1));
+                               die("unable to read %s",
+                                   oid_to_hex(&s->oid));
                        if (size_only)
                                return 0;
                        if (s->size > big_file_threshold && s->is_binary == -1) {
@@ -2832,9 +2832,9 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                                return 0;
                        }
                }
-               s->data = read_sha1_file(s->sha1, &type, &s->size);
+               s->data = read_sha1_file(s->oid.hash, &type, &s->size);
                if (!s->data)
-                       die("unable to read %s", sha1_to_hex(s->sha1));
+                       die("unable to read %s", oid_to_hex(&s->oid));
                s->should_free = 1;
        }
        return 0;
@@ -2863,7 +2863,7 @@ void diff_free_filespec_data(struct diff_filespec *s)
 static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
                           void *blob,
                           unsigned long size,
-                          const unsigned char *sha1,
+                          const struct object_id *oid,
                           int mode)
 {
        int fd;
@@ -2888,7 +2888,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
                die_errno("unable to write temp-file");
        close_tempfile(&temp->tempfile);
        temp->name = get_tempfile_path(&temp->tempfile);
-       sha1_to_hex_r(temp->hex, sha1);
+       oid_to_hex_r(temp->hex, oid);
        xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
        strbuf_release(&buf);
        strbuf_release(&template);
@@ -2912,8 +2912,8 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
        }
 
        if (!S_ISGITLINK(one->mode) &&
-           (!one->sha1_valid ||
-            reuse_worktree_file(name, one->sha1, 1))) {
+           (!one->oid_valid ||
+            reuse_worktree_file(name, one->oid.hash, 1))) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
@@ -2925,19 +2925,19 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                        if (strbuf_readlink(&sb, name, st.st_size) < 0)
                                die_errno("readlink(%s)", name);
                        prep_temp_blob(name, temp, sb.buf, sb.len,
-                                      (one->sha1_valid ?
-                                       one->sha1 : null_sha1),
-                                      (one->sha1_valid ?
+                                      (one->oid_valid ?
+                                       &one->oid : &null_oid),
+                                      (one->oid_valid ?
                                        one->mode : S_IFLNK));
                        strbuf_release(&sb);
                }
                else {
                        /* we can borrow from the file in the work tree */
                        temp->name = name;
-                       if (!one->sha1_valid)
+                       if (!one->oid_valid)
                                sha1_to_hex_r(temp->hex, null_sha1);
                        else
-                               sha1_to_hex_r(temp->hex, one->sha1);
+                               sha1_to_hex_r(temp->hex, one->oid.hash);
                        /* Even though we may sometimes borrow the
                         * contents from the work tree, we always want
                         * one->mode.  mode is trustworthy even when
@@ -2952,7 +2952,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                if (diff_populate_filespec(one, 0))
                        die("cannot read data blob for %s", one->path);
                prep_temp_blob(name, temp, one->data, one->size,
-                              one->sha1, one->mode);
+                              &one->oid, one->mode);
        }
        return temp;
 }
@@ -3065,7 +3065,7 @@ static void fill_metainfo(struct strbuf *msg,
        default:
                *must_show_header = 0;
        }
-       if (one && two && hashcmp(one->sha1, two->sha1)) {
+       if (one && two && oidcmp(&one->oid, &two->oid)) {
                int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
 
                if (DIFF_OPT_TST(o, BINARY)) {
@@ -3075,8 +3075,8 @@ static void fill_metainfo(struct strbuf *msg,
                                abbrev = 40;
                }
                strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
-                           find_unique_abbrev(one->sha1, abbrev));
-               strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
+                           find_unique_abbrev(one->oid.hash, abbrev));
+               strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
                strbuf_addf(msg, "%s\n", reset);
@@ -3131,20 +3131,20 @@ static void run_diff_cmd(const char *pgm,
 static void diff_fill_sha1_info(struct diff_filespec *one)
 {
        if (DIFF_FILE_VALID(one)) {
-               if (!one->sha1_valid) {
+               if (!one->oid_valid) {
                        struct stat st;
                        if (one->is_stdin) {
-                               hashcpy(one->sha1, null_sha1);
+                               oidclr(&one->oid);
                                return;
                        }
                        if (lstat(one->path, &st) < 0)
                                die_errno("stat '%s'", one->path);
-                       if (index_path(one->sha1, one->path, &st, 0))
+                       if (index_path(one->oid.hash, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
        }
        else
-               hashclr(one->sha1);
+               oidclr(&one->oid);
 }
 
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
@@ -3977,6 +3977,8 @@ int diff_opt_parse(struct diff_options *options,
                if (!options->file)
                        die_errno("Could not open '%s'", path);
                options->close_file = 1;
+               if (options->use_color != GIT_COLOR_ALWAYS)
+                       options->use_color = GIT_COLOR_NEVER;
                return argcount;
        } else
                return 0;
@@ -4118,8 +4120,9 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
        fprintf(opt->file, "%s", diff_line_prefix(opt));
        if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
                fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
-                       diff_unique_abbrev(p->one->sha1, opt->abbrev));
-               fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
+                       diff_unique_abbrev(p->one->oid.hash, opt->abbrev));
+               fprintf(opt->file, "%s ",
+                       diff_unique_abbrev(p->two->oid.hash, opt->abbrev));
        }
        if (p->score) {
                fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
@@ -4168,11 +4171,11 @@ int diff_unmodified_pair(struct diff_filepair *p)
        /* both are valid and point at the same path.  that is, we are
         * dealing with a change.
         */
-       if (one->sha1_valid && two->sha1_valid &&
-           !hashcmp(one->sha1, two->sha1) &&
+       if (one->oid_valid && two->oid_valid &&
+           !oidcmp(&one->oid, &two->oid) &&
            !one->dirty_submodule && !two->dirty_submodule)
                return 1; /* no change */
-       if (!one->sha1_valid && !two->sha1_valid)
+       if (!one->oid_valid && !two->oid_valid)
                return 1; /* both look at the same file on the filesystem. */
        return 0;
 }
@@ -4233,7 +4236,7 @@ void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
                s->path,
                DIFF_FILE_VALID(s) ? "valid" : "invalid",
                s->mode,
-               s->sha1_valid ? sha1_to_hex(s->sha1) : "");
+               s->oid_valid ? oid_to_hex(&s->oid) : "");
        fprintf(stderr, "queue[%d] %s size %lu\n",
                x, one ? one : "",
                s->size);
@@ -4303,11 +4306,11 @@ static void diff_resolve_rename_copy(void)
                        else
                                p->status = DIFF_STATUS_RENAMED;
                }
-               else if (hashcmp(p->one->sha1, p->two->sha1) ||
+               else if (oidcmp(&p->one->oid, &p->two->oid) ||
                         p->one->mode != p->two->mode ||
                         p->one->dirty_submodule ||
                         p->two->dirty_submodule ||
-                        is_null_sha1(p->one->sha1))
+                        is_null_oid(&p->one->oid))
                        p->status = DIFF_STATUS_MODIFIED;
                else {
                        /* This is a "no-change" entry and should not
@@ -4449,7 +4452,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
 }
 
 /* returns 0 upon success, and writes result into sha1 */
-static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
+static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
@@ -4484,9 +4487,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
 
                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");
 
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
@@ -4521,10 +4521,19 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                                        len2, p->two->path);
                git_SHA1_Update(&ctx, buffer, len1);
 
+               if (diff_header_only)
+                       continue;
+
+               if (fill_mmfile(&mf1, p->one) < 0 ||
+                   fill_mmfile(&mf2, p->two) < 0)
+                       return error("unable to read files to diff");
+
                if (diff_filespec_is_binary(p->one) ||
                    diff_filespec_is_binary(p->two)) {
-                       git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
-                       git_SHA1_Update(&ctx, sha1_to_hex(p->two->sha1), 40);
+                       git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
+                                       40);
+                       git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
+                                       40);
                        continue;
                }
 
@@ -4541,11 +4550,11 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
        return 0;
 }
 
-int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
+int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
-       int result = diff_get_patch_id(options, sha1);
+       int result = diff_get_patch_id(options, sha1, diff_header_only);
 
        for (i = 0; i < q->nr; i++)
                diff_free_filepair(q->queue[i]);
@@ -4816,7 +4825,7 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
         */
        if (!DIFF_FILE_VALID(p->one) || /* (1) */
            !DIFF_FILE_VALID(p->two) ||
-           (p->one->sha1_valid && p->two->sha1_valid) ||
+           (p->one->oid_valid && p->two->oid_valid) ||
            (p->one->mode != p->two->mode) ||
            diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
            diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
@@ -5112,8 +5121,9 @@ size_t fill_textconv(struct userdiff_driver *driver,
        if (!driver->textconv)
                die("BUG: fill_textconv called with non-textconv driver");
 
-       if (driver->textconv_cache && df->sha1_valid) {
-               *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+       if (driver->textconv_cache && df->oid_valid) {
+               *outbuf = notes_cache_get(driver->textconv_cache,
+                                         df->oid.hash,
                                          &size);
                if (*outbuf)
                        return size;
@@ -5123,9 +5133,9 @@ size_t fill_textconv(struct userdiff_driver *driver,
        if (!*outbuf)
                die("unable to read files to diff");
 
-       if (driver->textconv_cache && df->sha1_valid) {
+       if (driver->textconv_cache && df->oid_valid) {
                /* ignore errors, as we might be in a readonly repository */
-               notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
+               notes_cache_put(driver->textconv_cache, df->oid.hash, *outbuf,
                                size);
                /*
                 * we could save up changes and flush them all at the end,