sequencer.c: teach append_signoff how to detect duplicate s-o-b
authorBrandon Casey <drafnel@gmail.com>
Tue, 12 Feb 2013 10:17:35 +0000 (02:17 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 12 Feb 2013 19:17:10 +0000 (11:17 -0800)
Teach append_signoff how to detect a duplicate s-o-b in the commit footer.
This is in preparation to unify the append_signoff implementations in
log-tree.c and sequencer.c.

Fixes test in t3511.

Signed-off-by: Brandon Casey <bcasey@nvidia.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/commit.c
sequencer.c
sequencer.h
t/t3511-cherry-pick-x.sh
index 1dd2ec5e6f878c02e2858d72f08d8b88d67bcd85..7b9e2ac2ebbe29efbda17bf0640f8796ead9d080 100644 (file)
@@ -700,7 +700,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        previous = eol;
                }
 
-               append_signoff(&sb, ignore_footer);
+               append_signoff(&sb, ignore_footer, 0);
        }
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
index f9f9f8a77268cadde74e36c70821e25a1842ec01..965b954baf65dc81e1e219ef3a91234b76ab5c73 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,33 @@ 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)
+               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, "\n", 1);
+
+       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);
 }
index 9d57d57524d411c54d55346a5ecff67bc256595c..1fc22dcabe132cd437e7b0d3c56588c8cd7e63f2 100644 (file)
@@ -6,6 +6,8 @@
 #define SEQ_TODO_FILE  "sequencer/todo"
 #define SEQ_OPTS_FILE  "sequencer/opts"
 
+#define APPEND_SIGNOFF_DEDUP (1u << 0)
+
 enum replay_action {
        REPLAY_REVERT,
        REPLAY_PICK
@@ -48,6 +50,6 @@ int sequencer_pick_revisions(struct replay_opts *opts);
 
 extern const char sign_off_header[];
 
-void append_signoff(struct strbuf *msgbuf, int ignore_footer);
+void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 
 #endif
index a845e45d40cc4d07a8dd2a0d6ebae6e2d668959d..f97727975b880ae52394c6af11dde15b71ac6eb7 100755 (executable)
@@ -82,7 +82,7 @@ test_expect_success 'cherry-pick -s inserts blank line after one line subject' '
        test_cmp expect actual
 '
 
-test_expect_failure 'cherry-pick -s inserts blank line after non-conforming footer' '
+test_expect_success 'cherry-pick -s inserts blank line after non-conforming footer' '
        pristine_detach initial &&
        git cherry-pick -s mesg-broken-footer &&
        cat <<-EOF >expect &&