builtin-fast-export.c: handle nested tags
[gitweb.git] / builtin-log.c
index 2ae39afccdc1978141fe4cc4453befa632ecaadf..c7a57725948553a955f49ab891aed3fbdd3aa33d 100644 (file)
@@ -17,6 +17,7 @@
 #include "run-command.h"
 #include "shortlog.h"
 #include "remote.h"
+#include "string-list.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -428,6 +429,8 @@ static const char *fmt_patch_suffix = ".patch";
 static int numbered = 0;
 static int auto_number = 1;
 
+static char *default_attach = NULL;
+
 static char **extra_hdr;
 static int extra_hdr_nr;
 static int extra_hdr_alloc;
@@ -459,6 +462,10 @@ static void add_header(const char *value)
        extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
 }
 
+#define THREAD_SHALLOW 1
+#define THREAD_DEEP 2
+static int thread = 0;
+
 static int git_format_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "format.headers")) {
@@ -488,6 +495,25 @@ static int git_format_config(const char *var, const char *value, void *cb)
                auto_number = auto_number && numbered;
                return 0;
        }
+       if (!strcmp(var, "format.attach")) {
+               if (value && *value)
+                       default_attach = xstrdup(value);
+               else
+                       default_attach = xstrdup(git_version_string);
+               return 0;
+       }
+       if (!strcmp(var, "format.thread")) {
+               if (value && !strcasecmp(value, "deep")) {
+                       thread = THREAD_DEEP;
+                       return 0;
+               }
+               if (value && !strcasecmp(value, "shallow")) {
+                       thread = THREAD_SHALLOW;
+                       return 0;
+               }
+               thread = git_config_bool(var, value) && THREAD_SHALLOW;
+               return 0;
+       }
 
        return git_log_config(var, value, cb);
 }
@@ -547,7 +573,7 @@ static FILE *realstdout = NULL;
 static const char *output_directory = NULL;
 static int outdir_offset;
 
-static int reopen_stdout(const char *oneline, int nr, int total)
+static int reopen_stdout(const char *oneline, int nr, struct rev_info *rev)
 {
        char filename[PATH_MAX];
        int len = 0;
@@ -572,7 +598,9 @@ static int reopen_stdout(const char *oneline, int nr, int total)
                strcpy(filename + len, fmt_patch_suffix);
        }
 
-       fprintf(realstdout, "%s\n", filename + outdir_offset);
+       if (!DIFF_OPT_TST(&rev->diffopt, QUIET))
+               fprintf(realstdout, "%s\n", filename + outdir_offset);
+
        if (freopen(filename, "w", stdout) == NULL)
                return error("Cannot open patch file %s",filename);
 
@@ -661,7 +689,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
                die("Cover letter needs email format");
 
        if (!use_stdout && reopen_stdout(numbered_files ?
-                               NULL : "cover-letter", 0, rev->total))
+                               NULL : "cover-letter", 0, rev))
                return;
 
        head_sha1 = sha1_to_hex(head->object.sha1);
@@ -766,7 +794,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int numbered_files = 0;         /* _just_ numbers */
        int subject_prefix = 0;
        int ignore_if_in_upstream = 0;
-       int thread = 0;
        int cover_letter = 0;
        int boundary_count = 0;
        int no_binary_diff = 0;
@@ -787,6 +814,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        rev.subject_prefix = fmt_patch_subject_prefix;
 
+       if (default_attach) {
+               rev.mime_boundary = default_attach;
+               rev.no_inline = 1;
+       }
+
        /*
         * Parse the arguments before setup_revisions(), or something
         * like "git format-patch -o a123 HEAD^.." may fail; a123 is
@@ -849,6 +881,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        rev.mime_boundary = argv[i] + 9;
                        rev.no_inline = 1;
                }
+               else if (!strcmp(argv[i], "--no-attach")) {
+                       rev.mime_boundary = NULL;
+                       rev.no_inline = 0;
+               }
                else if (!strcmp(argv[i], "--inline")) {
                        rev.mime_boundary = git_version_string;
                        rev.no_inline = 0;
@@ -859,8 +895,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                }
                else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
                        ignore_if_in_upstream = 1;
-               else if (!strcmp(argv[i], "--thread"))
-                       thread = 1;
+               else if (!strcmp(argv[i], "--thread")
+                       || !strcmp(argv[i], "--thread=shallow"))
+                       thread = THREAD_SHALLOW;
+               else if (!strcmp(argv[i], "--thread=deep"))
+                       thread = THREAD_DEEP;
+               else if (!strcmp(argv[i], "--no-thread"))
+                       thread = 0;
                else if (!prefixcmp(argv[i], "--in-reply-to="))
                        in_reply_to = argv[i] + 14;
                else if (!strcmp(argv[i], "--in-reply-to")) {
@@ -917,8 +958,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                die ("-n and -k are mutually exclusive.");
        if (keep_subject && subject_prefix)
                die ("--subject-prefix and -k are mutually exclusive.");
-       if (numbered_files && use_stdout)
-               die ("--numbered-files and --stdout are mutually exclusive.");
 
        argc = setup_revisions(argc, argv, &rev, "HEAD");
        if (argc > 1)
@@ -1011,8 +1050,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                numbered = 1;
        if (numbered)
                rev.total = total + start_number - 1;
-       if (in_reply_to)
-               rev.ref_message_id = clean_message_id(in_reply_to);
+       if (in_reply_to || thread || cover_letter)
+               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);
+       }
        if (cover_letter) {
                if (thread)
                        gen_message_id(&rev, "cover");
@@ -1031,21 +1074,39 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        /* Have we already had a message ID? */
                        if (rev.message_id) {
                                /*
-                                * If we've got the ID to be a reply
-                                * to, discard the current ID;
-                                * otherwise, make everything a reply
-                                * to that.
+                                * For deep threading: make every mail
+                                * a reply to the previous one, no
+                                * matter what other options are set.
+                                *
+                                * For shallow threading:
+                                *
+                                * Without --cover-letter and
+                                * --in-reply-to, make every mail a
+                                * reply to the one before.
+                                *
+                                * With --in-reply-to but no
+                                * --cover-letter, make every mail a
+                                * reply to the <reply-to>.
+                                *
+                                * With --cover-letter, make every
+                                * mail but the cover letter a reply
+                                * to the cover letter.  The cover
+                                * letter is a reply to the
+                                * --in-reply-to, if specified.
                                 */
-                               if (rev.ref_message_id)
+                               if (thread == THREAD_SHALLOW
+                                   && rev.ref_message_ids->nr > 0
+                                   && (!cover_letter || rev.nr > 1))
                                        free(rev.message_id);
                                else
-                                       rev.ref_message_id = rev.message_id;
+                                       string_list_append(rev.message_id,
+                                                          rev.ref_message_ids);
                        }
                        gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
                }
                if (!use_stdout && reopen_stdout(numbered_files ? NULL :
                                get_oneline_for_filename(commit, keep_subject),
-                               rev.nr, rev.total))
+                               rev.nr, &rev))
                        die("Failed to create output files");
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);