Skip to contentSkip to conversation

git cherry-pick -x

GIT 2.12.2 REGRESSION Superseded Brian Norris 7 participants 18 messages Apr 21, 2017 – Apr 26, 2017

lore
Share & tools
lore

Brian Norris

author Acked-by Arnd Bergmann Acked-by Arnd Bergmann
#0 lore raw

Hi all,

I haven't tried bisecting precisely, but somewhere along the way git cherry-pick -x regressed between 2.9.3 and 2.12.2.

Looking at upstream linux.git, I can do:

git fetch git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next git cherry-pick -x 7b309aef0463340d3ad5449d1f605d14e10a4225

And see the following ending to the new commit:

On 2.9.3, this worked fine:

(cherry picked from commit 7b309aef0463340d3ad5449d1f605d14e10a4225)

This issue doesn't happen on all commits; maybe it's bad parsing looking at 'Signed-off' and the like?

Regards,
Brian

#1 lore raw

+Jonathan, who worked on trailers

Quoted 24 lines · Brian Norris

Hi all,

I haven't tried bisecting precisely, but somewhere along the way git cherry-pick -x regressed between 2.9.3 and 2.12.2.

Looking at upstream linux.git, I can do:

git fetch git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next git cherry-pick -x 7b309aef0463340d3ad5449d1f605d14e10a4225

And see the following ending to the new commit:

On 2.9.3, this worked fine:

(cherry picked from commit 7b309aef0463340d3ad5449d1f605d14e10a4225)

This issue doesn't happen on all commits; maybe it's bad parsing looking at 'Signed-off' and the like?

Regards,
Brian

#2 lore raw
Quoted 4 lines · Stefan Beller

+Jonathan, who worked on trailers
[...]

Quoted 2 lines

I haven't tried bisecting precisely, but somewhere along the way git cherry-pick -x regressed between 2.9.3 and 2.12.2.

Yeah, it bisects to 967dfd4d5 (sequencer: use trailer's trailer layout, 2016-11-02).

-Peff

#3 lore raw

PATCH 1/1 sequencer: require trailing NL in footers

In reply to Brian Norris

In commit 967dfd4 ("sequencer: use trailer's trailer layout", 2016-11-29), sequencer was taught to use the same mechanism as interpret-trailers to determine the nature of the trailer of a commit message (referred to as the "footer" in sequencer.c). However, the requirement that a footer end in a newline character was inadvertently removed. Restore that requirement.

While writing this commit, I noticed that if the "ignore_footer" parameter in "has_conforming_footer" is greater than the distance between the trailer start and sb->len, "has_conforming_footer" will return an unexpected result. This does not occur in practice, because "ignore_footer" is either zero or the return value of an invocation to "ignore_non_trailer", which only skips empty lines and comment lines. This commit contains a comment explaining this in the function's documentation.

---

Thanks for the bug report. Here's a fix - I've verified this with the way to reproduce provided in the original e-mail, and it seems to work now.

The commit message of the referenced commit (7b309aef0463340d3ad5449d1f605d14e10a4225) does not end in a newline, which is probably why different behavior was observed with this commit (as compared to others).

 sequencer.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
sequencer.c +11 -0
index 77afecaeb..4cbe64b7f 100644
@@ -151,6 +151,12 @@ static const char *get_todo_path(const struct replay_opts *opts)
  * Returns 1 for conforming footer
  * Returns 2 when sob exists within conforming footer
  * Returns 3 when sob exists within conforming footer as last entry
+ *
+ * A footer that does not end in a newline is considered non-conforming.
+ *
+ * ignore_footer, if not zero, should be the return value of an invocation to
+ * ignore_non_trailer. See the documentation of that function for more
+ * information.
  */
 static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 	int ignore_footer)
@@ -159,6 +165,11 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 	int i;
 	int found_sob = 0, found_sob_last = 0;
 
+	if (sb->len <= ignore_footer)
+		return 0;
+	if (sb->buf[sb->len - ignore_footer - 1] != '\n')
+		return 0;
+
 	trailer_info_get(&info, sb->buf);
 
 	if (info.trailer_start == info.trailer_end)
Signature · 1 line
2.13.0.rc0.306.g87b477812d-goog
#4 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

