Merge branch 'cc/trailers-corner-case-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 2 Sep 2015 19:50:21 +0000 (12:50 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 2 Sep 2015 19:50:21 +0000 (12:50 -0700)
The "interpret-trailers" helper mistook a multi-paragraph title of
a commit log message with a colon in it as the end of the trailer
block.

* cc/trailers-corner-case-fix:
trailer: support multiline title

1  2 
t/t7513-interpret-trailers.sh
trailer.c
index 9577b4effb05904f9f6b29b4321b103ebdd52e7f,1abec57f6e08a95a8d1bf6a3ec855a599fd20e2a..322c436a494c14a7350d6fdd85efbfbc5a7a8d28
@@@ -112,6 -112,20 +112,20 @@@ test_expect_success 'with only a title 
        test_cmp expected actual
  '
  
+ test_expect_success 'with multiline title in the message' '
+       cat >expected <<-\EOF &&
+               place of
+               code: change
+               Reviewed-by: Peff
+               Acked-by: Johan
+       EOF
+       printf "%s\n" "place of" "code: change" |
+       git interpret-trailers --trailer "Reviewed-by: Peff" \
+               --trailer "Acked-by: Johan" >actual &&
+       test_cmp expected actual
+ '
  test_expect_success 'with config setup' '
        git config trailer.ack.key "Acked-by: " &&
        cat >expected <<-\EOF &&
