]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/log.c
Merge branch 'km/submodule-doc-use-sm-path' into maint
[thirdparty/git.git] / builtin / log.c
index 1f0405f72be8d01bdb3f5d8c54ff455ecb4b1a89..83a4a6188e221caefc5028e59cb7c95c2f1d1e0c 100644 (file)
@@ -37,6 +37,7 @@
 #include "range-diff.h"
 
 #define MAIL_DEFAULT_WRAP 72
+#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -47,7 +48,7 @@ static int default_follow;
 static int default_show_signature;
 static int decoration_style;
 static int decoration_given;
-static int use_mailmap_config;
+static int use_mailmap_config = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
@@ -63,9 +64,14 @@ struct line_opt_callback_data {
        struct string_list args;
 };
 
+static int session_is_interactive(void)
+{
+       return isatty(1) || pager_in_use();
+}
+
 static int auto_decoration_style(void)
 {
-       return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
+       return session_is_interactive() ? DECORATE_SHORT_REFS : 0;
 }
 
 static int parse_decoration_style(const char *value)
@@ -155,7 +161,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                         struct rev_info *rev, struct setup_revision_opt *opt)
 {
        struct userformat_want w;
-       int quiet = 0, source = 0, mailmap = 0;
+       int quiet = 0, source = 0, mailmap;
        static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
        static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
        static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
@@ -622,6 +628,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                        break;
                case OBJ_TAG: {
                        struct tag *t = (struct tag *)o;
+                       struct object_id *oid = get_tagged_oid(t);
 
                        if (rev.shown_one)
                                putchar('\n');
@@ -633,10 +640,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                        rev.shown_one = 1;
                        if (ret)
                                break;
-                       o = parse_object(the_repository, &t->tagged->oid);
+                       o = parse_object(the_repository, oid);
                        if (!o)
                                ret = error(_("could not read object %s"),
-                                           oid_to_hex(&t->tagged->oid));
+                                           oid_to_hex(oid));
                        objects[i].item = o;
                        i--;
                        break;
@@ -759,28 +766,56 @@ static void add_header(const char *value)
        item->string[len] = '\0';
 }
 
-#define THREAD_SHALLOW 1
-#define THREAD_DEEP 2
-static int thread;
+enum cover_setting {
+       COVER_UNSET,
+       COVER_OFF,
+       COVER_ON,
+       COVER_AUTO
+};
+
+enum thread_level {
+       THREAD_UNSET,
+       THREAD_SHALLOW,
+       THREAD_DEEP
+};
+
+enum cover_from_description {
+       COVER_FROM_NONE,
+       COVER_FROM_MESSAGE,
+       COVER_FROM_SUBJECT,
+       COVER_FROM_AUTO
+};
+
+static enum thread_level thread;
 static int do_signoff;
 static int base_auto;
 static char *from;
 static const char *signature = git_version_string;
 static const char *signature_file;
-static int config_cover_letter;
+static enum cover_setting config_cover_letter;
 static const char *config_output_directory;
+static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
+static int show_notes;
+static struct display_notes_opt notes_opt;
 
-enum {
-       COVER_UNSET,
-       COVER_OFF,
-       COVER_ON,
-       COVER_AUTO
-};
+static enum cover_from_description parse_cover_from_description(const char *arg)
+{
+       if (!arg || !strcmp(arg, "default"))
+               return COVER_FROM_MESSAGE;
+       else if (!strcmp(arg, "none"))
+               return COVER_FROM_NONE;
+       else if (!strcmp(arg, "message"))
+               return COVER_FROM_MESSAGE;
+       else if (!strcmp(arg, "subject"))
+               return COVER_FROM_SUBJECT;
+       else if (!strcmp(arg, "auto"))
+               return COVER_FROM_AUTO;
+       else
+               die(_("%s: invalid cover from description mode"), arg);
+}
 
 static int git_format_config(const char *var, const char *value, void *cb)
 {
-       struct rev_info *rev = cb;
-
        if (!strcmp(var, "format.headers")) {
                if (!value)
                        die(_("format.headers without value"));
@@ -830,7 +865,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
                        thread = THREAD_SHALLOW;
                        return 0;
                }
-               thread = git_config_bool(var, value) && THREAD_SHALLOW;
+               thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
                return 0;
        }
        if (!strcmp(var, "format.signoff")) {
@@ -868,7 +903,16 @@ static int git_format_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "format.notes")) {
                int b = git_parse_maybe_bool(value);
-               rev->show_notes = set_display_notes(&rev->notes_opt, b, b < 0 ? value : NULL);
+               if (b < 0)
+                       enable_ref_display_notes(&notes_opt, &show_notes, value);
+               else if (b)
+                       enable_default_display_notes(&notes_opt, &show_notes);
+               else
+                       disable_display_notes(&notes_opt, &show_notes);
+               return 0;
+       }
+       if (!strcmp(var, "format.coverfromdescription")) {
+               cover_from_description_mode = parse_cover_from_description(value);
                return 0;
        }
 
