t3422: new testcases for checking when incompatible options passed
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 915f4d6e9f8e4ce203221ebfe2ae9e6dc78b22c3..136d44b45560d5c9db7e88a1ff22aa6b086108e8 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -177,7 +177,7 @@ static int parse_submodule_params(struct diff_options *options, const char *valu
        return 0;
 }
 
-static int git_config_rename(const char *var, const char *value)
+int git_config_rename(const char *var, const char *value)
 {
        if (!value)
                return DIFF_DETECT_RENAME;
@@ -1184,7 +1184,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
                fputs(o->stat_sep, o->file);
                break;
        default:
-               die("BUG: unknown diff symbol");
+               BUG("unknown diff symbol");
        }
        strbuf_release(&sb);
 }
@@ -1343,7 +1343,7 @@ static struct diff_tempfile *claim_diff_tempfile(void) {
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
                if (!diff_temp[i].name)
                        return diff_temp + i;
-       die("BUG: diff is failing to clean up its tempfiles");
+       BUG("diff is failing to clean up its tempfiles");
 }
 
 static void remove_tempfile(void)
@@ -2045,11 +2045,10 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        }
 }
 
-static char *pprint_rename(const char *a, const char *b)
+static void pprint_rename(struct strbuf *name, const char *a, const char *b)
 {
        const char *old_name = a;
        const char *new_name = b;
-       struct strbuf name = STRBUF_INIT;
        int pfx_length, sfx_length;
        int pfx_adjust_for_slash;
        int len_a = strlen(a);
@@ -2059,10 +2058,10 @@ static char *pprint_rename(const char *a, const char *b)
        int qlen_b = quote_c_style(b, NULL, NULL, 0);
 
        if (qlen_a || qlen_b) {
-               quote_c_style(a, &name, NULL, 0);
-               strbuf_addstr(&name, " => ");
-               quote_c_style(b, &name, NULL, 0);
-               return strbuf_detach(&name, NULL);
+               quote_c_style(a, name, NULL, 0);
+               strbuf_addstr(name, " => ");
+               quote_c_style(b, name, NULL, 0);
+               return;
        }
 
        /* Find common prefix */
@@ -2109,19 +2108,18 @@ static char *pprint_rename(const char *a, const char *b)
        if (b_midlen < 0)
                b_midlen = 0;
 
-       strbuf_grow(&name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
+       strbuf_grow(name, pfx_length + a_midlen + b_midlen + sfx_length + 7);
        if (pfx_length + sfx_length) {
-               strbuf_add(&name, a, pfx_length);
-               strbuf_addch(&name, '{');
+               strbuf_add(name, a, pfx_length);
+               strbuf_addch(name, '{');
        }
-       strbuf_add(&name, a + pfx_length, a_midlen);
-       strbuf_addstr(&name, " => ");
-       strbuf_add(&name, b + pfx_length, b_midlen);
+       strbuf_add(name, a + pfx_length, a_midlen);
+       strbuf_addstr(name, " => ");
+       strbuf_add(name, b + pfx_length, b_midlen);
        if (pfx_length + sfx_length) {
-               strbuf_addch(&name, '}');
-               strbuf_add(&name, a + len_a - sfx_length, sfx_length);
+               strbuf_addch(name, '}');
+               strbuf_add(name, a + len_a - sfx_length, sfx_length);
        }
-       return strbuf_detach(&name, NULL);
 }
 
 struct diffstat_t {
@@ -2131,6 +2129,7 @@ struct diffstat_t {
                char *from_name;
                char *name;
                char *print_name;
+               const char *comments;
                unsigned is_unmerged:1;
                unsigned is_binary:1;
                unsigned is_renamed:1;
@@ -2197,23 +2196,20 @@ static void show_graph(struct strbuf *out, char ch, int cnt,
 
 static void fill_print_name(struct diffstat_file *file)
 {
-       char *pname;
+       struct strbuf pname = STRBUF_INIT;
 
        if (file->print_name)
                return;
 
-       if (!file->is_renamed) {
-               struct strbuf buf = STRBUF_INIT;
-               if (quote_c_style(file->name, &buf, NULL, 0)) {
-                       pname = strbuf_detach(&buf, NULL);
-               } else {
-                       pname = file->name;
-                       strbuf_release(&buf);
-               }
-       } else {
-               pname = pprint_rename(file->from_name, file->name);
-       }
-       file->print_name = pname;
+       if (file->is_renamed)
+               pprint_rename(&pname, file->from_name, file->name);
+       else
+               quote_c_style(file->name, &pname, NULL, 0);
+
+       if (file->comments)
+               strbuf_addf(&pname, " (%s)", file->comments);
+
+       file->print_name = strbuf_detach(&pname, NULL);
 }
 
 static void print_stat_summary_inserts_deletes(struct diff_options *options,
@@ -2797,8 +2793,7 @@ static void free_diffstat_info(struct diffstat_t *diffstat)
        int i;
        for (i = 0; i < diffstat->nr; i++) {
                struct diffstat_file *f = diffstat->files[i];
-               if (f->name != f->print_name)
-                       free(f->print_name);
+               free(f->print_name);
                free(f->name);
                free(f->from_name);
                free(f);
@@ -3248,6 +3243,32 @@ static void builtin_diff(const char *name_a,
        return;
 }
 
+static char *get_compact_summary(const struct diff_filepair *p, int is_renamed)
+{
+       if (!is_renamed) {
+               if (p->status == DIFF_STATUS_ADDED) {
+                       if (S_ISLNK(p->two->mode))
+                               return "new +l";
+                       else if ((p->two->mode & 0777) == 0755)
+                               return "new +x";
+                       else
+                               return "new";
+               } else if (p->status == DIFF_STATUS_DELETED)
+                       return "gone";
+       }
+       if (S_ISLNK(p->one->mode) && !S_ISLNK(p->two->mode))
+               return "mode -l";
+       else if (!S_ISLNK(p->one->mode) && S_ISLNK(p->two->mode))
+               return "mode +l";
+       else if ((p->one->mode & 0777) == 0644 &&
+                (p->two->mode & 0777) == 0755)
+               return "mode +x";
+       else if ((p->one->mode & 0777) == 0755 &&
+                (p->two->mode & 0777) == 0644)
+               return "mode -x";
+       return NULL;
+}
+
 static void builtin_diffstat(const char *name_a, const char *name_b,
                             struct diff_filespec *one,
                             struct diff_filespec *two,
@@ -3267,6 +3288,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 
        data = diffstat_add(diffstat, name_a, name_b);
        data->is_interesting = p->status != DIFF_STATUS_UNKNOWN;
+       if (o->flags.stat_with_summary)
+               data->comments = get_compact_summary(p, data->is_renamed);
 
        if (!one || !two) {
                data->is_unmerged = 1;
@@ -3449,7 +3472,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in
         * objects however would tend to be slower as they need
         * to be individually opened and inflated.
         */
-       if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(oid->hash))
+       if (!FAST_WORKING_DIRECTORY && !want_file && has_object_pack(oid))
                return 0;
 
        /*
@@ -3615,7 +3638,8 @@ 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->oid.hash, &s->size);
+                       type = oid_object_info(the_repository, &s->oid,
+                                              &s->size);
                        if (type < 0)
                                die("unable to read %s",
                                    oid_to_hex(&s->oid));
@@ -3626,7 +3650,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                                return 0;
                        }
                }
-               s->data = read_sha1_file(s->oid.hash, &type, &s->size);
+               s->data = read_object_file(&s->oid, &type, &s->size);
                if (!s->data)
                        die("unable to read %s", oid_to_hex(&s->oid));
                s->should_free = 1;
@@ -3811,13 +3835,13 @@ static int similarity_index(struct diff_filepair *p)
 static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
 {
        if (startup_info->have_repository)
-               return find_unique_abbrev(oid->hash, abbrev);
+               return find_unique_abbrev(oid, abbrev);
        else {
                char *hex = oid_to_hex(oid);
                if (abbrev < 0)
                        abbrev = FALLBACK_DEFAULT_ABBREV;
                if (abbrev > GIT_SHA1_HEXSZ)
-                       die("BUG: oid abbreviation out of range: %d", abbrev);
+                       BUG("oid abbreviation out of range: %d", abbrev);
                if (abbrev)
                        hex[abbrev] = '\0';
                return hex;
@@ -3874,13 +3898,14 @@ static void fill_metainfo(struct strbuf *msg,
                *must_show_header = 0;
        }
        if (one && two && oidcmp(&one->oid, &two->oid)) {
-               int abbrev = o->flags.full_index ? 40 : DEFAULT_ABBREV;
+               const unsigned hexsz = the_hash_algo->hexsz;
+               int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV;
 
                if (o->flags.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;
+                               abbrev = hexsz;
                }
                strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
                            diff_abbrev_oid(&one->oid, abbrev),
@@ -4115,6 +4140,11 @@ void diff_setup_done(struct diff_options *options)
                              DIFF_FORMAT_NAME_STATUS |
                              DIFF_FORMAT_CHECKDIFF |
                              DIFF_FORMAT_NO_OUTPUT;
+       /*
+        * This must be signed because we're comparing against a potentially
+        * negative value.
+        */
+       const int hexsz = the_hash_algo->hexsz;
 
        if (options->set_default)
                options->set_default(options);
@@ -4195,8 +4225,8 @@ void diff_setup_done(struct diff_options *options)
                         */
                        read_cache();
        }
-       if (40 < options->abbrev)
-               options->abbrev = 40; /* full */
+       if (hexsz < options->abbrev)
+               options->abbrev = hexsz; /* full */
 
        /*
         * It does not make sense to show the first hit we happened
@@ -4311,7 +4341,7 @@ static int stat_opt(struct diff_options *options, const char **av)
        int argcount = 1;
 
        if (!skip_prefix(arg, "--stat", &arg))
-               die("BUG: stat option does not begin with --stat: %s", arg);
+               BUG("stat option does not begin with --stat: %s", arg);
        end = (char *)arg;
 
        switch (*arg) {
@@ -4553,6 +4583,11 @@ int diff_opt_parse(struct diff_options *options,
        else if (starts_with(arg, "--stat"))
                /* --stat, --stat-width, --stat-name-width, or --stat-count */
                return stat_opt(options, av);
+       else if (!strcmp(arg, "--compact-summary")) {
+                options->flags.stat_with_summary = 1;
+                options->output_format |= DIFF_FORMAT_DIFFSTAT;
+       } else if (!strcmp(arg, "--no-compact-summary"))
+                options->flags.stat_with_summary = 0;
 
        /* renames options */
        else if (starts_with(arg, "-B") ||
@@ -4769,8 +4804,8 @@ int diff_opt_parse(struct diff_options *options,
                options->abbrev = strtoul(arg, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
-               else if (40 < options->abbrev)
-                       options->abbrev = 40;
+               else if (the_hash_algo->hexsz < options->abbrev)
+                       options->abbrev = the_hash_algo->hexsz;
        }
        else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
                options->a_prefix = optarg;
@@ -5241,10 +5276,12 @@ static void show_rename_copy(struct diff_options *opt, const char *renamecopy,
                struct diff_filepair *p)
 {
        struct strbuf sb = STRBUF_INIT;
-       char *names = pprint_rename(p->one->path, p->two->path);
+       struct strbuf names = STRBUF_INIT;
+
+       pprint_rename(&names, p->one->path, p->two->path);
        strbuf_addf(&sb, " %s %s (%d%%)\n",
-                       renamecopy, names, similarity_index(p));
-       free(names);
+                   renamecopy, names.buf, similarity_index(p));
+       strbuf_release(&names);
        emit_diff_symbol(opt, DIFF_SYMBOL_SUMMARY,
                                 sb.buf, sb.len, 0);
        show_mode_change(opt, p, 0);
@@ -5489,7 +5526,7 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
        struct diff_queue_struct *q = &diff_queued_diff;
 
        if (WSEH_NEW & WS_RULE_MASK)
-               die("BUG: WS rules bit mask overlaps with diff symbol flags");
+               BUG("WS rules bit mask overlaps with diff symbol flags");
 
        if (o->color_moved)
                o->emitted_symbols = &esm;
@@ -6023,7 +6060,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
        }
 
        if (!driver->textconv)
-               die("BUG: fill_textconv called with non-textconv driver");
+               BUG("fill_textconv called with non-textconv driver");
 
        if (driver->textconv_cache && df->oid_valid) {
                *outbuf = notes_cache_get(driver->textconv_cache,