When 'cherry-pick -s' is used to append a signed-off-by line to a cherry
picked commit, it does not currently detect the "(cherry picked from..."
that may have been appended by a previous 'cherry-pick -x' as part of the
s-o-b footer and it will insert a blank line before appending a new s-o-b.

Let's detect "(cherry picked from...)" as part of the footer so that we
will produce this:

   Signed-off-by: A U Thor <aut...@example.com>
   (cherry picked from da39a3ee5e6b4b0d3255bfef95601890afd80709)
   Signed-off-by: C O Mitter <commit...@example.com>

instead of this:

   Signed-off-by: A U Thor <aut...@example.com>
   (cherry picked from da39a3ee5e6b4b0d3255bfef95601890afd80709)

   Signed-off-by: C O Mitter <commit...@example.com>

Signed-off-by: Brandon Casey <bca...@nvidia.com>
---
 sequencer.c              | 42 ++++++++++++++++++++++++------------
 t/t3511-cherry-pick-x.sh | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 13 deletions(-)

diff --git a/sequencer.c b/sequencer.c
index 656df6b..19eaf11 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -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,11 +1018,32 @@ 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)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               int ch = buf[i];
+               if (ch == ':')
+                       break;
+               if (isalnum(ch) || (ch == '-'))
+                       continue;
+               return 0;
+       }
+
+       return 1;
+}
+
+static int is_cherry_pick_from_line(const char *buf, int len)
+{
+       return (strlen(cherry_picked_prefix) + 41) <= len &&
+               !prefixcmp(buf, cherry_picked_prefix);
+}
+
+static int has_conforming_footer(struct strbuf *sb, int ignore_footer)
 {
-       int ch;
        int hit = 0;
-       int i, j, k;
+       int i, k;
        int len = sb->len - ignore_footer;
        const char *buf = sb->buf;
 
@@ -1039,15 +1061,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) ||
+                       is_cherry_pick_from_line(buf+i, k-i)))
                        return 0;
-               }
        }
        return 1;
 }
@@ -1064,7 +1080,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);
        }
diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh
index a6c4168..32c0bb1 100755
--- a/t/t3511-cherry-pick-x.sh
+++ b/t/t3511-cherry-pick-x.sh
@@ -32,6 +32,10 @@ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 mesg_with_footer_sob="$mesg_with_footer
 Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 
+mesg_with_cherry_footer="$mesg_with_footer_sob
+(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
+Tested-by: C.U. Thor <cut...@example.com>"
+
 
 test_expect_success setup '
        git config advice.detachedhead false &&
@@ -47,6 +51,8 @@ test_expect_success setup '
        test_commit "$mesg_with_footer" foo b mesg-with-footer &&
        git reset --hard initial &&
        test_commit "$mesg_with_footer_sob" foo b mesg-with-footer-sob &&
+       git reset --hard initial &&
+       test_commit "$mesg_with_cherry_footer" foo b mesg-with-cherry-footer &&
        pristine_detach initial &&
        test_commit conflicting unrelated
 '
@@ -98,6 +104,19 @@ test_expect_success 'cherry-pick -s adds sob when last sob 
doesnt match committe
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x -s adds sob when last sob doesnt match 
committer' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-with-footer^0` &&
+       git cherry-pick -x -s mesg-with-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_footer
+               (cherry picked from commit $sha1)
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'cherry-pick -s refrains from adding duplicate trailing 
sob' '
        pristine_detach initial &&
        git cherry-pick -s mesg-with-footer-sob &&
@@ -108,4 +127,40 @@ test_expect_success 'cherry-pick -s refrains from adding 
duplicate trailing sob'
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists 
for committer' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-with-footer-sob^0` &&
+       git cherry-pick -x -s mesg-with-footer-sob &&
+       cat <<-EOF >expect &&
+               $mesg_with_footer_sob
+               (cherry picked from commit $sha1)
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x treats "(cherry picked from..." line as 
part of footer' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-with-cherry-footer^0` &&
+       git cherry-pick -x mesg-with-cherry-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_cherry_footer
+               (cherry picked from commit $sha1)
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s treats "(cherry picked from..." line as 
part of footer' '
+       pristine_detach initial &&
+       git cherry-pick -s mesg-with-cherry-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_cherry_footer
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
 test_done
-- 
1.8.0.284.g959048a

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to