string_list: Add STRING_LIST_INIT macro and make use of it.
[gitweb.git] / builtin / log.c
index 9fbc7ab9cee8176ff3e85ec28995d2cd8d992d31..08b872263cd646ec9d7dc8ad91273c71b1af2b69 100644 (file)
@@ -24,6 +24,7 @@
 static const char *default_date_mode = NULL;
 
 static int default_show_root = 1;
+static int decoration_style;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
@@ -31,11 +32,29 @@ static const char * const builtin_log_usage =
        "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
        "   or: git show [options] <object>...";
 
+static int parse_decoration_style(const char *var, const char *value)
+{
+       switch (git_config_maybe_bool(var, value)) {
+       case 1:
+               return DECORATE_SHORT_REFS;
+       case 0:
+               return 0;
+       default:
+               break;
+       }
+       if (!strcmp(value, "full"))
+               return DECORATE_FULL_REFS;
+       else if (!strcmp(value, "short"))
+               return DECORATE_SHORT_REFS;
+       return -1;
+}
+
 static void cmd_log_init(int argc, const char **argv, const char *prefix,
-                     struct rev_info *rev)
+                        struct rev_info *rev, struct setup_revision_opt *opt)
 {
        int i;
-       int decoration_style = 0;
+       int decoration_given = 0;
+       struct userformat_want w;
 
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
@@ -56,10 +75,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
         */
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(builtin_log_usage);
-       argc = setup_revisions(argc, argv, rev, "HEAD");
+       argc = setup_revisions(argc, argv, rev, opt);
 
-       if (!rev->show_notes_given && !rev->pretty_given)
+       memset(&w, 0, sizeof(w));
+       userformat_find_requirements(NULL, &w);
+
+       if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
                rev->show_notes = 1;
+       if (rev->show_notes)
+               init_display_notes(&rev->notes_opt);
 
        if (rev->diffopt.pickaxe || rev->diffopt.filter)
                rev->always_show_header = 0;
@@ -72,14 +96,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                const char *arg = argv[i];
                if (!strcmp(arg, "--decorate")) {
                        decoration_style = DECORATE_SHORT_REFS;
+                       decoration_given = 1;
                } else if (!prefixcmp(arg, "--decorate=")) {
                        const char *v = skip_prefix(arg, "--decorate=");
-                       if (!strcmp(v, "full"))
-                               decoration_style = DECORATE_FULL_REFS;
-                       else if (!strcmp(v, "short"))
-                               decoration_style = DECORATE_SHORT_REFS;
-                       else
+                       decoration_style = parse_decoration_style(arg, v);
+                       if (decoration_style < 0)
                                die("invalid --decorate option: %s", arg);
+                       decoration_given = 1;
+               } else if (!strcmp(arg, "--no-decorate")) {
+                       decoration_style = 0;
                } else if (!strcmp(arg, "--source")) {
                        rev->show_source = 1;
                } else if (!strcmp(arg, "-h")) {
@@ -87,6 +112,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                } else
                        die("unrecognized argument: %s", arg);
        }
+
+       /*
+        * defeat log.decorate configuration interacting with --pretty=raw
+        * from the command line.
+        */
+       if (!decoration_given && rev->pretty_given
+           && rev->commit_format == CMIT_FMT_RAW)
+               decoration_style = 0;
+
        if (decoration_style) {
                rev->show_decorations = 1;
                load_ref_decorations(decoration_style);
@@ -252,16 +286,26 @@ static int git_log_config(const char *var, const char *value, void *cb)
                return git_config_string(&fmt_patch_subject_prefix, var, value);
        if (!strcmp(var, "log.date"))
                return git_config_string(&default_date_mode, var, value);
+       if (!strcmp(var, "log.decorate")) {
+               decoration_style = parse_decoration_style(var, value);
+               if (decoration_style < 0)
+                       decoration_style = 0; /* maybe warn? */
+               return 0;
+       }
        if (!strcmp(var, "log.showroot")) {
                default_show_root = git_config_bool(var, value);
                return 0;
        }
+       if (!prefixcmp(var, "color.decorate."))
+               return parse_decorate_color_config(var, 15, value);
+
        return git_diff_ui_config(var, value, cb);
 }
 
 int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        git_config(git_log_config, NULL);
 
@@ -271,7 +315,9 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.simplify_history = 0;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
        return cmd_log_walk(&rev);
@@ -324,10 +370,26 @@ static int show_tree_object(const unsigned char *sha1,
        return 0;
 }
 
