Skip to content

Commit 4cff50b

Browse files
committed
Merge branch 'jt/mailinfo-fold-in-body-headers'
When "git format-patch --stdout" output is placed as an in-body header and it uses the RFC2822 header folding, "git am" failed to put the header line back into a single logical line. The underlying "git mailinfo" was taught to handle this properly. * jt/mailinfo-fold-in-body-headers: mailinfo: handle in-body header continuations mailinfo: make is_scissors_line take plain char * mailinfo: separate in-body header processing
2 parents 21f862b + 6b4b013 commit 4cff50b

12 files changed

+159
-36
lines changed

mailinfo.c

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -495,26 +495,26 @@ static int check_header(struct mailinfo *mi,
495495
goto check_header_out;
496496
}
497497

498-
/* for inbody stuff */
499-
if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
500-
ret = is_format_patch_separator(line->buf + 1, line->len - 1);
501-
goto check_header_out;
502-
}
503-
if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
504-
for (i = 0; header[i]; i++) {
505-
if (!strcmp("Subject", header[i])) {
506-
handle_header(&hdr_data[i], line);
507-
ret = 1;
508-
goto check_header_out;
509-
}
510-
}
511-
}
512-
513498
check_header_out:
514499
strbuf_release(&sb);
515500
return ret;
516501
}
517502

503+
/*
504+
* Returns 1 if the given line or any line beginning with the given line is an
505+
* in-body header (that is, check_header will succeed when passed
506+
* mi->s_hdr_data).
507+
*/
508+
static int is_inbody_header(const struct mailinfo *mi,
509+
const struct strbuf *line)
510+
{
511+
int i;
512+
for (i = 0; header[i]; i++)
513+
if (!mi->s_hdr_data[i] && cmp_header(line, header[i]))
514+
return 1;
515+
return 0;
516+
}
517+
518518
static void decode_transfer_encoding(struct mailinfo *mi, struct strbuf *line)
519519
{
520520
struct strbuf *ret;
@@ -572,37 +572,35 @@ static inline int patchbreak(const struct strbuf *line)
572572
return 0;
573573
}
574574