@@@ -226,7 -240,7 +240,7 @@@ test_expect_success 'with 2 files argum
  '
  
  test_expect_success 'with message that has comments' '
 -      cat basic_message >>message_with_comments &&
 +      cat basic_message >message_with_comments &&
        sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
                # comment
  
  
                Reviewed-by: Johan
                Cc: Peff
 +              # last comment
 +
        EOF
        cat basic_patch >>expected &&
        git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
        test_cmp expected actual
  '
  
 +test_expect_success 'with message that has an old style conflict block' '
 +      cat basic_message >message_with_comments &&
 +      sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
 +              # comment
 +
 +              # other comment
 +              Cc: Z
 +              # yet another comment
 +              Reviewed-by: Johan
 +              Reviewed-by: Z
 +              # last comment
 +
 +              Conflicts:
 +
 +      EOF
 +      cat basic_message >expected &&
 +      cat >>expected <<-\EOF &&
 +              # comment
 +
 +              Reviewed-by: Johan
 +              Cc: Peff
 +              # last comment
 +
 +              Conflicts:
 +
 +      EOF
 +      git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
 +      test_cmp expected actual
 +'
 +
  test_expect_success 'with commit complex message and trailer args' '
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
diff --combined trailer.c
index b8088687d407eaafd6ac3f5680cebb2fe64dc80b,5ec5f9ddc1c17047d2ce2df137a0e23009fdafd4..6f3416febaba90f281e95ea605ac6aec10853ef3
+++ b/trailer.c
@@@ -1,7 -1,7 +1,7 @@@
  #include "cache.h"
  #include "string-list.h"
  #include "run-command.h"
 -#include "string-list.h"
 +#include "commit.h"
  #include "trailer.h"
  /*
   * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
@@@ -214,6 -214,16 +214,6 @@@ static struct trailer_item *remove_firs
        return item;
  }
  
 -static int read_from_command(struct child_process *cp, struct strbuf *buf)
 -{
 -      if (run_command(cp))
 -              return error("running trailer command '%s' failed", cp->argv[0]);
 -      if (strbuf_read(buf, cp->out, 1024) < 1)
 -              return error("reading from trailer command '%s' failed", cp->argv[0]);
 -      strbuf_trim(buf);
 -      return 0;
 -}
 -
  static const char *apply_command(const char *command, const char *arg)
  {
        struct strbuf cmd = STRBUF_INIT;
        cp.argv = argv;
        cp.env = local_repo_env;
        cp.no_stdin = 1;
 -      cp.out = -1;
        cp.use_shell = 1;
  
 -      if (read_from_command(&cp, &buf)) {
 +      if (capture_command(&cp, &buf, 1024)) {
 +              error("running trailer command '%s' failed", cmd.buf);
                strbuf_release(&buf);
                result = xstrdup("");
 -      } else
 +      } else {
 +              strbuf_trim(&buf);
                result = strbuf_detach(&buf, NULL);
 +      }
  
        strbuf_release(&cmd);
        return result;
@@@ -735,15 -743,22 +735,22 @@@ static int find_patch_start(struct strb
   */
  static int find_trailer_start(struct strbuf **lines, int count)
  {
-       int start, only_spaces = 1;
+       int start, end_of_title, only_spaces = 1;
+       /* The first paragraph is the title and cannot be trailers */
+       for (start = 0; start < count; start++) {
+               if (lines[start]->buf[0] == comment_line_char)
+                       continue;
+               if (contains_only_spaces(lines[start]->buf))
+                       break;
+       }
+       end_of_title = start;
  
        /*
         * Get the start of the trailers by looking starting from the end
         * for a line with only spaces before lines with one separator.
-        * The first line must not be analyzed as the others as it
-        * should be either the message title or a blank line.
         */
-       for (start = count - 1; start >= 1; start--) {
+       for (start = count - 1; start >= end_of_title; start--) {
                if (lines[start]->buf[0] == comment_line_char)
                        continue;
                if (contains_only_spaces(lines[start]->buf)) {
        return only_spaces ? count : 0;
  }
  
 +/* Get the index of the end of the trailers */
 +static int find_trailer_end(struct strbuf **lines, int patch_start)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int i, ignore_bytes;
 +
 +      for (i = 0; i < patch_start; i++)
 +              strbuf_addbuf(&sb, lines[i]);
 +      ignore_bytes = ignore_non_trailer(&sb);
 +      strbuf_release(&sb);
 +      for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--)
 +              ignore_bytes -= lines[i]->len;
 +
 +      return i + 1;
 +}
 +
  static int has_blank_line_before(struct strbuf **lines, int start)
  {
        for (;start >= 0; start--) {
@@@ -800,15 -799,14 +807,15 @@@ static int process_input_file(struct st
                              struct trailer_item **in_tok_last)
  {
        int count = 0;
 -      int patch_start, trailer_start, i;
 +      int patch_start, trailer_start, trailer_end, i;
  
        /* Get the line count */
        while (lines[count])
                count++;
  
        patch_start = find_patch_start(lines, count);
 -      trailer_start = find_trailer_start(lines, patch_start);
 +      trailer_end = find_trailer_end(lines, patch_start);
 +      trailer_start = find_trailer_start(lines, trailer_end);
  
        /* Print lines before the trailers as is */
        print_lines(lines, 0, trailer_start);
                printf("\n");
  
        /* Parse trailer lines */
 -      for (i = trailer_start; i < patch_start; i++) {
 +      for (i = trailer_start; i < trailer_end; i++) {
                if (lines[i]->buf[0] != comment_line_char) {
                        struct trailer_item *new = create_trailer_item(lines[i]->buf);
                        add_trailer_item(in_tok_first, in_tok_last, new);
                }
        }
  
 -      return patch_start;
 +      return trailer_end;
  }
  
  static void free_all(struct trailer_item **first)
@@@ -841,7 -839,7 +848,7 @@@ void process_trailers(const char *file
        struct trailer_item *in_tok_last = NULL;
        struct trailer_item *arg_tok_first;
        struct strbuf **lines;
 -      int patch_start;
 +      int trailer_end;
  
        /* Default config must be setup first */
        git_config(git_trailer_default_config, NULL);
        lines = read_input_file(file);
  
        /* Print the lines before the trailers */
 -      patch_start = process_input_file(lines, &in_tok_first, &in_tok_last);
 +      trailer_end = process_input_file(lines, &in_tok_first, &in_tok_last);
  
        arg_tok_first = process_command_line_args(trailers);
  
        free_all(&in_tok_first);
  
        /* Print the lines after the trailers as is */
 -      print_lines(lines, patch_start, INT_MAX);
 +      print_lines(lines, trailer_end, INT_MAX);
  
        strbuf_list_free(lines);
  }