+static void show_rev_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt)
+{
+       if (rev->ignore_merges) {
+               /* There was no "-m" on the command line */
+               rev->ignore_merges = 0;
+               if (!rev->first_parent_only && !rev->combine_merges) {
+                       /* No "--first-parent", "-c", nor "--cc" */
+                       rev->combine_merges = 1;
+                       rev->dense_combined_merges = 1;
+               }
+       }
+       if (!rev->diffopt.output_format)
+               rev->diffopt.output_format = DIFF_FORMAT_PATCH;
+}
+
 int cmd_show(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
        struct object_array_entry *objects;
+       struct setup_revision_opt opt;
        int i, count, ret = 0;
 
        git_config(git_log_config, NULL);
@@ -337,12 +399,12 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 
        init_revisions(&rev, prefix);
        rev.diff = 1;
-       rev.combine_merges = 1;
-       rev.dense_combined_merges = 1;
        rev.always_show_header = 1;
-       rev.ignore_merges = 0;
        rev.no_walk = 1;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       opt.tweak = show_rev_tweak_rev;
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
 
        count = rev.pending.nr;
        objects = rev.pending.objects;
@@ -405,6 +467,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        git_config(git_log_config, NULL);
 
@@ -415,7 +478,9 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        init_reflog_walk(&rev.reflog_info);
        rev.abbrev_commit = 1;
        rev.verbose_header = 1;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
 
        /*
         * This means that we override whatever commit format the user gave
@@ -438,6 +503,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 int cmd_log(int argc, const char **argv, const char *prefix)
 {
        struct rev_info rev;
+       struct setup_revision_opt opt;
 
        git_config(git_log_config, NULL);
 
@@ -446,7 +512,9 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 
        init_revisions(&rev, prefix);
        rev.always_show_header = 1;
-       cmd_log_init(argc, argv, prefix, &rev);
+       memset(&opt, 0, sizeof(opt));
+       opt.def = "HEAD";
+       cmd_log_init(argc, argv, prefix, &rev, &opt);
        return cmd_log_walk(&rev);
 }
 
@@ -470,13 +538,13 @@ static void add_header(const char *value)
                len--;
 
        if (!strncasecmp(value, "to: ", 4)) {
-               item = string_list_append(value + 4, &extra_to);
+               item = string_list_append(&extra_to, value + 4);
                len -= 4;
        } else if (!strncasecmp(value, "cc: ", 4)) {
-               item = string_list_append(value + 4, &extra_cc);
+               item = string_list_append(&extra_cc, value + 4);
                len -= 4;
        } else {
-               item = string_list_append(value, &extra_hdr);
+               item = string_list_append(&extra_hdr, value);
        }
 
        item->string[len] = '\0';
@@ -484,8 +552,9 @@ static void add_header(const char *value)
 
 #define THREAD_SHALLOW 1
 #define THREAD_DEEP 2
-static int thread = 0;
-static int do_signoff = 0;
+static int thread;
+static int do_signoff;
+static const char *signature = git_version_string;
 
 static int git_format_config(const char *var, const char *value, void *cb)
 {
@@ -500,13 +569,13 @@ static int git_format_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "format.to")) {
                if (!value)
                        return config_error_nonbool(var);
-               string_list_append(value, &extra_to);
+               string_list_append(&extra_to, value);
                return 0;
        }
        if (!strcmp(var, "format.cc")) {
                if (!value)
                        return config_error_nonbool(var);
-               string_list_append(value, &extra_cc);
+               string_list_append(&extra_cc, value);
                return 0;
        }
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -544,6 +613,8 @@ static int git_format_config(const char *var, const char *value, void *cb)
                do_signoff = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "format.signature"))
+               return git_config_string(&signature, var, value);
 
        return git_log_config(var, value, cb);
 }
@@ -638,6 +709,12 @@ static void gen_message_id(struct rev_info *info, char *base)
        info->message_id = strbuf_detach(&buf, NULL);
 }
 
+static void print_signature(void)
+{
+       if (signature && *signature)
+               printf("-- \n%s\n\n", signature);
+}
+
 static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int numbered, int numbered_files,
                              struct commit *origin,
@@ -731,6 +808,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        diff_flush(&opts);
 
        printf("\n");
+       print_signature();
 }
 
 static const char *clean_message_id(const char *msg_id)
@@ -884,7 +962,7 @@ static int to_callback(const struct option *opt, const char *arg, int unset)
        if (unset)
                string_list_clear(&extra_to, 0);
        else
-               string_list_append(arg, &extra_to);
+               string_list_append(&extra_to, arg);
        return 0;
 }
 
@@ -893,7 +971,7 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
        if (unset)
                string_list_clear(&extra_cc, 0);
        else
-               string_list_append(arg, &extra_cc);
+               string_list_append(&extra_cc, arg);
        return 0;
 }
 
@@ -902,6 +980,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        struct commit *commit;
        struct commit **list = NULL;
        struct rev_info rev;
+       struct setup_revision_opt s_r_opt;
        int nr = 0, total, i;
        int use_stdout = 0;
        int start_number = -1;
@@ -969,6 +1048,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "thread", &thread, "style",
                            "enable message threading, styles: shallow, deep",
                            PARSE_OPT_OPTARG, thread_callback },
+               OPT_STRING(0, "signature", &signature, "signature",
+                           "add a signature"),
                OPT_END()
        };
 
@@ -983,8 +1064,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.combine_merges = 0;
        rev.ignore_merges = 1;
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
-
        rev.subject_prefix = fmt_patch_subject_prefix;
+       memset(&s_r_opt, 0, sizeof(s_r_opt));
+       s_r_opt.def = "HEAD";
 
        if (default_attach) {
                rev.mime_boundary = default_attach;
@@ -1056,7 +1138,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (keep_subject && subject_prefix)
                die ("--subject-prefix and -k are mutually exclusive.");
 
-       argc = setup_revisions(argc, argv, &rev, "HEAD");
+       argc = setup_revisions(argc, argv, &rev, &s_r_opt);
        if (argc > 1)
                die ("unrecognized argument: %s", argv[1]);
 
@@ -1078,6 +1160,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
                DIFF_OPT_SET(&rev.diffopt, BINARY);
 
+       if (rev.show_notes)
+               init_display_notes(&rev.notes_opt);
+
        if (!use_stdout)
                output_directory = set_outdir(prefix, output_directory);
 
@@ -1125,8 +1210,15 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        return 0;
        }
 
-       if (ignore_if_in_upstream)
+       if (ignore_if_in_upstream) {
+               /* Don't say anything if head and upstream are the same. */
+               if (rev.pending.nr == 2) {
+                       struct object_array_entry *o = rev.pending.objects;
+                       if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+                               return 0;
+               }
                get_patch_ids(&rev, &ids, prefix);
+       }
 
        if (!use_stdout)
                realstdout = xfdopen(xdup(1), "w");
