Skip to content

Commit 288aa75

Browse files
committed
Merge branch 'fc/send-email-annotate'
Allows format-patch --cover-letter to be configurable; the most notable is the "auto" mode to create cover-letter only for multi patch series. * fc/send-email-annotate: rebase-am: explicitly disable cover-letter format-patch: trivial cleanups format-patch: add format.coverLetter configuration variable log: update to OPT_BOOL format-patch: refactor branch name calculation format-patch: improve head calculation for cover-letter send-email: make annotate configurable
2 parents 54a3c67 + 0597ffa commit 288aa75

File tree

7 files changed

+136
-90
lines changed

7 files changed

+136
-90
lines changed

Documentation/config.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,11 @@ format.signoff::
11091109
the rights to submit this work under the same open source license.
11101110
Please see the 'SubmittingPatches' document for further discussion.
11111111

1112+
format.coverLetter::
1113+
A boolean that controls whether to generate a cover-letter when
1114+
format-patch is invoked, but in addition can be set to "auto", to
1115+
generate a cover-letter only when there's more than one patch.
1116+
11121117
filter.<driver>.clean::
11131118
The command which is used to convert the content of a worktree
11141119
file to a blob upon checkin. See linkgit:gitattributes[5] for
@@ -2016,6 +2021,7 @@ sendemail.<identity>.*::
20162021

20172022
sendemail.aliasesfile::
20182023
sendemail.aliasfiletype::
2024+
sendemail.annotate::
20192025
sendemail.bcc::
20202026
sendemail.cc::
20212027
sendemail.cccmd::

Documentation/git-format-patch.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ SYNOPSIS
2020
[--ignore-if-in-upstream]
2121
[--subject-prefix=Subject-Prefix] [(--reroll-count|-v) <n>]
2222
[--to=<email>] [--cc=<email>]
23-
[--cover-letter] [--quiet] [--notes[=<ref>]]
23+
[--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
2424
[<common diff options>]
2525
[ <since> | <revision range> ]
2626

@@ -195,7 +195,7 @@ will want to ensure that threading is disabled for `git send-email`.
195195
`Cc:`, and custom) headers added so far from config or command
196196
line.
197197

198-
--cover-letter::
198+
--[no-]cover-letter::
199199
In addition to the patches, generate a cover letter file
200200
containing the shortlog and the overall diffstat. You can
201201
fill in a description in the file before sending it out.
@@ -260,6 +260,7 @@ attachments, and sign off patches with configuration variables.
260260
cc = <email>
261261
attach [ = mime-boundary-string ]
262262
signoff = true
263+
coverletter = auto
263264
------------
264265

265266

Documentation/git-send-email.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ Composing
4545
~~~~~~~~~
4646

4747
--annotate::
48-
Review and edit each patch you're about to send. See the
49-
CONFIGURATION section for 'sendemail.multiedit'.
48+
Review and edit each patch you're about to send. Default is the value
49+
of 'sendemail.annotate'. See the CONFIGURATION section for
50+
'sendemail.multiedit'.
5051

5152
--bcc=<address>::
5253
Specify a "Bcc:" value for each email. Default is the value of

builtin/log.c

Lines changed: 85 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
100100
int quiet = 0, source = 0, mailmap = 0;
101101

102102
const struct option builtin_log_options[] = {
103-
OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
104-
OPT_BOOLEAN(0, "source", &source, N_("show source")),
105-
OPT_BOOLEAN(0, "use-mailmap", &mailmap, N_("Use mail map file")),
103+
OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
104+
OPT_BOOL(0, "source", &source, N_("show source")),
105+
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
106106
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
107107
PARSE_OPT_OPTARG, decorate_callback},
108108
OPT_END()
@@ -622,6 +622,14 @@ static void add_header(const char *value)
622622
static int thread;
623623
static int do_signoff;
624624
static const char *signature = git_version_string;
625+
static int config_cover_letter;
626+
627+
enum {
628+
COVER_UNSET,
629+
COVER_OFF,
630+
COVER_ON,
631+
COVER_AUTO
632+
};
625633

