From 00ebc977484e18c03160b6319322858c088bce55 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 26 May 2011 16:41:18 -0400 Subject: [PATCH 1/5] t: test subject handling in format-patch / am pipeline Commit a1f6baa (format-patch: wrap long header lines, 2011-02-23) changed format-patch's behavior with respect to long header lines, but made no accompanying changes to the receiving side. It was thought that "git am" would handle these folded subjects fine, but there is a regression when using "am -k". Let's add a test documenting this. While we're at it, let's give more complete test coverage to document what should be happening in each case. We test three types of subjects: a short one, one long enough to require wrapping, and a multiline subject. For each, we test these three combinations: format-patch | am format-patch -k | am format-patch -k | am -k We don't bother testing "format-patch | am -k", which is nonsense (you will be adding in [PATCH] cruft to each subject). This reveals the regression above (long subjects have linebreaks introduced via "format-patch -k | am -k"), as well as an existing non-optimal behavior (multiline subjects are not preserved using "-k"). Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t4152-am-subjects.sh | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 t/t4152-am-subjects.sh diff --git a/t/t4152-am-subjects.sh b/t/t4152-am-subjects.sh new file mode 100755 index 0000000000..7222c06b80 --- /dev/null +++ b/t/t4152-am-subjects.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +test_description='test subject preservation with format-patch | am' +. ./test-lib.sh + +make_patches() { + type=$1 + subject=$2 + test_expect_success "create patches with $type subject" ' + git reset --hard baseline && + echo $type >file && + git commit -a -m "$subject" && + git format-patch -1 --stdout >$type.patch && + git format-patch -1 --stdout -k >$type-k.patch + ' +} + +check_subject() { + git reset --hard baseline && + git am $2 $1.patch && + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +} + +test_expect_success 'setup baseline commit' ' + test_commit baseline file +' + +SHORT_SUBJECT='short subject' +make_patches short "$SHORT_SUBJECT" + +LONG_SUBJECT1='this is a long subject that is virtually guaranteed' +LONG_SUBJECT2='to require wrapping via format-patch if it is all' +LONG_SUBJECT3='going to appear on a single line' +LONG_SUBJECT="$LONG_SUBJECT1 $LONG_SUBJECT2 $LONG_SUBJECT3" +make_patches long "$LONG_SUBJECT" + +MULTILINE_SUBJECT="$LONG_SUBJECT1 +$LONG_SUBJECT2 +$LONG_SUBJECT3" +make_patches multiline "$MULTILINE_SUBJECT" + +echo "$SHORT_SUBJECT" >expect +test_expect_success 'short subject preserved (format-patch | am)' ' + check_subject short +' +test_expect_success 'short subject preserved (format-patch -k | am)' ' + check_subject short-k +' +test_expect_success 'short subject preserved (format-patch -k | am -k)' ' + check_subject short-k -k +' + +echo "$LONG_SUBJECT" >expect +test_expect_success 'long subject preserved (format-patch | am)' ' + check_subject long +' +test_expect_success 'long subject preserved (format-patch -k | am)' ' + check_subject long-k +' +test_expect_failure 'long subject preserved (format-patch -k | am -k)' ' + check_subject long-k -k +' + +echo "$LONG_SUBJECT" >expect +test_expect_success 'multiline subject unwrapped (format-patch | am)' ' + check_subject multiline +' +test_expect_success 'multiline subject unwrapped (format-patch -k | am)' ' + check_subject multiline-k +' +echo "$MULTILINE_SUBJECT" >expect +test_expect_failure 'multiline subject preserved (format-patch -k | am -k)' ' + check_subject multiline-k -k +' + +test_done From 5b38456ec7bd0229bb35146ab8a905c4b63daeec Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 26 May 2011 16:53:38 -0400 Subject: [PATCH 2/5] mailinfo: always clean up rfc822 header folding Without the "-k" option, mailinfo will convert a folded subject header like: Subject: this is a subject that doesn't fit on one line into a single line. With "-k", however, we assumed that these newlines were significant and represented something that the sending side would want us to preserve. For messages created by format-patch, this assumption was broken by a1f6baa (format-patch: wrap long header lines, 2011-02-23). For messages sent by arbitrary MUAs, this was probably never a good assumption to make, as they may have been folding subjects in accordance with rfc822's line length recommendations all along. This patch now joins folded lines with a single whitespace character. This treats header folding purely as a syntactic feature of the transport mechanism, not as something that format-patch is trying to tell us about the original subject. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/mailinfo.c | 2 +- t/t4152-am-subjects.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c index 71e6262a87..bfb32b7233 100644 --- a/builtin/mailinfo.c +++ b/builtin/mailinfo.c @@ -400,7 +400,7 @@ static int read_one_header_line(struct strbuf *line, FILE *in) break; if (strbuf_getline(&continuation, in, '\n')) break; - continuation.buf[0] = '\n'; + continuation.buf[0] = ' '; strbuf_rtrim(&continuation); strbuf_addbuf(line, &continuation); } diff --git a/t/t4152-am-subjects.sh b/t/t4152-am-subjects.sh index 7222c06b80..37e5c0361c 100755 --- a/t/t4152-am-subjects.sh +++ b/t/t4152-am-subjects.sh @@ -58,7 +58,7 @@ test_expect_success 'long subject preserved (format-patch | am)' ' test_expect_success 'long subject preserved (format-patch -k | am)' ' check_subject long-k ' -test_expect_failure 'long subject preserved (format-patch -k | am -k)' ' +test_expect_success 'long subject preserved (format-patch -k | am -k)' ' check_subject long-k -k ' From 8b8a53744f60274ef07e3a2a51995129c8d42f38 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 26 May 2011 18:27:24 -0400 Subject: [PATCH 3/5] pretty: add pp_commit_easy function for simple callers Many callers don't actually care about the pretty print context at all; let's just give them a simple way of pretty-printing a commit without having to create a context struct. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/branch.c | 4 +--- builtin/checkout.c | 3 +-- builtin/log.c | 4 +--- builtin/shortlog.c | 3 +-- builtin/show-branch.c | 3 +-- commit.h | 2 ++ pretty.c | 7 +++++++ 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/builtin/branch.c b/builtin/branch.c index 9e546e4a83..d8f15221ed 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -436,9 +436,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, commit = item->commit; if (commit && !parse_commit(commit)) { - struct pretty_print_context ctx = {0}; - pretty_print_commit(CMIT_FMT_ONELINE, commit, - &subject, &ctx); + pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject); sub = subject.buf; } diff --git a/builtin/checkout.c b/builtin/checkout.c index 757f9a08dd..c1759dc3a4 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -300,9 +300,8 @@ static void show_local_changes(struct object *head, struct diff_options *opts) static void describe_detached_head(char *msg, struct commit *commit) { struct strbuf sb = STRBUF_INIT; - struct pretty_print_context ctx = {0}; parse_commit(commit); - pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, &ctx); + pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb); fprintf(stderr, "%s %s... %s\n", msg, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf); strbuf_release(&sb); diff --git a/builtin/log.c b/builtin/log.c index d8c6c28d2f..cedfdb6d42 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1439,9 +1439,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) if (verbose) { struct strbuf buf = STRBUF_INIT; - struct pretty_print_context ctx = {0}; - pretty_print_commit(CMIT_FMT_ONELINE, commit, - &buf, &ctx); + pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf); printf("%c %s %s\n", sign, find_unique_abbrev(commit->object.sha1, abbrev), buf.buf); diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 1a21e4b053..90877b5fe2 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -141,9 +141,8 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) const char *author = NULL, *buffer; struct strbuf buf = STRBUF_INIT; struct strbuf ufbuf = STRBUF_INIT; - struct pretty_print_context ctx = {0}; - pretty_print_commit(CMIT_FMT_RAW, commit, &buf, &ctx); + pp_commit_easy(CMIT_FMT_RAW, commit, &buf); buffer = buf.buf; while (*buffer && *buffer != '\n') { const char *eol = strchr(buffer, '\n'); diff --git a/builtin/show-branch.c b/builtin/show-branch.c index da695815e2..a5fc2aa884 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -293,8 +293,7 @@ static void show_one_commit(struct commit *commit, int no_name) struct commit_name *name = commit->util; if (commit->object.parsed) { - struct pretty_print_context ctx = {0}; - pretty_print_commit(CMIT_FMT_ONELINE, commit, &pretty, &ctx); + pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); pretty_str = pretty.buf; } if (!prefixcmp(pretty_str, "[PATCH] ")) diff --git a/commit.h b/commit.h index eb6c5af1f6..3e733be1ac 100644 --- a/commit.h +++ b/commit.h @@ -98,6 +98,8 @@ extern void format_commit_message(const struct commit *commit, extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, struct strbuf *sb, const struct pretty_print_context *context); +extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb); void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding); diff --git a/pretty.c b/pretty.c index 305ff85d74..75a9a416a4 100644 --- a/pretty.c +++ b/pretty.c @@ -1288,3 +1288,10 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, free(reencoded); } + +void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb) +{ + struct pretty_print_context pp = {0}; + pretty_print_commit(fmt, commit, sb, &pp); +} From 6bf139440c192e157b9c0dab701fa2100fbb1e1e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 26 May 2011 18:27:49 -0400 Subject: [PATCH 4/5] clean up calling conventions for pretty.c functions We have a pretty_print_context representing the parameters for a pretty-print session, but we did not use it uniformly. As a result, functions kept growing more and more arguments. Let's clean this up in a few ways: 1. All pretty-print pp_* functions now take a context. This lets us reduce the number of arguments to these functions, since we were just passing around the context values separately. 2. The context argument now has a cmit_fmt field, which was passed around separately. That's one less argument per function. 3. The context argument always comes first, which makes calling a little more uniform. This drops lines from some callers, and adds lines in a few places (because we need an extra line to set the context's fmt field). Overall, we don't save many lines, but the lines that are there are a lot simpler and more readable. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/log.c | 21 +++++----- builtin/merge.c | 3 +- builtin/rev-list.c | 3 +- builtin/shortlog.c | 3 +- commit.h | 19 +++++---- log-tree.c | 3 +- pretty.c | 99 ++++++++++++++++++++++------------------------ 7 files changed, 75 insertions(+), 76 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index cedfdb6d42..8d842cbcac 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -327,9 +327,11 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix) static void show_tagger(char *buf, int len, struct rev_info *rev) { struct strbuf out = STRBUF_INIT; + struct pretty_print_context pp = {0}; - pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode, - get_log_output_encoding()); + pp.fmt = rev->commit_format; + pp.date_mode = rev->date_mode; + pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding()); printf("%s", out.buf); strbuf_release(&out); } @@ -715,10 +717,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, int nr, struct commit **list, struct commit *head) { const char *committer; - const char *subject_start = NULL; const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n"; const char *msg; - const char *extra_headers = rev->extra_headers; struct shortlog log; struct strbuf sb = STRBUF_INIT; int i; @@ -726,6 +726,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, struct diff_options opts; int need_8bit_cte = 0; struct commit *commit = NULL; + struct pretty_print_context pp = {0}; if (rev->commit_format != CMIT_FMT_EMAIL) die("Cover letter needs email format"); @@ -757,7 +758,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, free(commit); } - log_write_email_headers(rev, head, &subject_start, &extra_headers, + log_write_email_headers(rev, head, &pp.subject, &pp.after_subject, &need_8bit_cte); for (i = 0; !need_8bit_cte && i < nr; i++) @@ -765,11 +766,11 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout, need_8bit_cte = 1; msg = body; - pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822, - encoding); - pp_title_line(CMIT_FMT_EMAIL, &msg, &sb, subject_start, extra_headers, - encoding, need_8bit_cte); - pp_remainder(CMIT_FMT_EMAIL, &msg, &sb, 0); + pp.fmt = CMIT_FMT_EMAIL; + pp.date_mode = DATE_RFC2822; + pp_user_info(&pp, NULL, &sb, committer, encoding); + pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte); + pp_remainder(&pp, &msg, &sb, 0); printf("%s\n", sb.buf); strbuf_release(&sb); diff --git a/builtin/merge.c b/builtin/merge.c index 42fff387e6..c902e81cf1 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -333,13 +333,14 @@ static void squash_message(void) ctx.abbrev = rev.abbrev; ctx.date_mode = rev.date_mode; + ctx.fmt = rev.commit_format; strbuf_addstr(&out, "Squashed commit of the following:\n"); while ((commit = get_revision(&rev)) != NULL) { strbuf_addch(&out, '\n'); strbuf_addf(&out, "commit %s\n", sha1_to_hex(commit->object.sha1)); - pretty_print_commit(rev.commit_format, commit, &out, &ctx); + pretty_print_commit(&ctx, commit, &out); } if (write(fd, out.buf, out.len) < 0) die_errno("Writing SQUASH_MSG"); diff --git a/builtin/rev-list.c b/builtin/rev-list.c index ba27d39f97..0ec42fc75c 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -108,7 +108,8 @@ static void show_commit(struct commit *commit, void *data) struct pretty_print_context ctx = {0}; ctx.abbrev = revs->abbrev; ctx.date_mode = revs->date_mode; - pretty_print_commit(revs->commit_format, commit, &buf, &ctx); + ctx.fmt = revs->commit_format; + pretty_print_commit(&ctx, commit, &buf); if (revs->graph) { if (buf.len) { if (revs->commit_format != CMIT_FMT_ONELINE) diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 90877b5fe2..074fd26048 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -161,11 +161,12 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) sha1_to_hex(commit->object.sha1)); if (log->user_format) { struct pretty_print_context ctx = {0}; + ctx.fmt = CMIT_FMT_USERFORMAT; ctx.abbrev = log->abbrev; ctx.subject = ""; ctx.after_subject = ""; ctx.date_mode = DATE_NORMAL; - pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &ufbuf, &ctx); + pretty_print_commit(&ctx, commit, &ufbuf); buffer = ufbuf.buf; } else if (*buffer) { buffer++; diff --git a/commit.h b/commit.h index 3e733be1ac..2935740a8d 100644 --- a/commit.h +++ b/commit.h @@ -70,6 +70,7 @@ enum cmit_fmt { struct pretty_print_context { + enum cmit_fmt fmt; int abbrev; const char *subject; const char *after_subject; @@ -95,22 +96,20 @@ extern void userformat_find_requirements(const char *fmt, struct userformat_want extern void format_commit_message(const struct commit *commit, const char *format, struct strbuf *sb, const struct pretty_print_context *context); -extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - struct strbuf *sb, - const struct pretty_print_context *context); +extern void pretty_print_commit(const struct pretty_print_context *pp, + const struct commit *commit, + struct strbuf *sb); extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit, struct strbuf *sb); -void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, - const char *line, enum date_mode dmode, - const char *encoding); -void pp_title_line(enum cmit_fmt fmt, +void pp_user_info(const struct pretty_print_context *pp, + const char *what, struct strbuf *sb, + const char *line, const char *encoding); +void pp_title_line(const struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, - const char *subject, - const char *after_subject, const char *encoding, int need_8bit_cte); -void pp_remainder(enum cmit_fmt fmt, +void pp_remainder(const struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, int indent); diff --git a/log-tree.c b/log-tree.c index b46ed3baef..0d8cc7af2c 100644 --- a/log-tree.c +++ b/log-tree.c @@ -505,7 +505,8 @@ void show_log(struct rev_info *opt) ctx.abbrev = opt->diffopt.abbrev; ctx.after_subject = extra_headers; ctx.reflog_info = opt->reflog_info; - pretty_print_commit(opt->commit_format, commit, &msgbuf, &ctx); + ctx.fmt = opt->commit_format; + pretty_print_commit(&ctx, commit, &msgbuf); if (opt->add_signoff) append_signoff(&msgbuf, opt->add_signoff); diff --git a/pretty.c b/pretty.c index 75a9a416a4..1bb7e075ea 100644 --- a/pretty.c +++ b/pretty.c @@ -266,16 +266,16 @@ needquote: strbuf_addstr(sb, "?="); } -void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, - const char *line, enum date_mode dmode, - const char *encoding) +void pp_user_info(const struct pretty_print_context *pp, + const char *what, struct strbuf *sb, + const char *line, const char *encoding) { char *date; int namelen; unsigned long time; int tz; - if (fmt == CMIT_FMT_ONELINE) + if (pp->fmt == CMIT_FMT_ONELINE) return; date = strchr(line, '>'); if (!date) @@ -284,7 +284,7 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); - if (fmt == CMIT_FMT_EMAIL) { + if (pp->fmt == CMIT_FMT_EMAIL) { char *name_tail = strchr(line, '<'); int display_name_length; int final_line; @@ -307,18 +307,18 @@ void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, strbuf_addch(sb, '\n'); } else { strbuf_addf(sb, "%s: %.*s%.*s\n", what, - (fmt == CMIT_FMT_FULLER) ? 4 : 0, + (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0, " ", namelen, line); } - switch (fmt) { + switch (pp->fmt) { case CMIT_FMT_MEDIUM: - strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, pp->date_mode)); break; case CMIT_FMT_EMAIL: strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); break; case CMIT_FMT_FULLER: - strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); + strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, pp->date_mode)); break; default: /* notin' */ @@ -349,12 +349,12 @@ static const char *skip_empty_lines(const char *msg) return msg; } -static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, - const struct commit *commit, int abbrev) +static void add_merge_info(const struct pretty_print_context *pp, + struct strbuf *sb, const struct commit *commit) { struct commit_list *parent = commit->parents; - if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || + if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) || !parent || !parent->next) return; @@ -363,8 +363,8 @@ static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, while (parent) { struct commit *p = parent->item; const char *hex = NULL; - if (abbrev) - hex = find_unique_abbrev(p->object.sha1, abbrev); + if (pp->abbrev) + hex = find_unique_abbrev(p->object.sha1, pp->abbrev); if (!hex) hex = sha1_to_hex(p->object.sha1); parent = parent->next; @@ -1061,9 +1061,7 @@ void format_commit_message(const struct commit *commit, free(context.message); } -static void pp_header(enum cmit_fmt fmt, - int abbrev, - enum date_mode dmode, +static void pp_header(const struct pretty_print_context *pp, const char *encoding, const struct commit *commit, const char **msg_p, @@ -1083,7 +1081,7 @@ static void pp_header(enum cmit_fmt fmt, /* End of header */ return; - if (fmt == CMIT_FMT_RAW) { + if (pp->fmt == CMIT_FMT_RAW) { strbuf_add(sb, line, linelen); continue; } @@ -1103,7 +1101,7 @@ static void pp_header(enum cmit_fmt fmt, ; /* with enough slop */ strbuf_grow(sb, num * 50 + 20); - add_merge_info(fmt, sb, commit, abbrev); + add_merge_info(pp, sb, commit); parents_shown = 1; } @@ -1114,21 +1112,19 @@ static void pp_header(enum cmit_fmt fmt, */ if (!memcmp(line, "author ", 7)) { strbuf_grow(sb, linelen + 80); - pp_user_info("Author", fmt, sb, line + 7, dmode, encoding); + pp_user_info(pp, "Author", sb, line + 7, encoding); } if (!memcmp(line, "committer ", 10) && - (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { + (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) { strbuf_grow(sb, linelen + 80); - pp_user_info("Commit", fmt, sb, line + 10, dmode, encoding); + pp_user_info(pp, "Commit", sb, line + 10, encoding); } } } -void pp_title_line(enum cmit_fmt fmt, +void pp_title_line(const struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, - const char *subject, - const char *after_subject, const char *encoding, int need_8bit_cte) { @@ -1138,8 +1134,8 @@ void pp_title_line(enum cmit_fmt fmt, *msg_p = format_subject(&title, *msg_p, " "); strbuf_grow(sb, title.len + 1024); - if (subject) { - strbuf_addstr(sb, subject); + if (pp->subject) { + strbuf_addstr(sb, pp->subject); add_rfc2047(sb, title.buf, title.len, encoding); } else { strbuf_addbuf(sb, &title); @@ -1153,16 +1149,16 @@ void pp_title_line(enum cmit_fmt fmt, "Content-Transfer-Encoding: 8bit\n"; strbuf_addf(sb, header_fmt, encoding); } - if (after_subject) { - strbuf_addstr(sb, after_subject); + if (pp->after_subject) { + strbuf_addstr(sb, pp->after_subject); } - if (fmt == CMIT_FMT_EMAIL) { + if (pp->fmt == CMIT_FMT_EMAIL) { strbuf_addch(sb, '\n'); } strbuf_release(&title); } -void pp_remainder(enum cmit_fmt fmt, +void pp_remainder(const struct pretty_print_context *pp, const char **msg_p, struct strbuf *sb, int indent) @@ -1179,7 +1175,7 @@ void pp_remainder(enum cmit_fmt fmt, if (is_empty_line(line, &linelen)) { if (first) continue; - if (fmt == CMIT_FMT_SHORT) + if (pp->fmt == CMIT_FMT_SHORT) break; } first = 0; @@ -1204,19 +1200,19 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding return logmsg_reencode(commit, encoding); } -void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - struct strbuf *sb, - const struct pretty_print_context *context) +void pretty_print_commit(const struct pretty_print_context *pp, + const struct commit *commit, + struct strbuf *sb) { unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; char *reencoded; const char *encoding; - int need_8bit_cte = context->need_8bit_cte; + int need_8bit_cte = pp->need_8bit_cte; - if (fmt == CMIT_FMT_USERFORMAT) { - format_commit_message(commit, user_format, sb, context); + if (pp->fmt == CMIT_FMT_USERFORMAT) { + format_commit_message(commit, user_format, sb, pp); return; } @@ -1225,14 +1221,14 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, msg = reencoded; } - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL) indent = 0; /* * We need to check and emit Content-type: to mark it * as 8-bit if we haven't done so. */ - if (fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) { + if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) { int i, ch, in_body; for (in_body = i = 0; (ch = msg[i]); i++) { @@ -1251,9 +1247,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, } } - pp_header(fmt, context->abbrev, context->date_mode, encoding, - commit, &msg, sb); - if (fmt != CMIT_FMT_ONELINE && !context->subject) { + pp_header(pp, encoding, commit, &msg, sb); + if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) { strbuf_addch(sb, '\n'); } @@ -1261,17 +1256,16 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, msg = skip_empty_lines(msg); /* These formats treat the title line specially. */ - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, sb, context->subject, - context->after_subject, encoding, need_8bit_cte); + if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL) + pp_title_line(pp, &msg, sb, encoding, need_8bit_cte); beginning_of_body = sb->len; - if (fmt != CMIT_FMT_ONELINE) - pp_remainder(fmt, &msg, sb, indent); + if (pp->fmt != CMIT_FMT_ONELINE) + pp_remainder(pp, &msg, sb, indent); strbuf_rtrim(sb); /* Make sure there is an EOLN for the non-oneline case */ - if (fmt != CMIT_FMT_ONELINE) + if (pp->fmt != CMIT_FMT_ONELINE) strbuf_addch(sb, '\n'); /* @@ -1279,10 +1273,10 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, * format. Make sure we did not strip the blank line * between the header and the body. */ - if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) strbuf_addch(sb, '\n'); - if (context->show_notes) + if (pp->show_notes) format_display_notes(commit->object.sha1, sb, encoding, NOTES_SHOW_HEADER | NOTES_INDENT); @@ -1293,5 +1287,6 @@ void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit, struct strbuf *sb) { struct pretty_print_context pp = {0}; - pretty_print_commit(fmt, commit, sb, &pp); + pp.fmt = fmt; + pretty_print_commit(&pp, commit, sb); } From 9553d2b26395d9a19bf60875784661090f607f4a Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 26 May 2011 18:28:17 -0400 Subject: [PATCH 5/5] format-patch: preserve subject newlines with -k In older versions of git, we used rfc822 header folding to indicate that the original subject line had multiple lines in it. But since a1f6baa (format-patch: wrap long header lines, 2011-02-23), we now use header folding whenever there is a long line. This means that "git am" cannot trust header folding as a sign from format-patch that newlines should be preserved. Instead, format-patch needs to signal more explicitly that the newlines are significant. This patch does so by rfc2047-encoding the newlines in the subject line. No changes are needed on the "git am" end; it already decodes the newlines properly. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- builtin/log.c | 1 + commit.h | 1 + log-tree.c | 1 + pretty.c | 3 ++- revision.h | 3 ++- t/t4152-am-subjects.sh | 2 +- 6 files changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin/log.c b/builtin/log.c index 8d842cbcac..0e46e5ae95 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1131,6 +1131,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) die ("-n and -k are mutually exclusive."); if (keep_subject && subject_prefix) die ("--subject-prefix and -k are mutually exclusive."); + rev.preserve_subject = keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); if (argc > 1) diff --git a/commit.h b/commit.h index 2935740a8d..e985dcc6e5 100644 --- a/commit.h +++ b/commit.h @@ -74,6 +74,7 @@ struct pretty_print_context int abbrev; const char *subject; const char *after_subject; + int preserve_subject; enum date_mode date_mode; int need_8bit_cte; int show_notes; diff --git a/log-tree.c b/log-tree.c index 0d8cc7af2c..0c41789356 100644 --- a/log-tree.c +++ b/log-tree.c @@ -504,6 +504,7 @@ void show_log(struct rev_info *opt) ctx.date_mode = opt->date_mode; ctx.abbrev = opt->diffopt.abbrev; ctx.after_subject = extra_headers; + ctx.preserve_subject = opt->preserve_subject; ctx.reflog_info = opt->reflog_info; ctx.fmt = opt->commit_format; pretty_print_commit(&ctx, commit, &msgbuf); diff --git a/pretty.c b/pretty.c index 1bb7e075ea..193bafd835 100644 --- a/pretty.c +++ b/pretty.c @@ -1131,7 +1131,8 @@ void pp_title_line(const struct pretty_print_context *pp, struct strbuf title; strbuf_init(&title, 80); - *msg_p = format_subject(&title, *msg_p, " "); + *msg_p = format_subject(&title, *msg_p, + pp->preserve_subject ? "\n" : " "); strbuf_grow(sb, title.len + 1024); if (pp->subject) { diff --git a/revision.h b/revision.h index 05659c64ac..f8ddd83e79 100644 --- a/revision.h +++ b/revision.h @@ -90,7 +90,8 @@ struct rev_info { abbrev_commit:1, use_terminator:1, missing_newline:1, - date_mode_explicit:1; + date_mode_explicit:1, + preserve_subject:1; unsigned int disable_stdin:1; enum date_mode date_mode; diff --git a/t/t4152-am-subjects.sh b/t/t4152-am-subjects.sh index 37e5c0361c..4c68245aca 100755 --- a/t/t4152-am-subjects.sh +++ b/t/t4152-am-subjects.sh @@ -70,7 +70,7 @@ test_expect_success 'multiline subject unwrapped (format-patch -k | am)' ' check_subject multiline-k ' echo "$MULTILINE_SUBJECT" >expect -test_expect_failure 'multiline subject preserved (format-patch -k | am -k)' ' +test_expect_success 'multiline subject preserved (format-patch -k | am -k)' ' check_subject multiline-k -k '