Subject: [RFC] format-patch: Ensure that author and commit time are not lost

From: Björn Gustavsson <[email-masked]>
Date: Sun, 15 Nov 2009 14:25:21 +0100

'git format-patch' encodes the author of the commit in the From:
header and the time of the commit in the Date: header.
Depending on how the email is sent, however, those headers can be
lost before the email reaches its destination.

Therefore, if the sender of the email (i.e. the configuration
options user.name and user.email) are different from the author
of a commit, insert From: and Date: headers at the beginning of
the body of the commit message.

Signed-off-by: Björn Gustavsson <[email-masked]>
---
The code works, but is not appropriate for 'pu' because a fair
number of test cases will fail.

Some additional work is needed before this functionality can
be considered ready for 'pu'. Since I am rather busy at the
moment, I don't want to spend any more time on this unless I'll
get some indication that this functionality is useful for
someone else.

I would also want some input on whether it would be OK
to make this functionality unconditionally turned on so that
there will always From: and Date: headers in the commit
message.

I suspect that the answer is no, because there might be scripts
that parse the output of format-patch (perhaps to interface
with other source-code control systems).

Assuming that we need an option, should the default be to
produce the extra headers (to make sure that the original
author is not lost) or to not produce any extra headers
(for backwards compatibility)?

I suspect that the answer is that the default should be not
to generate any extra headers not to break any existing
scripts.

To implement an option is not difficult, but will need
changes in a fair number of source files in order to propagate
a boolean down to pretty_print_commit() (either as an
additional argument or possibly as an additional field
in the pretty_print_context struct).

 pretty.c |   45 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 45 insertions(+), 0 deletions(-)

diff --git a/pretty.c b/pretty.c
index da15cf2..63268a1 100644
--- a/pretty.c
+++ b/pretty.c
@@ -916,6 +916,36 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding
 	return logmsg_reencode(commit, encoding);
 }
 
+static int sender_is_not_author(const char *message)
+{
+	const char **msg_p = &message;
+
+	for (;;) {
+		const char *line = *msg_p;
+		int linelen = get_one_line(*msg_p);
+		*msg_p += linelen;
+
+		/* Get out of here if the commit has no author. */
+		if (linelen <= 1)
+			return 0;
+
+		if (!memcmp(line, "author ", 7)) {
+			size_t name_len = strlen(git_default_name);
+			size_t email_len;
+			line += 7;
+			if (strncmp(line, git_default_name, name_len))
+				return 1;
+			line += name_len;
+			if (line[0] != ' ' || line[1] != '<')
+				return 1;
+			line += 2;
+			email_len = strlen(git_default_email);
+			return strncmp(line, git_default_email, email_len) ||
+				line[email_len] != '>';
+		}
+	}
+}
+
 void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
 			 struct strbuf *sb,
 			 const struct pretty_print_context *context)
@@ -977,6 +1007,21 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
 		pp_title_line(fmt, &msg, sb, context->subject,
 			      context->after_subject, encoding, need_8bit_cte);
 
+	/*
+	 * If we are formatting an email and if the sender is different
+	 * from the author of the commit, include the From: and Date:
+	 * headers in the body of the commit message to make sure they
+	 * are not lost.
+	 */
+	if (fmt == CMIT_FMT_EMAIL) {
+		const char *p = reencoded ? reencoded : commit->buffer;
+		if (sender_is_not_author(commit->buffer)) {
+			pp_header(fmt, context->abbrev, context->date_mode, encoding,
+				  commit, &p, sb);
+			strbuf_addch(sb, '\n');
+		}
+	}
+
 	beginning_of_body = sb->len;
 	if (fmt != CMIT_FMT_ONELINE)
 		pp_remainder(fmt, &msg, sb, indent);
-- 
1.6.5.2.186.geb718e