626634
static int git_format_config(const char *var, const char *value, void *cb)
627635
{
@@ -683,6 +691,14 @@ static int git_format_config(const char *var, const char *value, void *cb)
683691
}
684692
if (!strcmp(var, "format.signature"))
685693
return git_config_string(&signature, var, value);
694+
if (!strcmp(var, "format.coverletter")) {
695+
if (value && !strcasecmp(value, "auto")) {
696+
config_cover_letter = COVER_AUTO;
697+
return 0;
698+
}
699+
config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF;
700+
return 0;
701+
}
686702

687703
return git_log_config(var, value, cb);
688704
}
@@ -794,9 +810,37 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
794810
}
795811
}
796812

813+
static char *find_branch_name(struct rev_info *rev)
814+
{
815+
int i, positive = -1;
816+
unsigned char branch_sha1[20];
817+
const unsigned char *tip_sha1;
818+
const char *ref;
819+
char *full_ref, *branch = NULL;
820+
821+
for (i = 0; i < rev->cmdline.nr; i++) {
822+
if (rev->cmdline.rev[i].flags & UNINTERESTING)
823+
continue;
824+
if (positive < 0)
825+
positive = i;
826+
else
827+
return NULL;
828+
}
829+
if (positive < 0)
830+
return NULL;
831+
ref = rev->cmdline.rev[positive].name;
832+
tip_sha1 = rev->cmdline.rev[positive].item->sha1;
833+
if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
834+
!prefixcmp(full_ref, "refs/heads/") &&
835+
!hashcmp(tip_sha1, branch_sha1))
836+
branch = xstrdup(full_ref + strlen("refs/heads/"));
837+
free(full_ref);
838+
return branch;
839+
}
840+
797841
static void make_cover_letter(struct rev_info *rev, int use_stdout,
798842
struct commit *origin,
799-
int nr, struct commit **list, struct commit *head,
843+
int nr, struct commit **list,
800844
const char *branch_name,
801845
int quiet)
802846
{
@@ -810,6 +854,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
810854
struct diff_options opts;
811855
int need_8bit_cte = 0;
812856
struct pretty_print_context pp = {0};
857+
struct commit *head = list[0];
813858

814859
if (rev->commit_format != CMIT_FMT_EMAIL)
815860
die(_("Cover letter needs email format"));
@@ -827,6 +872,9 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
827872
if (has_non_ascii(list[i]->buffer))
828873
need_8bit_cte = 1;
829874

875+
if (!branch_name)
876+
branch_name = find_branch_name(rev);
877+
830878
msg = body;
831879
pp.fmt = CMIT_FMT_EMAIL;
832880
pp.date_mode = DATE_RFC2822;
@@ -1033,45 +1081,6 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
10331081
return 0;
10341082
}
10351083