575-
static int is_scissors_line(const struct strbuf *line)
575+
static int is_scissors_line(const char *line)
576576
{
577-
size_t i, len = line->len;
577+
const char *c;
578578
int scissors = 0, gap = 0;
579-
int first_nonblank = -1;
580-
int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
581-
const char *buf = line->buf;
579+
const char *first_nonblank = NULL, *last_nonblank = NULL;
580+
int visible, perforation = 0, in_perforation = 0;
582581

583-
for (i = 0; i < len; i++) {
584-
if (isspace(buf[i])) {
582+
for (c = line; *c; c++) {
583+
if (isspace(*c)) {
585584
if (in_perforation) {
586585
perforation++;
587586
gap++;
588587
}
589588
continue;
590589
}
591-
last_nonblank = i;
592-
if (first_nonblank < 0)
593-
first_nonblank = i;
594-
if (buf[i] == '-') {
590+
last_nonblank = c;
591+
if (first_nonblank == NULL)
592+
first_nonblank = c;
593+
if (*c == '-') {
595594
in_perforation = 1;
596595
perforation++;
597596
continue;
598597
}
599-
if (i + 1 < len &&
600-
(!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
601-
!memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
598+
if ((!memcmp(c, ">8", 2) || !memcmp(c, "8<", 2) ||
599+
!memcmp(c, ">%", 2) || !memcmp(c, "%<", 2))) {
602600
in_perforation = 1;
603601
perforation += 2;
604602
scissors += 2;
605-
i++;
603+
c++;
606604
continue;
607605
}
608606
in_perforation = 0;
@@ -617,12 +615,60 @@ static int is_scissors_line(const struct strbuf *line)
617615
* than half of the perforation.
618616
*/
619617

620-
visible = last_nonblank - first_nonblank + 1;
618+
if (first_nonblank && last_nonblank)
619+
visible = last_nonblank - first_nonblank + 1;
620+
else
621+
visible = 0;
621622
return (scissors && 8 <= visible &&
622623
visible < perforation * 3 &&
623624
gap * 2 < perforation);
624625
}
625626

627+
static void flush_inbody_header_accum(struct mailinfo *mi)
628+
{
629+
if (!mi->inbody_header_accum.len)
630+
return;
631+
assert(check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0));
632+
strbuf_reset(&mi->inbody_header_accum);
633+
}
634+
635+
static int check_inbody_header(struct mailinfo *mi, const struct strbuf *line)
636+
{
637+
if (mi->inbody_header_accum.len &&
638+
(line->buf[0] == ' ' || line->buf[0] == '\t')) {
639+
if (mi->use_scissors && is_scissors_line(line->buf)) {
640+
/*
641+
* This is a scissors line; do not consider this line
642+
* as a header continuation line.
643+
*/
644+
flush_inbody_header_accum(mi);
645+
return 0;
646+
}
647+
strbuf_strip_suffix(&mi->inbody_header_accum, "\n");
648+
strbuf_addbuf(&mi->inbody_header_accum, line);
649+
return 1;
650+
}
651+
652+
flush_inbody_header_accum(mi);
653+
654+
if (starts_with(line->buf, ">From") && isspace(line->buf[5]))
655+
return is_format_patch_separator(line->buf + 1, line->len - 1);
656+
if (starts_with(line->buf, "[PATCH]") && isspace(line->buf[7])) {
657+
int i;
658+
for (i = 0; header[i]; i++)
659+
if (!strcmp("Subject", header[i])) {
660+
handle_header(&mi->s_hdr_data[i], line);
661+
return 1;
662+
}
663+
return 0;
664+
}
665+
if (is_inbody_header(mi, line)) {
666+
strbuf_addbuf(&mi->inbody_header_accum, line);
667+
return 1;
668+
}
669+
return 0;
670+
}
671+
626672
static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
627673
{
628674
assert(!mi->filter_stage);
@@ -633,7 +679,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
633679
}
634680

635681
if (mi->use_inbody_headers && mi->header_stage) {
636-
mi->header_stage = check_header(mi, line, mi->s_hdr_data, 0);
682+
mi->header_stage = check_inbody_header(mi, line);
637683
if (mi->header_stage)
638684
return 0;
639685
} else
@@ -646,7 +692,7 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
646692
if (convert_to_utf8(mi, line, mi->charset.buf))
647693
return 0; /* mi->input_error already set */
648694

649-
if (mi->use_scissors && is_scissors_line(line)) {
695+
if (mi->use_scissors && is_scissors_line(line->buf)) {
650696
int i;
651697

652698
strbuf_setlen(&mi->log_message, 0);
@@ -886,6 +932,8 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
886932
break;
887933
} while (!strbuf_getwholeline(line, mi->input, '\n'));
888934

935+
flush_inbody_header_accum(mi);
936+
889937
handle_body_out:
890938
strbuf_release(&prev);
891939
}
@@ -1001,6 +1049,7 @@ void setup_mailinfo(struct mailinfo *mi)
10011049
strbuf_init(&mi->email, 0);
10021050
strbuf_init(&mi->charset, 0);
10031051
strbuf_init(&mi->log_message, 0);
1052+
strbuf_init(&mi->inbody_header_accum, 0);
10041053
mi->header_stage = 1;
10051054
mi->use_inbody_headers = 1;
10061055
mi->content_top = mi->content;
@@ -1014,6 +1063,7 @@ void clear_mailinfo(struct mailinfo *mi)
10141063
strbuf_release(&mi->name);
10151064
strbuf_release(&mi->email);
10161065
strbuf_release(&mi->charset);
1066+
strbuf_release(&mi->inbody_header_accum);
10171067
free(mi->message_id);
10181068

10191069
for (i = 0; mi->p_hdr_data[i]; i++)

mailinfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct mailinfo {
2727
int patch_lines;
2828
int filter_stage; /* still reading log or are we copying patch? */
2929
int header_stage; /* still checking in-body headers? */
30+
struct strbuf inbody_header_accum;
3031
struct strbuf **p_hdr_data;
3132
struct strbuf **s_hdr_data;
3233

t/t4150-am.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,4 +977,27 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' '
977977
test_cmp msg out
978978
'
979979

980+
test_expect_success 'am works with multi-line in-body headers' '
981+
FORTY="String that has a length of more than forty characters" &&
982+
LONG="$FORTY $FORTY" &&
983+
rm -fr .git/rebase-apply &&
984+
git checkout -f first &&
985+
echo one >> file &&
986+
git commit -am "$LONG" --author="$LONG <[email protected]>" &&
987+
git format-patch --stdout -1 >patch &&
988+
# bump from, date, and subject down to in-body header
989+
perl -lpe "
990+
if (/^From:/) {
991+
print \"From: x <x\@example.com>\";
992+
print \"Date: Sat, 1 Jan 2000 00:00:00 +0000\";
993+
print \"Subject: x\n\";
994+
}
995+
" patch >msg &&
996+
git checkout HEAD^ &&
997+
git am msg &&
998+
# Ensure that the author and full message are present
999+
git cat-file commit HEAD | grep "^author.*[email protected]" &&
1000+
git cat-file commit HEAD | grep "^$LONG"
1001+
'
1002+
9801003
test_done

t/t5100-mailinfo.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ test_expect_success 'split sample box' \
1111
'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
1212
last=$(cat last) &&
1313
echo total is $last &&
14-
test $(cat last) = 17'
14+
test $(cat last) = 18'
1515

1616
check_mailinfo () {
1717
mail=$1 opt=$2

t/t5100/info0018

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Author: Another Thor
2+
3+
Subject: This one contains a tab and a space
4+
Date: Fri, 9 Jun 2006 00:44:16 -0700
5+

t/t5100/info0018--no-inbody-headers

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Author: A U Thor
2+
3+
Subject: check multiline inbody headers
4+
Date: Fri, 9 Jun 2006 00:44:16 -0700
5+

t/t5100/msg0015

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +0,0 @@
1-
- a list
2-
- of stuff

t/t5100/msg0018

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
a commit message
2+

t/t5100/msg0018--no-inbody-headers

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
From: Another Thor
2+
3+
Subject: This one contains
4+
a tab
5+
and a space
6+
7+
a commit message
8+

t/t5100/patch0018

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
diff --git a/foo b/foo
2+
index e69de29..d95f3ad 100644
3+
--- a/foo
4+
+++ b/foo
5+
@@ -0,0 +1 @@
6+
+content

0 commit comments

Comments
 (0)