@@ -977,20 +1021,6 @@ static void print_signature(FILE *file)
        putc('\n', file);
 }
 
-static void add_branch_description(struct strbuf *buf, const char *branch_name)
-{
-       struct strbuf desc = STRBUF_INIT;
-       if (!branch_name || !*branch_name)
-               return;
-       read_branch_desc(&desc, branch_name);
-       if (desc.len) {
-               strbuf_addch(buf, '\n');
-               strbuf_addbuf(buf, &desc);
-               strbuf_addch(buf, '\n');
-       }
-       strbuf_release(&desc);
-}
-
 static char *find_branch_name(struct rev_info *rev)
 {
        int i, positive = -1;
@@ -1037,6 +1067,63 @@ static void show_diffstat(struct rev_info *rev,
        fprintf(rev->diffopt.file, "\n");
 }
 
+static void prepare_cover_text(struct pretty_print_context *pp,
+                              const char *branch_name,
+                              struct strbuf *sb,
+                              const char *encoding,
+                              int need_8bit_cte)
+{
+       const char *subject = "*** SUBJECT HERE ***";
+       const char *body = "*** BLURB HERE ***";
+       struct strbuf description_sb = STRBUF_INIT;
+       struct strbuf subject_sb = STRBUF_INIT;
+
+       if (cover_from_description_mode == COVER_FROM_NONE)
+               goto do_pp;
+
+       if (branch_name && *branch_name)
+               read_branch_desc(&description_sb, branch_name);
+       if (!description_sb.len)
+               goto do_pp;
+
+       if (cover_from_description_mode == COVER_FROM_SUBJECT ||
+                       cover_from_description_mode == COVER_FROM_AUTO)
+               body = format_subject(&subject_sb, description_sb.buf, " ");
+
+       if (cover_from_description_mode == COVER_FROM_MESSAGE ||
+                       (cover_from_description_mode == COVER_FROM_AUTO &&
+                        subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
+               body = description_sb.buf;
+       else
+               subject = subject_sb.buf;
+
+do_pp:
+       pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
+       pp_remainder(pp, &body, sb, 0);
+
+       strbuf_release(&description_sb);
+       strbuf_release(&subject_sb);
+}
+
+static int get_notes_refs(struct string_list_item *item, void *arg)
+{
+       argv_array_pushf(arg, "--notes=%s", item->string);
+       return 0;
+}
+
+static void get_notes_args(struct argv_array *arg, struct rev_info *rev)
+{
+       if (!rev->show_notes) {
+               argv_array_push(arg, "--no-notes");
+       } else if (rev->notes_opt.use_default_notes > 0 ||
+                  (rev->notes_opt.use_default_notes == -1 &&
+                   !rev->notes_opt.extra_notes_refs.nr)) {
+               argv_array_push(arg, "--notes");
+       } else {
+               for_each_string_list(&rev->notes_opt.extra_notes_refs, get_notes_refs, arg);
+       }
+}
+
 static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              struct commit *origin,
                              int nr, struct commit **list,
@@ -1044,8 +1131,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int quiet)
 {
        const char *committer;
-       const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
-       const char *msg;
        struct shortlog log;
        struct strbuf sb = STRBUF_INIT;
        int i;
@@ -1075,15 +1160,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        if (!branch_name)
                branch_name = find_branch_name(rev);
 
-       msg = body;
        pp.fmt = CMIT_FMT_EMAIL;
        pp.date_mode.type = DATE_RFC2822;
        pp.rev = rev;
        pp.print_email_subject = 1;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
-       pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
-       pp_remainder(&pp, &msg, &sb, 0);
-       add_branch_description(&sb, branch_name);
+       prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);
        fprintf(rev->diffopt.file, "%s\n", sb.buf);
 
        strbuf_release(&sb);
@@ -1114,13 +1196,16 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                 * can be added later if deemed desirable.
                 */
                struct diff_options opts;
+               struct argv_array other_arg = ARGV_ARRAY_INIT;
                diff_setup(&opts);
                opts.file = rev->diffopt.file;
                opts.use_color = rev->diffopt.use_color;
                diff_setup_done(&opts);
                fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title);
+               get_notes_args(&other_arg, rev);
                show_range_diff(rev->rdiff1, rev->rdiff2,
-                               rev->creation_factor, 1, &opts);
+                               rev->creation_factor, 1, &opts, &other_arg);
+               argv_array_clear(&other_arg);
        }
 }
 
