interpret_branch_name(): handle auto-namelen for @{-1}
[gitweb.git] / trailer.c
index eeaafc4976e34d50ade67e5322c2ea9e9540fcb6..f0ecde2d2f8b35c565ab176c76882a5fee8d8fb2 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -27,6 +27,10 @@ static struct conf_info default_conf_info;
 
 struct trailer_item {
        struct list_head list;
+       /*
+        * If this is not a trailer line, the line is stored in value
+        * (excluding the terminating newline) and token is NULL.
+        */
        char *token;
        char *value;
 };
@@ -44,6 +48,12 @@ static char *separators = ":";
 
 #define TRAILER_ARG_STRING "$ARG"
 
+static const char *git_generated_prefixes[] = {
+       "Signed-off-by: ",
+       "(cherry picked from commit ",
+       NULL
+};
+
 /* Iterate over the elements of the list. */
 #define list_for_each_dir(pos, head, is_reverse) \
        for (pos = is_reverse ? (head)->prev : (head)->next; \
@@ -70,9 +80,14 @@ static size_t token_len_without_separator(const char *token, size_t len)
 
 static int same_token(struct trailer_item *a, struct arg_item *b)
 {
-       size_t a_len = token_len_without_separator(a->token, strlen(a->token));
-       size_t b_len = token_len_without_separator(b->token, strlen(b->token));
-       size_t min_len = (a_len > b_len) ? b_len : a_len;
+       size_t a_len, b_len, min_len;
+
+       if (!a->token)
+               return 0;
+
+       a_len = token_len_without_separator(a->token, strlen(a->token));
+       b_len = token_len_without_separator(b->token, strlen(b->token));
+       min_len = (a_len > b_len) ? b_len : a_len;
 
        return !strncasecmp(a->token, b->token, min_len);
 }
@@ -130,7 +145,14 @@ static char last_non_space_char(const char *s)
 
 static void print_tok_val(FILE *outfile, const char *tok, const char *val)
 {
-       char c = last_non_space_char(tok);
+       char c;
+
+       if (!tok) {
+               fprintf(outfile, "%s\n", val);
+               return;
+       }
+
+       c = last_non_space_char(tok);
        if (!c)
                return;
        if (strchr(separators, c))
@@ -394,12 +416,9 @@ static int set_if_missing(struct conf_info *item, const char *value)
 static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
 {
        *dst = *src;
-       if (src->name)
-               dst->name = xstrdup(src->name);
-       if (src->key)
-               dst->key = xstrdup(src->key);
-       if (src->command)
-               dst->command = xstrdup(src->command);
+       dst->name = xstrdup_or_null(src->name);
+       dst->key = xstrdup_or_null(src->key);
+       dst->command = xstrdup_or_null(src->command);
 }
 
 static struct arg_item *get_conf_item(const char *name)
@@ -597,12 +616,14 @@ static void parse_trailer(struct strbuf *tok, struct strbuf *val,
        }
 }
 
-static void add_trailer_item(struct list_head *head, char *tok, char *val)
+static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
+                                            char *val)
 {
        struct trailer_item *new = xcalloc(sizeof(*new), 1);
        new->token = tok;
        new->value = val;
        list_add_tail(&new->list, head);
+       return new;
 }
 
 static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