Quoted 20 lines · Jonathan Tan

In commit 967dfd4 ("sequencer: use trailer's trailer layout", 2016-11-29), sequencer was taught to use the same mechanism as interpret-trailers to determine the nature of the trailer of a commit message (referred to as the "footer" in sequencer.c). However, the requirement that a footer end in a newline character was inadvertently removed. Restore that requirement.

While writing this commit, I noticed that if the "ignore_footer" parameter in "has_conforming_footer" is greater than the distance between the trailer start and sb->len, "has_conforming_footer" will return an unexpected result. This does not occur in practice, because "ignore_footer" is either zero or the return value of an invocation to "ignore_non_trailer", which only skips empty lines and comment lines. This commit contains a comment explaining this in the function's documentation.

---

Quoted 8 lines

Thanks for the bug report. Here's a fix - I've verified this with the way to reproduce provided in the original e-mail, and it seems to work now.

The commit message of the referenced commit (7b309aef0463340d3ad5449d1f605d14e10a4225) does not end in a newline, which is probably why different behavior was observed with this commit (as compared to others).

Thanks for the fix. :)
Do we want to test for this use case in the future?

sequencer.c static const char *get_todo_path(const struct replay_opts *opts) · line 160

Quoted 11 lines
sequencer.c +6 -0
@@ -151,6 +151,12 @@ static const char *get_todo_path(const struct replay_opts *opts)
  * Returns 1 for conforming footer
  * Returns 2 when sob exists within conforming footer
  * Returns 3 when sob exists within conforming footer as last entry
+ *
+ * A footer that does not end in a newline is considered non-conforming.
+ *
+ * ignore_footer, if not zero, should be the return value of an invocation to
+ * ignore_non_trailer. See the documentation of that function for more
+ * information.
  */

Makes sense. Maybe s/ignore_non_trailer/ignore_non_trailer()/ which makes it easier to recognize it as a function? I'd also drop the last sentence as it is
implied in the previous sentence (sort of).

Thanks,
Stefan

#5 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

Quoted 2 lines · Stefan Beller

Thanks for the fix. :)
Do we want to test for this use case in the future?

Thanks!

I'm not sure of the value of including a test for this specific use case, because Git normally does not create commit messages with no trailing newlines. (To test this, I suspect I would need to use hash-object with a specifically crafted commit object.)

Quoted 17 lines

sequencer.c static const char *get_todo_path(const struct replay_opts *opts) · line 160

Quoted 12 lines
sequencer.c +6 -0
@@ -151,6 +151,12 @@ static const char *get_todo_path(const struct replay_opts *opts)
  * Returns 1 for conforming footer
  * Returns 2 when sob exists within conforming footer
  * Returns 3 when sob exists within conforming footer as last entry
+ *
+ * A footer that does not end in a newline is considered non-conforming.
+ *
+ * ignore_footer, if not zero, should be the return value of an invocation to
+ * ignore_non_trailer. See the documentation of that function for more
+ * information.
  */

Makes sense. Maybe s/ignore_non_trailer/ignore_non_trailer()/ which makes it easier to recognize it as a function? I'd also drop the last sentence as it is
implied in the previous sentence (sort of).

OK, I'll add the parentheses if I need to reroll. As for the last sentence, the documentation for ignore_non_trailer is a bit unusual in that it says specifically "to be fed as the second parameter to append_signoff()", so I want to call extra attention to that.

#11 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

In reply to Jonathan Tan

Hi Jonathan,

Quoted 3 lines · Jonathan Tan

Thanks for the bug report. Here's a fix - I've verified this with the way to reproduce provided in the original e-mail, and it seems to work now.

If there is a straight-forward way to convert the manual test into an automated one, it would be good to make it part of the patch so that we can stave off future regressions of the fix.

Ciao,
Dscho

#6 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

In reply to Jonathan Tan
Quoted 6 lines · Jonathan Tan
Quoted 3 lines · Stefan Beller

Thanks for the fix. :)
Do we want to test for this use case in the future?

Quoted 6 lines

Thanks!

I'm not sure of the value of including a test for this specific use case, because Git normally does not create commit messages with no trailing newlines. (To test this, I suspect I would need to use hash-object with a specifically crafted commit object.)

