Merge branch 'tr/format-patch-thread'
authorJunio C Hamano <gitster@pobox.com>
Wed, 11 Mar 2009 20:48:07 +0000 (13:48 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 11 Mar 2009 20:48:07 +0000 (13:48 -0700)
* tr/format-patch-thread:
format-patch: support deep threading
format-patch: thread as reply to cover letter even with in-reply-to
format-patch: track several references
format-patch: threading test reactivation

Conflicts:
builtin-log.c

1  2 
Documentation/git-format-patch.txt
builtin-log.c
log-tree.c
index e7ae8cf109003f0dc43c448c40e5fb09a480f912,4302b1490be3944c9ee4a4479fe0fd940b88a75e..c14e3ee395f465c761bcdcd71e49807286f08742
@@@ -10,8 -10,7 +10,8 @@@ SYNOPSI
  --------
  [verse]
  'git format-patch' [-k] [-o <dir> | --stdout] [--thread]
 -                 [--attach[=<boundary>] | --inline[=<boundary>]]
 +                 [--attach[=<boundary>] | --inline[=<boundary>] |
 +                   [--no-attach]]
                   [-s | --signoff] [<common diff options>]
                   [-n | --numbered | -N | --no-numbered]
                   [--start-number <n>] [--numbered-files]
@@@ -118,19 -117,23 +118,27 @@@ include::diff-options.txt[
        which is the commit message and the patch itself in the
        second part, with "Content-Disposition: attachment".
  
 +--no-attach::
 +      Disable the creation of an attachment, overriding the
 +      configuration setting.
 +
  --inline[=<boundary>]::
        Create multipart/mixed attachment, the first part of
        which is the commit message and the patch itself in the
        second part, with "Content-Disposition: inline".
  
- --thread::
+ --thread[=<style>]::
        Add In-Reply-To and References headers to make the second and
        subsequent mails appear as replies to the first.  Also generates
        the Message-Id header to reference.
+ +
+ The optional <style> argument can be either `shallow` or `deep`.
+ 'Shallow' threading makes every mail a reply to the head of the
+ series, where the head is chosen from the cover letter, the
+ `\--in-reply-to`, and the first patch mail, in this order.  'Deep'
+ threading makes every mail a reply to the previous one.  If not
+ specified, defaults to the 'format.thread' configuration, or `shallow`
+ if that is not set.
  
  --in-reply-to=Message-Id::
        Make the first mail (or all the mails with --no-thread) appear as a
@@@ -179,8 -182,7 +187,8 @@@ CONFIGURATIO
  -------------
  You can specify extra mail header lines to be added to each message
  in the repository configuration, new defaults for the subject prefix
 -and file suffix, and number patches when outputting more than one.
 +and file suffix, control attachements, and number patches when outputting
 +more than one.
  
  ------------
  [format]
        suffix = .txt
        numbered = auto
        cc = <email>
 +      attach [ = mime-boundary-string ]
  ------------
  
  
diff --combined builtin-log.c
index 8549028817ac1a4add0a224ac2996bcbea9a6455,6bf04e8afe24c242704465c31b3a979306be176b..8684fcdb67fc0216ffc860f60f5c060f10907b83
@@@ -17,6 -17,7 +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,8 -429,6 +429,8 @@@ static const char *fmt_patch_suffix = "
  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;
@@@ -461,6 -460,10 +462,10 @@@ static void add_header(const char *valu
        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")) {
                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);
  }
@@@ -776,7 -783,6 +792,6 @@@ int cmd_format_patch(int argc, const ch
        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;
  
        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
                        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;
                }
                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")) {
                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");
                        /* 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));
                }
diff --combined log-tree.c
index 63cff74350521cf4cc86cdc253cd95a036fa1c75,a315ebb78fcf6fccca444f678cbd6977e5e7fcbc..9565c184db681ac1de905d33dbaf175ec61c48fc
@@@ -6,6 -6,7 +6,7 @@@
  #include "log-tree.h"
  #include "reflog-walk.h"
  #include "refs.h"
+ #include "string-list.h"
  
  struct decoration name_decoration = { "object names" };
  
@@@ -79,18 -80,18 +80,18 @@@ void show_decorations(struct rev_info *
   */
  static int detect_any_signoff(char *letter, int size)
  {
 -      char ch, *cp;
 +      char *cp;
        int seen_colon = 0;
        int seen_at = 0;
        int seen_name = 0;
        int seen_head = 0;
  
        cp = letter + size;
 -      while (letter <= --cp && (ch = *cp) == '\n')
 +      while (letter <= --cp && *cp == '\n')
                continue;
  
        while (letter <= cp) {
 -              ch = *cp--;
 +              char ch = *cp--;
                if (ch == '\n')
                        break;
  
@@@ -211,9 -212,13 +212,13 @@@ void log_write_email_headers(struct rev
                printf("Message-Id: <%s>\n", opt->message_id);
                graph_show_oneline(opt->graph);
        }
-       if (opt->ref_message_id) {
-               printf("In-Reply-To: <%s>\nReferences: <%s>\n",
-                      opt->ref_message_id, opt->ref_message_id);
+       if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
+               int i, n;
+               n = opt->ref_message_ids->nr;
+               printf("In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
+               for (i = 0; i < n; i++)
+                       printf("%s<%s>\n", (i > 0 ? "\t" : "References: "),
+                              opt->ref_message_ids->items[i].string);
                graph_show_oneline(opt->graph);
        }
        if (opt->mime_boundary) {