@@ -709,6 +730,15 @@ static int find_patch_start(struct strbuf **lines, int count)
 static int find_trailer_start(struct strbuf **lines, int count)
 {
        int start, end_of_title, only_spaces = 1;
+       int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
+       /*
+        * Number of possible continuation lines encountered. This will be
+        * reset to 0 if we encounter a trailer (since those lines are to be
+        * considered continuations of that trailer), and added to
+        * non_trailer_lines if we encounter a non-trailer (since those lines
+        * are to be considered non-trailers).
+        */
+       int possible_continuation_lines = 0;
 
        /* The first paragraph is the title and cannot be trailers */
        for (start = 0; start < count; start++) {
@@ -720,26 +750,71 @@ static int find_trailer_start(struct strbuf **lines, int count)
        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.
+        * Get the start of the trailers by looking starting from the end for a
+        * blank line before a set of non-blank lines that (i) are all
+        * trailers, or (ii) contains at least one Git-generated trailer and
+        * consists of at least 25% trailers.
         */
        for (start = count - 1; start >= end_of_title; start--) {
-               if (lines[start]->buf[0] == comment_line_char)
+               const char **p;
+               int separator_pos;
+
+               if (lines[start]->buf[0] == comment_line_char) {
+                       non_trailer_lines += possible_continuation_lines;
+                       possible_continuation_lines = 0;
                        continue;
+               }
                if (contains_only_spaces(lines[start]->buf)) {
                        if (only_spaces)
                                continue;
-                       return start + 1;
+                       non_trailer_lines += possible_continuation_lines;
+                       if (recognized_prefix &&
+                           trailer_lines * 3 >= non_trailer_lines)
+                               return start + 1;
+                       if (trailer_lines && !non_trailer_lines)
+                               return start + 1;
+                       return count;
                }
-               if (strcspn(lines[start]->buf, separators) < lines[start]->len) {
-                       if (only_spaces)
-                               only_spaces = 0;
-                       continue;
+               only_spaces = 0;
+
+               for (p = git_generated_prefixes; *p; p++) {
+                       if (starts_with(lines[start]->buf, *p)) {
+                               trailer_lines++;
+                               possible_continuation_lines = 0;
+                               recognized_prefix = 1;
+                               goto continue_outer_loop;
+                       }
+               }
+
+               separator_pos = find_separator(lines[start]->buf, separators);
+               if (separator_pos >= 1 && !isspace(lines[start]->buf[0])) {
+                       struct list_head *pos;
+
+                       trailer_lines++;
+                       possible_continuation_lines = 0;
+                       if (recognized_prefix)
+                               continue;
+                       list_for_each(pos, &conf_head) {
+                               struct arg_item *item;
+                               item = list_entry(pos, struct arg_item, list);
+                               if (token_matches_item(lines[start]->buf, item,
+                                                      separator_pos)) {
+                                       recognized_prefix = 1;
+                                       break;
+                               }
+                       }
+               } else if (isspace(lines[start]->buf[0]))
+                       possible_continuation_lines++;
+               else {
+                       non_trailer_lines++;
+                       non_trailer_lines += possible_continuation_lines;
+                       possible_continuation_lines = 0;
                }
-               return count;
+continue_outer_loop:
+               ;
        }
 
-       return only_spaces ? count : 0;
+       return count;
 }
 
 /* Get the index of the end of the trailers */
@@ -783,6 +858,7 @@ static int process_input_file(FILE *outfile,
        int patch_start, trailer_start, trailer_end, i;
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
+       struct trailer_item *last = NULL;
 
        /* Get the line count */
        while (lines[count])
@@ -803,13 +879,28 @@ static int process_input_file(FILE *outfile,
                int separator_pos;
                if (lines[i]->buf[0] == comment_line_char)
                        continue;
+               if (last && isspace(lines[i]->buf[0])) {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addf(&sb, "%s\n%s", last->value, lines[i]->buf);
+                       strbuf_strip_suffix(&sb, "\n");
+                       free(last->value);
+                       last->value = strbuf_detach(&sb, NULL);
+                       continue;
+               }
                separator_pos = find_separator(lines[i]->buf, separators);
                if (separator_pos >= 1) {
                        parse_trailer(&tok, &val, NULL, lines[i]->buf,
                                      separator_pos);
+                       last = add_trailer_item(head,
+                                               strbuf_detach(&tok, NULL),
+                                               strbuf_detach(&val, NULL));
+               } else {
+                       strbuf_addbuf(&val, lines[i]);
+                       strbuf_strip_suffix(&val, "\n");
                        add_trailer_item(head,
-                                        strbuf_detach(&tok, NULL),
+                                        NULL,
                                         strbuf_detach(&val, NULL));
+                       last = NULL;
                }
        }