sequencer.c: teach append_signoff to avoid adding a duplicate newline
[gitweb.git] / sequencer.c
index f9f9f8a77268cadde74e36c70821e25a1842ec01..53ee49a9b0b02a687e3b56e85d96794d9f1b31ce 100644 (file)
@@ -44,12 +44,20 @@ static int is_cherry_picked_from_line(const char *buf, int len)
                !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
 }
 
-static int has_conforming_footer(struct strbuf *sb, int ignore_footer)
+/*
+ * Returns 0 for non-conforming footer
+ * Returns 1 for conforming footer
+ * Returns 2 when sob exists within conforming footer
+ * Returns 3 when sob exists within conforming footer as last entry
+ */
+static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
+       int ignore_footer)
 {
        char prev;
        int i, k;
        int len = sb->len - ignore_footer;
        const char *buf = sb->buf;
+       int found_sob = 0;
 
        /* footer must end with newline */
        if (!len || buf[len - 1] != '\n')
@@ -72,14 +80,25 @@ static int has_conforming_footer(struct strbuf *sb, int ignore_footer)
                i++;
 
        for (; i < len; i = k) {
+               int found_rfc2822;
+
                for (k = i; k < len && buf[k] != '\n'; k++)
                        ; /* do nothing */
                k++;
 
-               if (!is_rfc2822_line(buf + i, k - i - 1) &&
-                   !is_cherry_picked_from_line(buf + i, k - i - 1))
+               found_rfc2822 = is_rfc2822_line(buf + i, k - i - 1);
+               if (found_rfc2822 && sob &&
+                   !strncmp(buf + i, sob->buf, sob->len))
+                       found_sob = k;
+
+               if (!(found_rfc2822 ||
+                     is_cherry_picked_from_line(buf + i, k - i - 1)))
                        return 0;
        }
+       if (found_sob == i)
+               return 3;
+       if (found_sob)
+               return 2;
        return 1;
 }
 
@@ -300,7 +319,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        rollback_lock_file(&index_lock);
 
        if (opts->signoff)
-               append_signoff(msgbuf, 0);
+               append_signoff(msgbuf, 0, 0);
 
        if (!clean) {
                int i;
@@ -556,7 +575,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                }
 
                if (opts->record_origin) {
-                       if (!has_conforming_footer(&msgbuf, 0))
+                       if (!has_conforming_footer(&msgbuf, NULL, 0))
                                strbuf_addch(&msgbuf, '\n');
                        strbuf_addstr(&msgbuf, cherry_picked_prefix);
                        strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
@@ -1083,21 +1102,44 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        return pick_commits(todo_list, opts);
 }
 
-void append_signoff(struct strbuf *msgbuf, int ignore_footer)
+void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 {
+       unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
        struct strbuf sob = STRBUF_INIT;
-       int i;
+       int has_footer;
 
        strbuf_addstr(&sob, sign_off_header);
        strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
                                getenv("GIT_COMMITTER_EMAIL")));
        strbuf_addch(&sob, '\n');
-       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 (!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);
+
+       /*
+        * If the whole message buffer is equal to the sob, pretend that we
+        * found a conforming footer with a matching sob
+        */
+       if (msgbuf->len - ignore_footer == sob.len &&
+           !strncmp(msgbuf->buf, sob.buf, sob.len))
+               has_footer = 3;
+       else
+               has_footer = has_conforming_footer(msgbuf, &sob, ignore_footer);
+
+       if (!has_footer) {
+               const char *append_newlines = NULL;
+               size_t len = msgbuf->len - ignore_footer;
+
+               if (len && msgbuf->buf[len - 1] != '\n')
+                       append_newlines = "\n\n";
+               else if (len > 1 && msgbuf->buf[len - 2] != '\n')
+                       append_newlines = "\n";
+
+               if (append_newlines)
+                       strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
+                               append_newlines, strlen(append_newlines));
        }
+
+       if (has_footer != 3 && (!no_dup_sob || has_footer != 2))
+               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
+                               sob.buf, sob.len);
+
        strbuf_release(&sob);
 }