Okay, makes sense to omit a test.
In that case: Is it needed to hint at how this bug occurred in the wild? (A different Git implementation, which may be fixed now?)

#7 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

Hi Stefan,

Quoted 9 lines · Stefan Beller
Quoted 8 lines · Jonathan Tan
Quoted 3 lines · Stefan Beller

Thanks for the fix. :)
Do we want to test for this use case in the future?

Thanks!

Quoted 8 lines
Quoted 4 lines

I'm not sure of the value of including a test for this specific use case, because Git normally does not create commit messages with no trailing newlines. (To test this, I suspect I would need to use hash-object with a specifically crafted commit object.)

Okay, makes sense to omit a test.
In that case: Is it needed to hint at how this bug occurred in the wild? (A different Git implementation, which may be fixed now?)

Just because Git usually does not create commit messages without trailing newlines does not mean that it is a rare thing. We got bug reports about this, so I think it is frequent enough that we could save time by adding that test and avoid future bug reports/bug hunts.

Ciao,
Johannes

#9 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

In reply to Stefan Beller
Quoted 2 lines · Stefan Beller

In that case: Is it needed to hint at how this bug occurred in the wild? (A different Git implementation, which may be fixed now?)

I might contact the original author, but it's easy enough to imagine automated 'filter-branch' scripts that could produce this. e.g., this trivial example:

git filter-branch --msg-filter "perl -pe 'chomp if eof'" HEAD^..

Reviewed-by on v1 1/1
#12 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

In reply to Jonathan Tan

Hi,

Quoted 19 lines · Jonathan Tan

In commit 967dfd4 ("sequencer: use trailer's trailer layout", 2016-11-29), sequencer was taught to use the same mechanism as interpret-trailers to determine the nature of the trailer of a commit message (referred to as the "footer" in sequencer.c). However, the requirement that a footer end in a newline character was inadvertently removed. Restore that requirement.

While writing this commit, I noticed that if the "ignore_footer" parameter in "has_conforming_footer" is greater than the distance between the trailer start and sb->len, "has_conforming_footer" will return an unexpected result. This does not occur in practice, because "ignore_footer" is either zero or the return value of an invocation to "ignore_non_trailer", which only skips empty lines and comment lines. This commit contains a comment explaining this in the function's documentation.

---

[...]

Quoted 16 lines
sequencer.c +6 -0
@@ -151,6 +151,12 @@ static const char *get_todo_path(const struct replay_opts *opts)
  * Returns 1 for conforming footer
  * Returns 2 when sob exists within conforming footer
  * Returns 3 when sob exists within conforming footer as last entry
+ *
+ * A footer that does not end in a newline is considered non-conforming.
+ *
+ * ignore_footer, if not zero, should be the return value of an invocation to
+ * ignore_non_trailer. See the documentation of that function for more
+ * information.
  */
 static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 	int ignore_footer)

sequencer.c static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob, · line 172

Quoted 8 lines
sequencer.c +5 -0
@@ -159,6 +165,11 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 	int i;
 	int found_sob = 0, found_sob_last = 0;
 
+	if (sb->len <= ignore_footer)
+		return 0;
+	if (sb->buf[sb->len - ignore_footer - 1] != '\n')
+		return 0;
+

This is super subtle, but it does the right thing. The caller will notice it's not a conforming footer, add a newline to separate the new footer from it, and repair the footer as a side effect.

Followup question: what should happen if there is a non-footer-shaped thing with no trailing newline at the end of the commit message? Should we add two newlines in that case when producing a new footer?

Thanks,
Jonathan

#8 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

In reply to Johannes Schindelin
Quoted 4 lines · Johannes Schindelin
Quoted 4 lines · Jonathan Tan
Quoted 3 lines · Stefan Beller
Quoted 2 lines

Do we want to test for this use case in the future?

[...]

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

I'm not sure of the value of including a test for this specific use case, because Git normally does not create commit messages with no trailing newlines. (To test this, I suspect I would need to use hash-object with a specifically crafted commit object.)

[...]

Quoted 4 lines

