sequencer.c: recognize "(cherry picked from ..." as part of s-o-b footer
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 <author@example.com> (cherry picked from da39a3ee5e6b4b0d3255bfef95601890afd80709) Signed-off-by: C O Mmitter <committer@example.com> instead of this: Signed-off-by: A U Thor <author@example.com> (cherry picked from da39a3ee5e6b4b0d3255bfef95601890afd80709) Signed-off-by: C O Mmitter <committer@example.com> [with improvements from Jonathan Nieder] Signed-off-by: Brandon Casey <bcasey@nvidia.com> Acked-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									f2b9a7555b
								
							
						
					
					
						commit
						cd650a4eee
					
				
							
								
								
									
										51
									
								
								sequencer.c
								
								
								
								
							
							
						
						
									
										51
									
								
								sequencer.c
								
								
								
								
							|  | @ -18,6 +18,7 @@ | ||||||
| #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" | #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" | ||||||
|  |  | ||||||
| const char sign_off_header[] = "Signed-off-by: "; | const char sign_off_header[] = "Signed-off-by: "; | ||||||
|  | static const char cherry_picked_prefix[] = "(cherry picked from commit "; | ||||||
|  |  | ||||||
| static void remove_sequencer_state(void) | 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) { | 		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, sha1_to_hex(commit->object.sha1)); | ||||||
| 			strbuf_addstr(&msgbuf, ")\n"); | 			strbuf_addstr(&msgbuf, ")\n"); | ||||||
| 		} | 		} | ||||||
|  | @ -1017,16 +1018,44 @@ int sequencer_pick_revisions(struct replay_opts *opts) | ||||||
| 	return pick_commits(todo_list, 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; | ||||||
| 	int i, j, k; |  | ||||||
|  | 	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; | 	int len = sb->len - ignore_footer; | ||||||
| 	const char *buf = sb->buf; | 	const char *buf = sb->buf; | ||||||
|  |  | ||||||
|  | 	/* footer must end with newline */ | ||||||
|  | 	if (!len || buf[len - 1] != '\n') | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
| 	prev = '\0'; | 	prev = '\0'; | ||||||
| 	for (i = len - 1; i > 0; i--) { | 	for (i = len - 1; i > 0; i--) { | ||||||
| 		ch = buf[i]; | 		char ch = buf[i]; | ||||||
| 		if (prev == '\n' && ch == '\n') /* paragraph break */ | 		if (prev == '\n' && ch == '\n') /* paragraph break */ | ||||||
| 			break; | 			break; | ||||||
| 		prev = ch; | 		prev = ch; | ||||||
|  | @ -1041,15 +1070,9 @@ static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer) | ||||||
| 			; /* do nothing */ | 			; /* do nothing */ | ||||||
| 		k++; | 		k++; | ||||||
|  |  | ||||||
| 		for (j = 0; i + j < len; j++) { | 		if (!is_rfc2822_line(buf + i, k - i - 1) && | ||||||
| 			ch = buf[i + j]; | 		    !is_cherry_picked_from_line(buf + i, k - i - 1)) | ||||||
| 			if (ch == ':') |  | ||||||
| 				break; |  | ||||||
| 			if (isalnum(ch) || |  | ||||||
| 			    (ch == '-')) |  | ||||||
| 				continue; |  | ||||||
| 			return 0; | 			return 0; | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	return 1; | 	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--) | 	for (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\n'; i--) | ||||||
| 		; /* do nothing */ | 		; /* do nothing */ | ||||||
| 	if (prefixcmp(msgbuf->buf + i, sob.buf)) { | 	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, "\n", 1); | ||||||
| 		strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len); | 		strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -32,6 +32,10 @@ Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" | ||||||
| mesg_with_footer_sob="$mesg_with_footer | mesg_with_footer_sob="$mesg_with_footer | ||||||
| Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" | 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 <cuthor@example.com>" | ||||||
|  |  | ||||||
|  |  | ||||||
| test_expect_success setup ' | test_expect_success setup ' | ||||||
| 	git config advice.detachedhead false && | 	git config advice.detachedhead false && | ||||||
|  | @ -47,6 +51,8 @@ test_expect_success setup ' | ||||||
| 	test_commit "$mesg_with_footer" foo b mesg-with-footer && | 	test_commit "$mesg_with_footer" foo b mesg-with-footer && | ||||||
| 	git reset --hard initial && | 	git reset --hard initial && | ||||||
| 	test_commit "$mesg_with_footer_sob" foo b mesg-with-footer-sob && | 	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 && | 	pristine_detach initial && | ||||||
| 	test_commit conflicting unrelated | 	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_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' ' | test_expect_success 'cherry-pick -s refrains from adding duplicate trailing sob' ' | ||||||
| 	pristine_detach initial && | 	pristine_detach initial && | ||||||
| 	git cherry-pick -s mesg-with-footer-sob && | 	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_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 | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Brandon Casey
						Brandon Casey