Skip to content

Commit 22fcbc4

Browse files
committed
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
2 parents 9678ee7 + a908047 commit 22fcbc4

File tree

7 files changed

+145
-9
lines changed

7 files changed

+145
-9
lines changed

Documentation/git-format-patch.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,21 @@ will want to ensure that threading is disabled for `git send-email`.
187187
The negated form `--no-cc` discards all `Cc:` headers added so
188188
far (from config or command line).
189189

190+
--from::
191+
--from=<ident>::
192+
Use `ident` in the `From:` header of each commit email. If the
193+
author ident of the commit is not textually identical to the
194+
provided `ident`, place a `From:` header in the body of the
195+
message with the original author. If no `ident` is given, use
196+
the committer ident.
197+
+
198+
Note that this option is only useful if you are actually sending the
199+
emails and want to identify yourself as the sender, but retain the
200+
original author (and `git am` will correctly pick up the in-body
201+
header). Note also that `git send-email` already handles this
202+
transformation for you, and this option should not be used if you are
203+
feeding the result to `git send-email`.
204+
190205
--add-header=<header>::
191206
Add an arbitrary header to the email headers. This is in addition
192207
to any configured headers, and may be used multiple times.

builtin/log.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,21 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
11121112
return 0;
11131113
}
11141114

1115+
static int from_callback(const struct option *opt, const char *arg, int unset)
1116+
{
1117+
char **from = opt->value;
1118+
1119+
free(*from);
1120+
1121+
if (unset)
1122+
*from = NULL;
1123+
else if (arg)
1124+
*from = xstrdup(arg);
1125+
else
1126+
*from = xstrdup(git_committer_info(IDENT_NO_DATE));
1127+
return 0;
1128+
}
1129+
11151130
int cmd_format_patch(int argc, const char **argv, const char *prefix)
11161131
{
11171132
struct commit *commit;
@@ -1134,6 +1149,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
11341149
int quiet = 0;
11351150
int reroll_count = -1;
11361151
char *branch_name = NULL;
1152+
char *from = NULL;
11371153
const struct option builtin_format_patch_options[] = {
11381154
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
11391155
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)
11771193
0, to_callback },
11781194
{ OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
11791195
0, cc_callback },
1196+
{ OPTION_CALLBACK, 0, "from", &from, N_("ident"),
1197+
N_("set From address to <ident> (or committer ident if absent)"),
1198+
PARSE_OPT_OPTARG, from_callback },
11801199
OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
11811200
N_("make first mail a reply to <message-id>")),
11821201
{ OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
@@ -1264,6 +1283,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
12641283

12651284
rev.extra_headers = strbuf_detach(&buf, NULL);
12661285

1286+
if (from) {
1287+
if (split_ident_line(&rev.from_ident, from, strlen(from)))
1288+
die(_("invalid ident line: %s"), from);
1289+
}
1290+
12671291
if (start_number < 0)
12681292
start_number = 1;
12691293

commit.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "strbuf.h"
77
#include "decorate.h"
88
#include "gpg-interface.h"
9+
#include "string-list.h"
910

1011
struct commit_list {
1112
struct commit *item;
@@ -79,6 +80,9 @@ enum cmit_fmt {
7980
};
8081

8182
struct pretty_print_context {
83+
/*
84+
* Callers should tweak these to change the behavior of pp_* functions.
85+
*/
8286
enum cmit_fmt fmt;
8387
int abbrev;
8488
const char *subject;
@@ -92,6 +96,15 @@ struct pretty_print_context {
9296
const char *output_encoding;
9397
struct string_list *mailmap;
9498
int color;
99+
struct ident_split *from_ident;
100+
101+
/*
102+
* Fields below here are manipulated internally by pp_* functions and
103+
* should not be counted on by callers.
104+
*/
105+
106+
/* Manipulated by the pp_* functions internally. */
107+
struct string_list in_body_headers;
95108
};
96109

97110
struct userformat_want {
@@ -111,20 +124,20 @@ extern void userformat_find_requirements(const char *fmt, struct userformat_want
111124
extern void format_commit_message(const struct commit *commit,
112125
const char *format, struct strbuf *sb,
113126
const struct pretty_print_context *context);
114-
extern void pretty_print_commit(const struct pretty_print_context *pp,
127+
extern void pretty_print_commit(struct pretty_print_context *pp,
115128
const struct commit *commit,
116129
struct strbuf *sb);
117130
extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
118131
struct strbuf *sb);
119-
void pp_user_info(const struct pretty_print_context *pp,
132+
void pp_user_info(struct pretty_print_context *pp,
120133
const char *what, struct strbuf *sb,
121134
const char *line, const char *encoding);
122-
void pp_title_line(const struct pretty_print_context *pp,
135+
void pp_title_line(struct pretty_print_context *pp,
123136
const char **msg_p,
124137
struct strbuf *sb,
125138
const char *encoding,
126139
int need_8bit_cte);
127-
void pp_remainder(const struct pretty_print_context *pp,
140+
void pp_remainder(struct pretty_print_context *pp,
128141
const char **msg_p,
129142
struct strbuf *sb,
130143
int indent);

log-tree.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,8 @@ void show_log(struct rev_info *opt)
618618
ctx.mailmap = opt->mailmap;
619619
ctx.color = opt->diffopt.use_color;
620620
ctx.output_encoding = get_log_output_encoding();
621+
if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
622+
ctx.from_ident = &opt->from_ident;
621623
pretty_print_commit(&ctx, commit, &msgbuf);
622624

623625
if (opt->add_signoff)

pretty.c

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ static const char *show_ident_date(const struct ident_split *ident,
406406
return show_date(date, tz, mode);
407407
}
408408

409-
void pp_user_info(const struct pretty_print_context *pp,
409+
void pp_user_info(struct pretty_print_context *pp,
410410
const char *what, struct strbuf *sb,
411411
const char *line, const char *encoding)
412412
{
@@ -432,6 +432,23 @@ void pp_user_info(const struct pretty_print_context *pp,
432432
map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
433433

434434
if (pp->fmt == CMIT_FMT_EMAIL) {
435+
if (pp->from_ident) {
436+
struct strbuf buf = STRBUF_INIT;
437+
438+
strbuf_addstr(&buf, "From: ");
439+
strbuf_add(&buf, namebuf, namelen);
440+
strbuf_addstr(&buf, " <");
441+
strbuf_add(&buf, mailbuf, maillen);
442+
strbuf_addstr(&buf, ">\n");
443+
string_list_append(&pp->in_body_headers,
444+
strbuf_detach(&buf, NULL));
445+
446+
mailbuf = pp->from_ident->mail_begin;
447+
maillen = pp->from_ident->mail_end - mailbuf;
448+
namebuf = pp->from_ident->name_begin;
449+
namelen = pp->from_ident->name_end - namebuf;
450+
}
451+
435452
strbuf_addstr(sb, "From: ");
436453
if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) {
437454
add_rfc2047(sb, namebuf, namelen,
@@ -1514,7 +1531,7 @@ void format_commit_message(const struct commit *commit,
15141531
free(context.signature_check.signer);
15151532
}
15161533

1517-
static void pp_header(const struct pretty_print_context *pp,
1534+
static void pp_header(struct pretty_print_context *pp,
15181535
const char *encoding,
15191536
const struct commit *commit,
15201537
const char **msg_p,
@@ -1575,7 +1592,7 @@ static void pp_header(const struct pretty_print_context *pp,
15751592
}
15761593
}
15771594

1578-
void pp_title_line(const struct pretty_print_context *pp,
1595+
void pp_title_line(struct pretty_print_context *pp,
15791596
const char **msg_p,
15801597
struct strbuf *sb,
15811598
const char *encoding,
@@ -1602,6 +1619,16 @@ void pp_title_line(const struct pretty_print_context *pp,
16021619
}
16031620
strbuf_addch(sb, '\n');
16041621

1622+
if (need_8bit_cte == 0) {
1623+
int i;
1624+
for (i = 0; i < pp->in_body_headers.nr; i++) {
1625+
if (has_non_ascii(pp->in_body_headers.items[i].string)) {
1626+
need_8bit_cte = 1;
1627+
break;
1628+
}
1629+
}
1630+
}
1631+
16051632
if (need_8bit_cte > 0) {
16061633
const char *header_fmt =
16071634
"MIME-Version: 1.0\n"
@@ -1615,10 +1642,21 @@ void pp_title_line(const struct pretty_print_context *pp,
16151642
if (pp->fmt == CMIT_FMT_EMAIL) {
16161643
strbuf_addch(sb, '\n');
16171644
}
1645+
1646+
if (pp->in_body_headers.nr) {
1647+
int i;
1648+
for (i = 0; i < pp->in_body_headers.nr; i++) {
1649+
strbuf_addstr(sb, pp->in_body_headers.items[i].string);
1650+
free(pp->in_body_headers.items[i].string);
1651+
}
1652+
string_list_clear(&pp->in_body_headers, 0);
1653+
strbuf_addch(sb, '\n');
1654+
}
1655+
16181656
strbuf_release(&title);
16191657
}
16201658

1621-
void pp_remainder(const struct pretty_print_context *pp,
1659+
void pp_remainder(struct pretty_print_context *pp,
16221660
const char **msg_p,
16231661
struct strbuf *sb,
16241662
int indent)
@@ -1650,7 +1688,7 @@ void pp_remainder(const struct pretty_print_context *pp,
16501688
}
16511689
}
16521690

1653-
void pretty_print_commit(const struct pretty_print_context *pp,
1691+
void pretty_print_commit(struct pretty_print_context *pp,
16541692
const struct commit *commit,
16551693
struct strbuf *sb)
16561694
{

revision.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ struct rev_info {
144144
int numbered_files;
145145
int reroll_count;
146146
char *message_id;
147+
struct ident_split from_ident;
147148
struct string_list *ref_message_ids;
148149
int add_signoff;
149150
const char *extra_headers;

t/t4014-format-patch.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,49 @@ test_expect_success 'empty subject prefix does not have extra space' '
972972
test_cmp expect actual
973973
'
974974

975+
test_expect_success '--from=ident notices bogus ident' '
976+
test_must_fail git format-patch -1 --stdout --from=foo >patch
977+
'
978+
979+
test_expect_success '--from=ident replaces author' '
980+
git format-patch -1 --stdout --from="Me <[email protected]>" >patch &&
981+
cat >expect <<-\EOF &&
982+
From: Me <[email protected]>
983+
984+
From: A U Thor <[email protected]>
985+
986+
EOF
987+
sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
988+
test_cmp expect patch.head
989+
'
990+
991+
test_expect_success '--from uses committer ident' '
992+
git format-patch -1 --stdout --from >patch &&
993+
cat >expect <<-\EOF &&
994+
From: C O Mitter <[email protected]>
995+
996+
From: A U Thor <[email protected]>
997+
998+
EOF
999+
sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
1000+
test_cmp expect patch.head
1001+
'
1002+
1003+
test_expect_success 'in-body headers trigger content encoding' '
1004+
GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
1005+
test_when_finished "git reset --hard HEAD^" &&
1006+
git format-patch -1 --stdout --from >patch &&
1007+
cat >expect <<-\EOF &&
1008+
From: C O Mitter <[email protected]>
1009+
Content-Type: text/plain; charset=UTF-8
1010+
1011+
From: éxötìc <[email protected]>
1012+
1013+
EOF
1014+
sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
1015+
test_cmp expect patch.head
1016+
'
1017+
9751018
append_signoff()
9761019
{
9771020
C=$(git commit-tree HEAD^^{tree} -p HEAD) &&

0 commit comments

Comments
 (0)