Merge branch 'jk/format-patch-from'
"git format-patch" learned "--from[=whom]" option, which sets the "From: " header to the specified person (or the person who runs the command, if "=whom" part is missing) and move the original author information to an in-body From: header as necessary. * jk/format-patch-from: teach format-patch to place other authors into in-body "From" pretty.c: drop const-ness from pretty_print_context
This commit is contained in:
commit
22fcbc420e
@ -187,6 +187,21 @@ will want to ensure that threading is disabled for `git send-email`.
|
||||
The negated form `--no-cc` discards all `Cc:` headers added so
|
||||
far (from config or command line).
|
||||
|
||||
--from::
|
||||
--from=<ident>::
|
||||
Use `ident` in the `From:` header of each commit email. If the
|
||||
author ident of the commit is not textually identical to the
|
||||
provided `ident`, place a `From:` header in the body of the
|
||||
message with the original author. If no `ident` is given, use
|
||||
the committer ident.
|
||||
+
|
||||
Note that this option is only useful if you are actually sending the
|
||||
emails and want to identify yourself as the sender, but retain the
|
||||
original author (and `git am` will correctly pick up the in-body
|
||||
header). Note also that `git send-email` already handles this
|
||||
transformation for you, and this option should not be used if you are
|
||||
feeding the result to `git send-email`.
|
||||
|
||||
--add-header=<header>::
|
||||
Add an arbitrary header to the email headers. This is in addition
|
||||
to any configured headers, and may be used multiple times.
|
||||
|
@ -1112,6 +1112,21 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int from_callback(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
char **from = opt->value;
|
||||
|
||||
free(*from);
|
||||
|
||||
if (unset)
|
||||
*from = NULL;
|
||||
else if (arg)
|
||||
*from = xstrdup(arg);
|
||||
else
|
||||
*from = xstrdup(git_committer_info(IDENT_NO_DATE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct commit *commit;
|
||||
@ -1134,6 +1149,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
int quiet = 0;
|
||||
int reroll_count = -1;
|
||||
char *branch_name = NULL;
|
||||
char *from = NULL;
|
||||
const struct option builtin_format_patch_options[] = {
|
||||
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
|
||||
N_("use [PATCH n/m] even with a single patch"),
|
||||
@ -1177,6 +1193,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
0, to_callback },
|
||||
{ OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
|
||||
0, cc_callback },
|
||||
{ OPTION_CALLBACK, 0, "from", &from, N_("ident"),
|
||||
N_("set From address to <ident> (or committer ident if absent)"),
|
||||
PARSE_OPT_OPTARG, from_callback },
|
||||
OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
|
||||
N_("make first mail a reply to <message-id>")),
|
||||
{ OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
|
||||
@ -1264,6 +1283,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
|
||||
|
||||
rev.extra_headers = strbuf_detach(&buf, NULL);
|
||||
|
||||
if (from) {
|
||||
if (split_ident_line(&rev.from_ident, from, strlen(from)))
|
||||
die(_("invalid ident line: %s"), from);
|
||||
}
|
||||
|
||||
if (start_number < 0)
|
||||
start_number = 1;
|
||||
|
||||
|
21
commit.h
21
commit.h
@ -6,6 +6,7 @@
|
||||
#include "strbuf.h"
|
||||
#include "decorate.h"
|
||||
#include "gpg-interface.h"
|
||||
#include "string-list.h"
|
||||
|
||||
struct commit_list {
|
||||
struct commit *item;
|
||||
@ -79,6 +80,9 @@ enum cmit_fmt {
|
||||
};
|
||||
|
||||
struct pretty_print_context {
|
||||
/*
|
||||
* Callers should tweak these to change the behavior of pp_* functions.
|
||||
*/
|
||||
enum cmit_fmt fmt;
|
||||
int abbrev;
|
||||
const char *subject;
|
||||
@ -92,6 +96,15 @@ struct pretty_print_context {
|
||||
const char *output_encoding;
|
||||
struct string_list *mailmap;
|
||||
int color;
|
||||
struct ident_split *from_ident;
|
||||
|
||||
/*
|
||||
* Fields below here are manipulated internally by pp_* functions and
|
||||
* should not be counted on by callers.
|
||||
*/
|
||||
|
||||
/* Manipulated by the pp_* functions internally. */
|
||||
struct string_list in_body_headers;
|
||||
};
|
||||
|
||||
struct userformat_want {
|
||||
@ -111,20 +124,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(const struct pretty_print_context *pp,
|
||||
extern void pretty_print_commit(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 struct pretty_print_context *pp,
|
||||
void pp_user_info(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,
|
||||
void pp_title_line(struct pretty_print_context *pp,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *encoding,
|
||||
int need_8bit_cte);
|
||||
void pp_remainder(const struct pretty_print_context *pp,
|
||||
void pp_remainder(struct pretty_print_context *pp,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent);
|
||||
|
@ -618,6 +618,8 @@ void show_log(struct rev_info *opt)
|
||||
ctx.mailmap = opt->mailmap;
|
||||
ctx.color = opt->diffopt.use_color;
|
||||
ctx.output_encoding = get_log_output_encoding();
|
||||
if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
|
||||
ctx.from_ident = &opt->from_ident;
|
||||
pretty_print_commit(&ctx, commit, &msgbuf);
|
||||
|
||||
if (opt->add_signoff)
|
||||
|
48
pretty.c
48
pretty.c
@ -406,7 +406,7 @@ static const char *show_ident_date(const struct ident_split *ident,
|
||||
return show_date(date, tz, mode);
|
||||
}
|
||||
|
||||
void pp_user_info(const struct pretty_print_context *pp,
|
||||
void pp_user_info(struct pretty_print_context *pp,
|
||||
const char *what, struct strbuf *sb,
|
||||
const char *line, const char *encoding)
|
||||
{
|
||||
@ -432,6 +432,23 @@ void pp_user_info(const struct pretty_print_context *pp,
|
||||
map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
|
||||
|
||||
if (pp->fmt == CMIT_FMT_EMAIL) {
|
||||
if (pp->from_ident) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
strbuf_addstr(&buf, "From: ");
|
||||
strbuf_add(&buf, namebuf, namelen);
|
||||
strbuf_addstr(&buf, " <");
|
||||
strbuf_add(&buf, mailbuf, maillen);
|
||||
strbuf_addstr(&buf, ">\n");
|
||||
string_list_append(&pp->in_body_headers,
|
||||
strbuf_detach(&buf, NULL));
|
||||
|
||||
mailbuf = pp->from_ident->mail_begin;
|
||||
maillen = pp->from_ident->mail_end - mailbuf;
|
||||
namebuf = pp->from_ident->name_begin;
|
||||
namelen = pp->from_ident->name_end - namebuf;
|
||||
}
|
||||
|
||||
strbuf_addstr(sb, "From: ");
|
||||
if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) {
|
||||
add_rfc2047(sb, namebuf, namelen,
|
||||
@ -1514,7 +1531,7 @@ void format_commit_message(const struct commit *commit,
|
||||
free(context.signature_check.signer);
|
||||
}
|
||||
|
||||
static void pp_header(const struct pretty_print_context *pp,
|
||||
static void pp_header(struct pretty_print_context *pp,
|
||||
const char *encoding,
|
||||
const struct commit *commit,
|
||||
const char **msg_p,
|
||||
@ -1575,7 +1592,7 @@ static void pp_header(const struct pretty_print_context *pp,
|
||||
}
|
||||
}
|
||||
|
||||
void pp_title_line(const struct pretty_print_context *pp,
|
||||
void pp_title_line(struct pretty_print_context *pp,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
const char *encoding,
|
||||
@ -1602,6 +1619,16 @@ void pp_title_line(const struct pretty_print_context *pp,
|
||||
}
|
||||
strbuf_addch(sb, '\n');
|
||||
|
||||
if (need_8bit_cte == 0) {
|
||||
int i;
|
||||
for (i = 0; i < pp->in_body_headers.nr; i++) {
|
||||
if (has_non_ascii(pp->in_body_headers.items[i].string)) {
|
||||
need_8bit_cte = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (need_8bit_cte > 0) {
|
||||
const char *header_fmt =
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -1615,10 +1642,21 @@ void pp_title_line(const struct pretty_print_context *pp,
|
||||
if (pp->fmt == CMIT_FMT_EMAIL) {
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
if (pp->in_body_headers.nr) {
|
||||
int i;
|
||||
for (i = 0; i < pp->in_body_headers.nr; i++) {
|
||||
strbuf_addstr(sb, pp->in_body_headers.items[i].string);
|
||||
free(pp->in_body_headers.items[i].string);
|
||||
}
|
||||
string_list_clear(&pp->in_body_headers, 0);
|
||||
strbuf_addch(sb, '\n');
|
||||
}
|
||||
|
||||
strbuf_release(&title);
|
||||
}
|
||||
|
||||
void pp_remainder(const struct pretty_print_context *pp,
|
||||
void pp_remainder(struct pretty_print_context *pp,
|
||||
const char **msg_p,
|
||||
struct strbuf *sb,
|
||||
int indent)
|
||||
@ -1650,7 +1688,7 @@ void pp_remainder(const struct pretty_print_context *pp,
|
||||
}
|
||||
}
|
||||
|
||||
void pretty_print_commit(const struct pretty_print_context *pp,
|
||||
void pretty_print_commit(struct pretty_print_context *pp,
|
||||
const struct commit *commit,
|
||||
struct strbuf *sb)
|
||||
{
|
||||
|
@ -144,6 +144,7 @@ struct rev_info {
|
||||
int numbered_files;
|
||||
int reroll_count;
|
||||
char *message_id;
|
||||
struct ident_split from_ident;
|
||||
struct string_list *ref_message_ids;
|
||||
int add_signoff;
|
||||
const char *extra_headers;
|
||||
|
@ -972,6 +972,49 @@ test_expect_success 'empty subject prefix does not have extra space' '
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success '--from=ident notices bogus ident' '
|
||||
test_must_fail git format-patch -1 --stdout --from=foo >patch
|
||||
'
|
||||
|
||||
test_expect_success '--from=ident replaces author' '
|
||||
git format-patch -1 --stdout --from="Me <me@example.com>" >patch &&
|
||||
cat >expect <<-\EOF &&
|
||||
From: Me <me@example.com>
|
||||
|
||||
From: A U Thor <author@example.com>
|
||||
|
||||
EOF
|
||||
sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
|
||||
test_cmp expect patch.head
|
||||
'
|
||||
|
||||
test_expect_success '--from uses committer ident' '
|
||||
git format-patch -1 --stdout --from >patch &&
|
||||
cat >expect <<-\EOF &&
|
||||
From: C O Mitter <committer@example.com>
|
||||
|
||||
From: A U Thor <author@example.com>
|
||||
|
||||
EOF
|
||||
sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
|
||||
test_cmp expect patch.head
|
||||
'
|
||||
|
||||
test_expect_success 'in-body headers trigger content encoding' '
|
||||
GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
|
||||
test_when_finished "git reset --hard HEAD^" &&
|
||||
git format-patch -1 --stdout --from >patch &&
|
||||
cat >expect <<-\EOF &&
|
||||
From: C O Mitter <committer@example.com>
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
|
||||
From: éxötìc <author@example.com>
|
||||
|
||||
EOF
|
||||
sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
|
||||
test_cmp expect patch.head
|
||||
'
|
||||
|
||||
append_signoff()
|
||||
{
|
||||
C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
|
||||
|
Loading…
Reference in New Issue
Block a user