sequencer.c: recognize "(cherry picked from ..." as part of s-o-b footer
[gitweb.git] / sequencer.c
index 1a80dac6c52b876f5f90d026d671fae61bf423fa..7889b6bc19c0a9e5b81203ea5465f21ed6306d51 100644 (file)
@@ -18,6 +18,7 @@
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
 const char sign_off_header[] = "Signed-off-by: ";
+static const char cherry_picked_prefix[] = "(cherry picked from commit ";
 
 static void remove_sequencer_state(void)
 {
@@ -492,7 +493,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                }
 
                if (opts->record_origin) {
-                       strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+                       strbuf_addstr(&msgbuf, cherry_picked_prefix);
                        strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
                        strbuf_addstr(&msgbuf, ")\n");
                }
@@ -1017,16 +1018,44 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        return pick_commits(todo_list, opts);
 }
 
-static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)
+static int is_rfc2822_line(const char *buf, int len)
 {
-       char ch, prev;
-       int i, j, k;
+       int i;
+
+       for (i = 0; i < len; i++) {
+               int ch = buf[i];
+               if (ch == ':')
+                       return 1;
+               if (!isalnum(ch) && ch != '-')
+                       break;
+       }
+
+       return 0;
+}
+
+static int is_cherry_picked_from_line(const char *buf, int len)
+{
+       /*
+        * We only care that it looks roughly like (cherry picked from ...)
+        */
+       return len > strlen(cherry_picked_prefix) + 1 &&
+               !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
+}
+
+static int has_conforming_footer(struct strbuf *sb, int ignore_footer)
+{
+       char prev;
+       int i, k;
        int len = sb->len - ignore_footer;
        const char *buf = sb->buf;
 
+       /* footer must end with newline */
+       if (!len || buf[len - 1] != '\n')
+               return 0;
+
        prev = '\0';
        for (i = len - 1; i > 0; i--) {
-               ch = buf[i];
+               char ch = buf[i];
                if (prev == '\n' && ch == '\n') /* paragraph break */
                        break;
                prev = ch;
@@ -1041,15 +1070,9 @@ static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)
                        ; /* do nothing */
                k++;
 
-               for (j = 0; i + j < len; j++) {
-                       ch = buf[i + j];
-                       if (ch == ':')
-                               break;
-                       if (isalnum(ch) ||
-                           (ch == '-'))
-                               continue;
+               if (!is_rfc2822_line(buf + i, k - i - 1) &&
+                   !is_cherry_picked_from_line(buf + i, k - i - 1))
                        return 0;
-               }
        }
        return 1;
 }
@@ -1066,7 +1089,7 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer)
        for (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\n'; i--)
                ; /* do nothing */
        if (prefixcmp(msgbuf->buf + i, sob.buf)) {
-               if (!i || !ends_rfc2822_footer(msgbuf, ignore_footer))
+               if (!i || !has_conforming_footer(msgbuf, ignore_footer))
                        strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, "\n", 1);
                strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len);
        }