Just because Git usually does not create commit messages without trailing newlines does not mean that it is a rare thing. We got bug reports about this, so I think it is frequent enough that we could save time by adding that test and avoid future bug reports/bug hunts.

To put it another way: it wouldn't have been worth writing this patch if it wasn't worth keeping the behavior. So we do need a test.

Thanks,
Jonathan

#13 lore raw

PATCH v2 1/1 sequencer: require trailing NL in footers

In reply to Jonathan Tan

In commit 967dfd4 ("sequencer: use trailer's trailer layout", 2016-11-29), sequencer was taught to use the same mechanism as interpret-trailers to determine the nature of the trailer of a commit message (referred to as the "footer" in sequencer.c). However, the requirement that a footer end in a newline character was inadvertently removed. Restore that requirement.

While writing this commit, I noticed that if the "ignore_footer" parameter in "has_conforming_footer" is greater than the distance between the trailer start and sb->len, "has_conforming_footer" will return an unexpected result. This does not occur in practice, because "ignore_footer" is either zero or the return value of an invocation to "ignore_non_trailer", which only skips empty lines and comment lines. This commit contains a comment explaining this in the function's documentation.

---

jrnieder pointed out the existence of commit-tree to me, which I have used to write the test below.

Changes from v1:
- added test
- used one of sbeller's documentation suggestions

 sequencer.c              | 11 +++++++++++
 t/t3511-cherry-pick-x.sh | 14 ++++++++++++++
 2 files changed, 25 insertions(+)
sequencer.c +11 -0
index 77afecaeb..500a76260 100644
@@ -151,6 +151,12 @@ static const char *get_todo_path(const struct replay_opts *opts)
  * Returns 1 for conforming footer
  * Returns 2 when sob exists within conforming footer
  * Returns 3 when sob exists within conforming footer as last entry
+ *
+ * A footer that does not end in a newline is considered non-conforming.
+ *
+ * ignore_footer, if not zero, should be the return value of an invocation to
+ * ignore_non_trailer(). See the documentation of that function for more
+ * information.
  */
 static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 	int ignore_footer)
@@ -159,6 +165,11 @@ static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
 	int i;
 	int found_sob = 0, found_sob_last = 0;
 
+	if (sb->len <= ignore_footer)
+		return 0;
+	if (sb->buf[sb->len - ignore_footer - 1] != '\n')
+		return 0;
+
 	trailer_info_get(&info, sb->buf);
 
 	if (info.trailer_start == info.trailer_end)
t/t3511-cherry-pick-x.sh +14 -0
index bf0a5c988..6f518020b 100755
@@ -208,6 +208,20 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x handles commits with no NL at end of message' '
+	pristine_detach initial &&
+	sha1=$(printf "title\n\nSigned-off-by: a" | git commit-tree -p initial mesg-with-footer^{tree}) &&
+	git cherry-pick -x $sha1 &&
+	cat <<-EOF >expect &&
+		title
+
+		Signed-off-by: a
+		(cherry picked from commit $sha1)
+	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) &&
Signature · 1 line
2.13.0.rc0.306.g87b477812d-goog
Reviewed-by on v2 1/1
#14 lore raw

Re: [PATCH v2] sequencer: require trailing NL in footers

Quoted 3 lines · Jonathan Tan

---

[...]

Quoted 3 lines
 sequencer.c              | 11 +++++++++++
 t/t3511-cherry-pick-x.sh | 14 ++++++++++++++
 2 files changed, 25 insertions(+)

This is still pretty subtle (using the added newline that is added after a non-footer to turn the invalid footer into a valid one), but

* it is clear from the code that it will work correctly
* the new test ensures we'll continue to support this case
* it is understandable after a little head scratching
* I'm hoping the subtlety will go away once the code learns to deal with ordinary non-footer text that has a missing newline

[...]

t/t3511-cherry-pick-x.sh test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo · line 213

Quoted 9 lines
t/t3511-cherry-pick-x.sh +3 -0
@@ -208,6 +208,20 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x handles commits with no NL at end of message' '
+	pristine_detach initial &&
+	sha1=$(printf "title\n\nSigned-off-by: a" | git commit-tree -p initial mesg-with-footer^{tree}) &&