@@ -1232,9 +1317,9 @@ static int output_directory_callback(const struct option *opt, const char *arg,
 
 static int thread_callback(const struct option *opt, const char *arg, int unset)
 {
-       int *thread = (int *)opt->value;
+       enum thread_level *thread = (enum thread_level *)opt->value;
        if (unset)
-               *thread = 0;
+               *thread = THREAD_UNSET;
        else if (!arg || !strcmp(arg, "shallow"))
                *thread = THREAD_SHALLOW;
        else if (!strcmp(arg, "deep"))
@@ -1281,7 +1366,7 @@ static int header_callback(const struct option *opt, const char *arg, int unset)
                string_list_clear(&extra_to, 0);
                string_list_clear(&extra_cc, 0);
        } else {
-           add_header(arg);
+               add_header(arg);
        }
        return 0;
 }
@@ -1337,7 +1422,7 @@ static struct commit *get_base_commit(const char *base_commit,
                base = lookup_commit_reference_by_name(base_commit);
                if (!base)
                        die(_("unknown commit %s"), base_commit);
-       } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
+       } else if ((base_commit && !strcmp(base_commit, "auto"))) {
                struct branch *curr_branch = branch_get(NULL);
                const char *upstream = branch_get_upstream(curr_branch, NULL);
                if (upstream) {
@@ -1442,7 +1527,7 @@ static void prepare_bases(struct base_tree_info *bases,
                struct object_id *patch_id;
                if (*commit_base_at(&commit_base, commit))
                        continue;
-               if (commit_patch_id(commit, &diffopt, &oid, 0))
+               if (commit_patch_id(commit, &diffopt, &oid, 0, 1))
                        die(_("cannot get patch id"));
                ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
                patch_id = bases->patch_id + bases->nr_patch_id;
@@ -1525,6 +1610,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int use_patch_format = 0;
        int quiet = 0;
        int reroll_count = -1;
+       char *cover_from_description_arg = NULL;
        char *branch_name = NULL;
        char *base_commit = NULL;
        struct base_tree_info bases;
@@ -1561,6 +1647,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK, 0, "rfc", &rev, NULL,
                            N_("Use [RFC PATCH] instead of [PATCH]"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
+               OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
+                           N_("cover-from-description-mode"),
+                           N_("generate parts of a cover letter based on a branch's description")),
                { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
                            N_("Use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback },
@@ -1624,8 +1713,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        extra_to.strdup_strings = 1;
        extra_cc.strdup_strings = 1;
        init_log_defaults();
+       init_display_notes(&notes_opt);
+       git_config(git_format_config, NULL);
        repo_init_revisions(the_repository, &rev, prefix);
-       git_config(git_format_config, &rev);
+       rev.show_notes = show_notes;
+       memcpy(&rev.notes_opt, &notes_opt, sizeof(notes_opt));
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.expand_tabs_in_log_default = 0;
        rev.verbose_header = 1;
@@ -1637,6 +1729,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        s_r_opt.def = "HEAD";
        s_r_opt.revarg_opt = REVARG_COMMITTISH;
 
+       if (base_auto)
+               base_commit = "auto";
+
        if (default_attach) {
                rev.mime_boundary = default_attach;
                rev.no_inline = 1;
@@ -1652,6 +1747,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
                             PARSE_OPT_KEEP_DASHDASH);
 
+       if (cover_from_description_arg)
+               cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
+
        if (0 < reroll_count) {
                struct strbuf sprefix = STRBUF_INIT;
                strbuf_addf(&sprefix, "%s v%d",
@@ -1749,10 +1847,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                setup_pager();
 
        if (output_directory) {
+               int saved;
                if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
                        rev.diffopt.use_color = GIT_COLOR_NEVER;
                if (use_stdout)
                        die(_("standard output, or directory, which one?"));
+               /*
+                * We consider <outdir> as 'outside of gitdir', therefore avoid
+                * applying adjust_shared_perm in s-c-l-d.
+                */
+               saved = get_shared_repository();
+               set_shared_repository(0);
+               switch (safe_create_leading_directories_const(output_directory)) {
+               case SCLD_OK:
+               case SCLD_EXISTS:
+                       break;
+               default:
+                       die(_("could not create leading directories "
+                             "of '%s'"), output_directory);
+               }
+               set_shared_repository(saved);
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
                        die_errno(_("could not create directory '%s'"),
                                  output_directory);
@@ -1881,7 +1995,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        }
 
        memset(&bases, 0, sizeof(bases));
-       if (base_commit || base_auto) {
+       if (base_commit) {
                struct commit *base = get_base_commit(base_commit, list, nr);
                reset_revision_walk();
                clear_object_flags(UNINTERESTING);