@@ -1162,7 +1254,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
        if (in_reply_to) {
                const char *msgid = clean_message_id(in_reply_to);
-               string_list_append(msgid, rev.ref_message_ids);
+               string_list_append(rev.ref_message_ids, msgid);
        }
        rev.numbered_files = numbered_files;
        rev.patch_suffix = fmt_patch_suffix;
@@ -1209,8 +1301,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                    && (!cover_letter || rev.nr > 1))
                                        free(rev.message_id);
                                else
-                                       string_list_append(rev.message_id,
-                                                          rev.ref_message_ids);
+                                       string_list_append(rev.ref_message_ids,
+                                                          rev.message_id);
                        }
                        gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
                }
@@ -1236,7 +1328,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                       mime_boundary_leader,
                                       rev.mime_boundary);
                        else
-                               printf("-- \n%s\n\n", git_version_string);
+                               print_signature();
                }
                if (!use_stdout)
                        fclose(stdout);
@@ -1264,8 +1356,11 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
        return -1;
 }
 
-static const char cherry_usage[] =
-"git cherry [-v] [<upstream> [<head> [<limit>]]]";
+static const char * const cherry_usage[] = {
+       "git cherry [-v] [<upstream> [<head> [<limit>]]]",
+       NULL
+};
+
 int cmd_cherry(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -1276,26 +1371,25 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
        const char *upstream;
        const char *head = "HEAD";
        const char *limit = NULL;
-       int verbose = 0;
+       int verbose = 0, abbrev = 0;
 
-       if (argc > 1 && !strcmp(argv[1], "-v")) {
-               verbose = 1;
-               argc--;
-               argv++;
-       }
+       struct option options[] = {
+               OPT__ABBREV(&abbrev),
+               OPT__VERBOSE(&verbose),
+               OPT_END()
+       };
 
-       if (argc > 1 && !strcmp(argv[1], "-h"))
-               usage(cherry_usage);
+       argc = parse_options(argc, argv, prefix, options, cherry_usage, 0);
 
        switch (argc) {
-       case 4:
-               limit = argv[3];
-               /* FALLTHROUGH */
        case 3:
-               head = argv[2];
+               limit = argv[2];
                /* FALLTHROUGH */
        case 2:
-               upstream = argv[1];
+               head = argv[1];
+               /* FALLTHROUGH */
+       case 1:
+               upstream = argv[0];
                break;
        default:
                current_branch = branch_get(NULL);
@@ -1305,7 +1399,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                        fprintf(stderr, "Could not find a tracked"
                                        " remote branch, please"
                                        " specify <upstream> manually.\n");
-                       usage(cherry_usage);
+                       usage_with_options(cherry_usage, options);
                }
 
                upstream = current_branch->merge[0]->dst;
@@ -1358,12 +1452,13 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                        pretty_print_commit(CMIT_FMT_ONELINE, commit,
                                            &buf, &ctx);
                        printf("%c %s %s\n", sign,
-                              sha1_to_hex(commit->object.sha1), buf.buf);
+                              find_unique_abbrev(commit->object.sha1, abbrev),
+                              buf.buf);
                        strbuf_release(&buf);
                }
                else {
                        printf("%c %s\n", sign,
-                              sha1_to_hex(commit->object.sha1));
+                              find_unique_abbrev(commit->object.sha1, abbrev));
                }
 
                list = list->next;