nit: Should this use a more typical sign-off line with an email address, to avoid a false-positive success in case git becomes more strict about its signoffs in the future?

Something like

	printf "title\n\nSigned-off-by: S. I. Gner <[email protected]>" >msg &&
	sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
	...

Thanks,
Jonathan

#15 lore raw

Re: [PATCH v2] sequencer: require trailing NL in footers

Quoted 12 lines · Jonathan Nieder
Quoted 3 lines · Jonathan Tan

---

[...]

Quoted 3 lines
 sequencer.c              | 11 +++++++++++
 t/t3511-cherry-pick-x.sh | 14 ++++++++++++++
 2 files changed, 25 insertions(+)
Quoted 8 lines

This is still pretty subtle (using the added newline that is added after a non-footer to turn the invalid footer into a valid one), but

* it is clear from the code that it will work correctly
* the new test ensures we'll continue to support this case
* it is understandable after a little head scratching
* I'm hoping the subtlety will go away once the code learns to deal with ordinary non-footer text that has a missing newline

Hmph, perhaps we need another test that documents a known failure as well in the meantime?

Quoted 15 lines

[...]

t/t3511-cherry-pick-x.sh test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo · line 213

Quoted 9 lines
t/t3511-cherry-pick-x.sh +3 -0
@@ -208,6 +208,20 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x handles commits with no NL at end of message' '
+	pristine_detach initial &&
+	sha1=$(printf "title\n\nSigned-off-by: a" | git commit-tree -p initial mesg-with-footer^{tree}) &&

nit: Should this use a more typical sign-off line with an email address, to avoid a false-positive success in case git becomes more strict about its signoffs in the future?

Quoted 5 lines

Something like

	printf "title\n\nSigned-off-by: S. I. Gner <[email protected]>" >msg &&
	sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
	...

That is a good point and has an added benefit that the test script becomes easier to follow.

 t/t3511-cherry-pick-x.sh | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
t/t3511-cherry-pick-x.sh +4 -2
index 6f518020b2..c2b143802d 100755
@@ -210,12 +210,14 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo
 
 test_expect_success 'cherry-pick -x handles commits with no NL at end of message' '
 	pristine_detach initial &&
-	sha1=$(printf "title\n\nSigned-off-by: a" | git commit-tree -p initial mesg-with-footer^{tree}) &&
+	signer="S. I. Gner <[email protected]>" &&
+	printf "title\n\nSigned-off-by: %s" "$signer" >msg &&
+	sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
 	git cherry-pick -x $sha1 &&
 	cat <<-EOF >expect &&
 		title
 
-		Signed-off-by: a
+		Signed-off-by: $signer
 		(cherry picked from commit $sha1)
 	EOF
 	git log -1 --pretty=format:%B >actual &&
#16 lore raw

Re: [PATCH v2] sequencer: require trailing NL in footers

Hi Junio,

Quoted 19 lines · Junio C Hamano
Quoted 17 lines · Jonathan Nieder

Jonathan Tan wrote:

[...]

t/t3511-cherry-pick-x.sh test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo · line 213

Quoted 9 lines
t/t3511-cherry-pick-x.sh +3 -0
@@ -208,6 +208,20 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo
 	test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x handles commits with no NL at end of message' '
+	pristine_detach initial &&
+	sha1=$(printf "title\n\nSigned-off-by: a" | git commit-tree -p initial mesg-with-footer^{tree}) &&

nit: Should this use a more typical sign-off line with an email address, to avoid a false-positive success in case git becomes more strict about its signoffs in the future?

Quoted 8 lines
Quoted 5 lines

Something like

	printf "title\n\nSigned-off-by: S. I. Gner <[email protected]>" >msg &&
	sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
	...

That is a good point and has an added benefit that the test script becomes easier to follow.

If you already try to make it easier to follow, you might just as well go the whole nine yards:

Quoted 11 lines
t/t3511-cherry-pick-x.sh +2 -1
index 6f518020b2..c2b143802d 100755
@@ -210,12 +210,14 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo
 
 test_expect_success 'cherry-pick -x handles commits with no NL at end of message' '
 	pristine_detach initial &&