1036-
static char *find_branch_name(struct rev_info *rev)
1037-
{
1038-
int i, positive = -1;
1039-
unsigned char branch_sha1[20];
1040-
const unsigned char *tip_sha1;
1041-
const char *ref;
1042-
char *full_ref, *branch = NULL;
1043-
1044-
for (i = 0; i < rev->cmdline.nr; i++) {
1045-
if (rev->cmdline.rev[i].flags & UNINTERESTING)
1046-
continue;
1047-
if (positive < 0)
1048-
positive = i;
1049-
else
1050-
return NULL;
1051-
}
1052-
if (0 <= positive) {
1053-
ref = rev->cmdline.rev[positive].name;
1054-
tip_sha1 = rev->cmdline.rev[positive].item->sha1;
1055-
} else if (!rev->cmdline.nr && rev->pending.nr == 1 &&
1056-
!strcmp(rev->pending.objects[0].name, "HEAD")) {
1057-
/*
1058-
* No actual ref from command line, but "HEAD" from
1059-
* rev->def was added in setup_revisions()
1060-
* e.g. format-patch --cover-letter -12
1061-
*/
1062-
ref = "HEAD";
1063-
tip_sha1 = rev->pending.objects[0].item->sha1;
1064-
} else {
1065-
return NULL;
1066-
}
1067-
if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
1068-
!prefixcmp(full_ref, "refs/heads/") &&
1069-
!hashcmp(tip_sha1, branch_sha1))
1070-
branch = xstrdup(full_ref + strlen("refs/heads/"));
1071-
free(full_ref);
1072-
return branch;
1073-
}
1074-
10751084
int cmd_format_patch(int argc, const char **argv, const char *prefix)
10761085
{
10771086
struct commit *commit;
@@ -1083,10 +1092,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
10831092
int start_number = -1;
10841093
int just_numbers = 0;
10851094
int ignore_if_in_upstream = 0;
1086-
int cover_letter = 0;
1095+
int cover_letter = -1;
10871096
int boundary_count = 0;
10881097
int no_binary_diff = 0;
1089-
struct commit *origin = NULL, *head = NULL;
1098+
struct commit *origin = NULL;
10901099
const char *in_reply_to = NULL;
10911100
struct patch_ids ids;
10921101
struct strbuf buf = STRBUF_INIT;
@@ -1101,12 +1110,12 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
11011110
{ OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
11021111
N_("use [PATCH] even with multiple patches"),
11031112
PARSE_OPT_NOARG, no_numbered_callback },
1104-
OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
1105-
OPT_BOOLEAN(0, "stdout", &use_stdout,
1113+
OPT_BOOL('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
1114+
OPT_BOOL(0, "stdout", &use_stdout,
11061115
N_("print patches to standard out")),
1107-
OPT_BOOLEAN(0, "cover-letter", &cover_letter,
1116+
OPT_BOOL(0, "cover-letter", &cover_letter,
11081117
N_("generate a cover letter")),
1109-
OPT_BOOLEAN(0, "numbered-files", &just_numbers,
1118+
OPT_BOOL(0, "numbered-files", &just_numbers,
11101119
N_("use simple number sequence for output file names")),
11111120
OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
11121121
N_("use <sfx> instead of '.patch'")),
@@ -1280,28 +1289,36 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
12801289
}
12811290

12821291
if (rev.pending.nr == 1) {
1292+
int check_head = 0;
1293+
12831294
if (rev.max_count < 0 && !rev.show_root_diff) {
12841295
/*
12851296
* This is traditional behaviour of "git format-patch
12861297
* origin" that prepares what the origin side still
12871298
* does not have.
12881299
*/
1289-
unsigned char sha1[20];
1290-
const char *ref;
1291-
12921300
rev.pending.objects[0].item->flags |= UNINTERESTING;
12931301
add_head_to_pending(&rev);
1294-
ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
1295-
if (ref && !prefixcmp(ref, "refs/heads/"))
1296-
branch_name = xstrdup(ref + strlen("refs/heads/"));
1297-
else
1298-
branch_name = xstrdup(""); /* no branch */
1302+
check_head = 1;
12991303
}
13001304
/*
13011305
* Otherwise, it is "format-patch -22 HEAD", and/or
13021306
* "format-patch --root HEAD". The user wants
13031307
* get_revision() to do the usual traversal.
13041308
*/
1309+
1310+
if (!strcmp(rev.pending.objects[0].name, "HEAD"))
1311+
check_head = 1;
1312+
1313+
if (check_head) {
1314+
unsigned char sha1[20];
1315+
const char *ref;
1316+
ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
1317+
if (ref && !prefixcmp(ref, "refs/heads/"))
1318+
branch_name = xstrdup(ref + strlen("refs/heads/"));
1319+
else
1320+
branch_name = xstrdup(""); /* no branch */
1321+
}
13051322
}
13061323

13071324
/*
@@ -1310,29 +1327,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
13101327
*/
13111328
rev.show_root_diff = 1;
13121329

1313-
if (cover_letter) {
1314-
/*
1315-
* NEEDSWORK:randomly pick one positive commit to show
1316-
* diffstat; this is often the tip and the command
1317-
* happens to do the right thing in most cases, but a
1318-
* complex command like "--cover-letter a b c ^bottom"
1319-
* picks "c" and shows diffstat between bottom..c
1320-
* which may not match what the series represents at
1321-
* all and totally broken.
1322-
*/
1323-
int i;
1324-
for (i = 0; i < rev.pending.nr; i++) {
1325-
struct object *o = rev.pending.objects[i].item;
1326-
if (!(o->flags & UNINTERESTING))
1327-
head = (struct commit *)o;
1328-
}
1329-
/* There is nothing to show; it is not an error, though. */
1330-
if (!head)
1331-
return 0;
1332-
if (!branch_name)
1333-
branch_name = find_branch_name(&rev);
1334-
}
1335-
13361330
if (ignore_if_in_upstream) {
13371331
/* Don't say anything if head and upstream are the same. */
13381332
if (rev.pending.nr == 2) {
@@ -1364,11 +1358,21 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
13641358
list = xrealloc(list, nr * sizeof(list[0]));
13651359
list[nr - 1] = commit;
13661360
}
1361+
if (nr == 0)
1362+
/* nothing to do */
1363+
return 0;
13671364
total = nr;
13681365
if (!keep_subject && auto_number && total > 1)
13691366
numbered = 1;
13701367
if (numbered)
13711368
rev.total = total + start_number - 1;
1369+
if (cover_letter == -1) {
1370+
if (config_cover_letter == COVER_AUTO)
1371+
cover_letter = (total > 1);
1372+
else
1373+
cover_letter = (config_cover_letter == COVER_ON);
1374+
}
1375+
13721376
if (in_reply_to || thread || cover_letter)
13731377
rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
13741378
if (in_reply_to) {
@@ -1381,7 +1385,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
13811385
if (thread)
13821386
gen_message_id(&rev, "cover");
13831387
make_cover_letter(&rev, use_stdout,
1384-
origin, nr, list, head, branch_name, quiet);
1388+
origin, nr, list, branch_name, quiet);
13851389
total++;
13861390
start_number--;
13871391
}

git-rebase--am.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ else
3131
rm -f "$GIT_DIR/rebased-patches"
3232

3333
git format-patch -k --stdout --full-index --ignore-if-in-upstream \
34-
--src-prefix=a/ --dst-prefix=b/ \
35-
--no-renames $root_flag "$revisions" >"$GIT_DIR/rebased-patches"
34+
--src-prefix=a/ --dst-prefix=b/ --no-renames --no-cover-letter \
35+
$root_flag "$revisions" >"$GIT_DIR/rebased-patches"
3636
ret=$?
3737

3838
if test 0 != $ret

git-send-email.perl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ sub usage {
5454
--[no-]bcc <str> * Email Bcc:
5555
--subject <str> * Email "Subject:"
5656
--in-reply-to <str> * Email "In-Reply-To:"
57-
--annotate * Review each patch that will be sent in an editor.
57+
--[no-]annotate * Review each patch that will be sent in an editor.
5858
--compose * Open an editor for introduction.
5959
--compose-encoding <str> * Encoding to assume for introduction.
6060
--8bit-encoding <str> * Encoding to assume 8bit mails if undeclared
@@ -212,7 +212,8 @@ sub do_edit {
212212
"signedoffbycc" => [\$signed_off_by_cc, undef],
213213
"signedoffcc" => [\$signed_off_by_cc, undef], # Deprecated
214214
"validate" => [\$validate, 1],
215-
"multiedit" => [\$multiedit, undef]
215+
"multiedit" => [\$multiedit, undef],
216+
"annotate" => [\$annotate, undef]
216217
);
217218

218219
my %config_settings = (
@@ -304,7 +305,7 @@ sub signal_handler {
304305
"smtp-debug:i" => \$debug_net_smtp,
305306
"smtp-domain:s" => \$smtp_domain,
306307
"identity=s" => \$identity,
307-
"annotate" => \$annotate,
308+
"annotate!" => \$annotate,
308309
"compose" => \$compose,
309310
"quiet" => \$quiet,
310311
"cc-cmd=s" => \$cc_cmd,

0 commit comments

Comments
 (0)