Merge branch 'jc/mailinfo-remove-brackets'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 Nov 2009 22:43:24 +0000 (14:43 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 Nov 2009 22:43:24 +0000 (14:43 -0800)
Conflicts:
Documentation/git-mailinfo.txt
builtin-mailinfo.c

1  2 
Documentation/git-mailinfo.txt
builtin-mailinfo.c
index 996c3fcc6cde0b7ce1acf6bfb3815bac4dd6176c,d800aea0c113bc88c076fd08f6e8f01dbc38b9e6..b81ac98cf0d95ab961b228e514c8fdabc152a21e
@@@ -8,7 -8,7 +8,7 @@@ git-mailinfo - Extracts patch and autho
  
  SYNOPSIS
  --------
- 'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
 -'git mailinfo' [-k|-b] [-u | --encoding=<encoding> | -n] <msg> <patch>
++'git mailinfo' [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
  
  
  DESCRIPTION
@@@ -32,6 -32,11 +32,11 @@@ OPTION
        munging, and is most useful when used to read back
        'git-format-patch -k' output.
  
+ -b::
+       When -k is not in effect, all leading strings bracketed with '['
+       and ']' pairs are stripped.  This option limits the stripping to
+       only the pairs whose bracketed string contains the word "PATCH".
  -u::
        The commit log message, author name and author email are
        taken from the e-mail, and after minimally decoding MIME
@@@ -49,25 -54,6 +54,25 @@@ conversion, even with this flag
  -n::
        Disable all charset re-coding of the metadata.
  
 +--scissors::
 +      Remove everything in body before a scissors line.  A line that
 +      mainly consists of scissors (either ">8" or "8<") and perforation
 +      (dash "-") marks is called a scissors line, and is used to request
 +      the reader to cut the message at that line.  If such a line
 +      appears in the body of the message before the patch, everything
 +      before it (including the scissors line itself) is ignored when
 +      this option is used.
 ++
 +This is useful if you want to begin your message in a discussion thread
 +with comments and suggestions on the message you are responding to, and to
 +conclude it with a patch submission, separating the discussion and the
 +beginning of the proposed commit log message with a scissors line.
 ++
 +This can enabled by default with the configuration option mailinfo.scissors.
 +
 +--no-scissors::
 +      Ignore scissors lines. Useful for overriding mailinfo.scissors settings.
 +
  <msg>::
        The commit log message extracted from e-mail, usually
        except the title line which comes from e-mail Subject.
diff --combined builtin-mailinfo.c
index 3c4f0753fe157960e206f50fbedb7f63d1a7f47d,a5949e2c9a43b1078accc559726f5a0c1c3eee02..a50ac2256cdbacd76ed44a50804212be07f949db
@@@ -10,6 -10,7 +10,7 @@@
  static FILE *cmitmsg, *patchfile, *fin, *fout;
  
  static int keep_subject;
+ static int keep_non_patch_brackets_in_subject;
  static const char *metainfo_charset;
  static struct strbuf line = STRBUF_INIT;
  static struct strbuf name = STRBUF_INIT;
@@@ -25,8 -26,6 +26,8 @@@ static enum  
  static struct strbuf charset = STRBUF_INIT;
  static int patch_lines;
  static struct strbuf **p_hdr_data, **s_hdr_data;
 +static int use_scissors;
 +static int use_inbody_headers = 1;
  
  #define MAX_HDR_PARSED 10
  #define MAX_BOUNDARIES 5
@@@ -221,35 -220,41 +222,41 @@@ static int is_multipart_boundary(const 
  
  static void cleanup_subject(struct strbuf *subject)
  {
-       char *pos;
-       size_t remove;
-       while (subject->len) {
-               switch (*subject->buf) {
+       size_t at = 0;
+       while (at < subject->len) {
+               char *pos;
+               size_t remove;
+               switch (subject->buf[at]) {
                case 'r': case 'R':
-                       if (subject->len <= 3)
+                       if (subject->len <= at + 3)
                                break;
-                       if (!memcmp(subject->buf + 1, "e:", 2)) {
-                               strbuf_remove(subject, 0, 3);
+                       if (!memcmp(subject->buf + at + 1, "e:", 2)) {
+                               strbuf_remove(subject, at, 3);
                                continue;
                        }
+                       at++;
                        break;
                case ' ': case '\t': case ':':
-                       strbuf_remove(subject, 0, 1);
+                       strbuf_remove(subject, at, 1);
                        continue;
                case '[':
-                       if ((pos = strchr(subject->buf, ']'))) {
-                               remove = pos - subject->buf;
-                               if (remove <= (subject->len - remove) * 2) {
-                                       strbuf_remove(subject, 0, remove + 1);
-                                       continue;
-                               }
-                       } else
-                               strbuf_remove(subject, 0, 1);
-                       break;
+                       pos = strchr(subject->buf + at, ']');
+                       if (!pos)
+                               break;
+                       remove = pos - subject->buf + at + 1;
+                       if (!keep_non_patch_brackets_in_subject ||
+                           (7 <= remove &&
+                            memmem(subject->buf + at, remove, "PATCH", 5)))
+                               strbuf_remove(subject, at, remove);
+                       else
+                               at += remove;
+                       continue;
                }
-               strbuf_trim(subject);
-               return;
+               break;
        }
+       strbuf_trim(subject);
  }
  
  static void cleanup_space(struct strbuf *sb)
@@@ -714,56 -719,6 +721,56 @@@ static inline int patchbreak(const stru
        return 0;
  }
  
 +static int is_scissors_line(const struct strbuf *line)
 +{
 +      size_t i, len = line->len;
 +      int scissors = 0, gap = 0;
 +      int first_nonblank = -1;
 +      int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
 +      const char *buf = line->buf;
 +
 +      for (i = 0; i < len; i++) {
 +              if (isspace(buf[i])) {
 +                      if (in_perforation) {
 +                              perforation++;
 +                              gap++;
 +                      }
 +                      continue;
 +              }
 +              last_nonblank = i;
 +              if (first_nonblank < 0)
 +                      first_nonblank = i;
 +              if (buf[i] == '-') {
 +                      in_perforation = 1;
 +                      perforation++;
 +                      continue;
 +              }
 +              if (i + 1 < len &&
 +                  (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2))) {
 +                      in_perforation = 1;
 +                      perforation += 2;
 +                      scissors += 2;
 +                      i++;
 +                      continue;
 +              }
 +              in_perforation = 0;
 +      }
 +
 +      /*
 +       * The mark must be at least 8 bytes long (e.g. "-- >8 --").
 +       * Even though there can be arbitrary cruft on the same line
 +       * (e.g. "cut here"), in order to avoid misidentification, the
 +       * perforation must occupy more than a third of the visible
 +       * width of the line, and dashes and scissors must occupy more
 +       * than half of the perforation.
 +       */
 +
 +      visible = last_nonblank - first_nonblank + 1;
 +      return (scissors && 8 <= visible &&
 +              visible < perforation * 3 &&
 +              gap * 2 < perforation);
 +}
 +
  static int handle_commit_msg(struct strbuf *line)
  {
        static int still_looking = 1;
                strbuf_ltrim(line);
                if (!line->len)
                        return 0;
 -              if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
 -                      return 0;
        }
  
 +      if (use_inbody_headers && still_looking) {
 +              still_looking = check_header(line, s_hdr_data, 0);
 +              if (still_looking)
 +                      return 0;
 +      } else
 +              /* Only trim the first (blank) line of the commit message
 +               * when ignoring in-body headers.
 +               */
 +              still_looking = 0;
 +
        /* normalize the log message to UTF-8. */
        if (metainfo_charset)
                convert_to_utf8(line, charset.buf);
  
 +      if (use_scissors && is_scissors_line(line)) {
 +              int i;
 +              if (fseek(cmitmsg, 0L, SEEK_SET))
 +                      die_errno("Could not rewind output message file");
 +              if (ftruncate(fileno(cmitmsg), 0))
 +                      die_errno("Could not truncate output message file at scissors");
 +              still_looking = 1;
 +
 +              /*
 +               * We may have already read "secondary headers"; purge
 +               * them to give ourselves a clean restart.
 +               */
 +              for (i = 0; header[i]; i++) {
 +                      if (s_hdr_data[i])
 +                              strbuf_release(s_hdr_data[i]);
 +                      s_hdr_data[i] = NULL;
 +              }
 +              return 0;
 +      }
 +
        if (patchbreak(line)) {
                fclose(cmitmsg);
                cmitmsg = NULL;
@@@ -845,6 -772,7 +852,6 @@@ static void handle_filter(struct strbu
  
  static void handle_body(void)
  {
 -      int len = 0;
        struct strbuf prev = STRBUF_INIT;
  
        /* Skip up to the first boundary */
        }
  
        do {
 -              strbuf_setlen(&line, line.len + len);
 -
                /* process any boundary lines */
                if (*content_top && is_multipart_boundary(&line)) {
                        /* flush any leftover */
                        handle_filter(&line);
                }
  
 -              strbuf_reset(&line);
 -              if (strbuf_avail(&line) < 100)
 -                      strbuf_grow(&line, 100);
 -      } while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin)));
 +      } while (!strbuf_getwholeline(&line, fin, '\n'));
  
  handle_body_out:
        strbuf_release(&prev);
@@@ -965,9 -898,12 +972,9 @@@ static void handle_info(void
        fprintf(fout, "\n");
  }
  
 -static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
 -                  const char *msg, const char *patch)
 +static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
  {
        int peek;
 -      keep_subject = ks;
 -      metainfo_charset = encoding;
        fin = in;
        fout = out;
  
        return 0;
  }
  
 +static int git_mailinfo_config(const char *var, const char *value, void *unused)
 +{
 +      if (prefixcmp(var, "mailinfo."))
 +              return git_default_config(var, value, unused);
 +      if (!strcmp(var, "mailinfo.scissors")) {
 +              use_scissors = git_config_bool(var, value);
 +              return 0;
 +      }
 +      /* perhaps others here */
 +      return 0;
 +}
 +
  static const char mailinfo_usage[] =
-       "git mailinfo [-k] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
 -      "git mailinfo [-k|-b] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
++      "git mailinfo [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
  
  int cmd_mailinfo(int argc, const char **argv, const char *prefix)
  {
        /* NEEDSWORK: might want to do the optional .git/ directory
         * discovery
         */
 -      git_config(git_default_config, NULL);
 +      git_config(git_mailinfo_config, NULL);
  
        def_charset = (git_commit_encoding ? git_commit_encoding : "UTF-8");
        metainfo_charset = def_charset;
        while (1 < argc && argv[1][0] == '-') {
                if (!strcmp(argv[1], "-k"))
                        keep_subject = 1;
+               else if (!strcmp(argv[1], "-b"))
+                       keep_non_patch_brackets_in_subject = 1;
                else if (!strcmp(argv[1], "-u"))
                        metainfo_charset = def_charset;
                else if (!strcmp(argv[1], "-n"))
                        metainfo_charset = NULL;
                else if (!prefixcmp(argv[1], "--encoding="))
                        metainfo_charset = argv[1] + 11;
 +              else if (!strcmp(argv[1], "--scissors"))
 +                      use_scissors = 1;
 +              else if (!strcmp(argv[1], "--no-scissors"))
 +                      use_scissors = 0;
 +              else if (!strcmp(argv[1], "--no-inbody-headers"))
 +                      use_inbody_headers = 0;
                else
                        usage(mailinfo_usage);
                argc--; argv++;
        if (argc != 3)
                usage(mailinfo_usage);
  
 -      return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
 +      return !!mailinfo(stdin, stdout, argv[1], argv[2]);
  }