-	sha1=$(printf "title\n\nSigned-off-by: a" | git commit-tree -p initial mesg-with-footer^{tree}) &&
+	signer="S. I. Gner <[email protected]>" &&
+	printf "title\n\nSigned-off-by: %s" "$signer" >msg &&

t/t3511-cherry-pick-x.sh test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo · line 223

Quoted 10 lines
t/t3511-cherry-pick-x.sh +2 -1
+	sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
 	git cherry-pick -x $sha1 &&
 	cat <<-EOF >expect &&
 		title
 
-		Signed-off-by: a
+		Signed-off-by: $signer
 		(cherry picked from commit $sha1)
 	EOF
 	git log -1 --pretty=format:%B >actual &&

It is even easier to read --format=%B than --pretty=format:%B, and as %B does *not* indent, the indentation in the lines writing the expect file is bogus anyway. And with the msg file having most of the stuff already, we Do Not Need To Repeat Ourselves:

	printf '\n(cherry picked from commit %s)\n' $sha1 >>msg &&
	git log -1 --format=%B >actual &&
	test_cmp msg actual

Ciao,
Dscho

#10 lore raw

Re: [PATCH] sequencer: require trailing NL in footers

In reply to Brian Norris
Quoted 5 lines · Brian Norris
Quoted 2 lines · Stefan Beller

In that case: Is it needed to hint at how this bug occurred in the wild? (A different Git implementation, which may be fixed now?)

I might contact the original author

Original author was using 'stgit', FYI.

#17 lore raw

PATCH v3 1/1 sequencer: add newline before adding footers

In reply to Jonathan Tan

When encountering a commit message that does not end in a newline, sequencer does not complete the line before determining if a blank line should be added. This causes the "(cherry picked..." and sign-off lines to sometimes appear on the same line as the last line of the commit message.

This behavior was introduced by commit 967dfd4 ("sequencer: use trailer's trailer layout", 2016-11-29). However, a revert of that commit would not resolve this issue completely: prior to that commit, a conforming footer was deemed to be non-conforming by has_conforming_footer() if there was no terminating newline, resulting in both conforming and non-conforming footers being treated the same when they should not be.

Resolve this issue, both for conforming and non-conforming footers, and in both do_pick_commit() and append_signoff(), by always adding a newline to the commit message if it does not end in one before checking the footer for conformity.

---

The failure in the case of non-footers not being terminated by a newline turns out to be quite easy to fix, so I've added that fix here. I think this makes the overall fix more obvious too.

I've used Jonathan Nieder's and Dscho's suggestions for the tests (except for --format vs --pretty, since --format seems to add an extra newline to the output).

 sequencer.c              | 11 ++++-------
 t/t3511-cherry-pick-x.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 7 deletions(-)
sequencer.c +4 -7
index 77afecaeb..ffac95545 100644
@@ -1045,6 +1045,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 			strbuf_addstr(&msgbuf, p);
 
 		if (opts->record_origin) {
+			strbuf_complete_line(&msgbuf);
 			if (!has_conforming_footer(&msgbuf, NULL, 0))
 				strbuf_addch(&msgbuf, '\n');
 			strbuf_addstr(&msgbuf, cherry_picked_prefix);
@@ -2335,6 +2336,9 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 				getenv("GIT_COMMITTER_EMAIL")));
 	strbuf_addch(&sob, '\n');
 
+	if (!ignore_footer)
+		strbuf_complete_line(msgbuf);
+
 	/*
 	 * If the whole message buffer is equal to the sob, pretend that we
 	 * found a conforming footer with a matching sob
@@ -2355,13 +2359,6 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 			 * the title and body to be filled in by the user.
 			 */
 			append_newlines = "\n\n";
-		} else if (msgbuf->buf[len - 1] != '\n') {
-			/*
-			 * Incomplete line.  Complete the line and add a
-			 * blank one so that there is an empty line between
-			 * the message body and the sob.
-			 */
-			append_newlines = "\n\n";
 		} else if (len == 1) {
 			/*
 			 * Buffer contains a single newline.  Add another
t/t3511-cherry-pick-x.sh +44 -0

50 lines not on this page · raw message

Signature · 1 line
2.13.0.rc0.306.g87b477812d-goog