Skip to content

Commit 7681ed2

Browse files
committed
Merge branch 'js/maint-send-email' into maint
* js/maint-send-email: send-email: don't create temporary compose file until it is needed send-email: --suppress-cc improvements send-email: handle multiple Cc addresses when reading mbox message send-email: allow send-email to run outside a repo
2 parents ecab04d + afe756c commit 7681ed2

File tree

3 files changed

+305
-112
lines changed

3 files changed

+305
-112
lines changed

Documentation/git-send-email.txt

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,25 @@ Automating
177177

178178
--suppress-cc::
179179
Specify an additional category of recipients to suppress the
180-
auto-cc of. 'self' will avoid including the sender, 'author' will
181-
avoid including the patch author, 'cc' will avoid including anyone
182-
mentioned in Cc lines in the patch, 'sob' will avoid including
183-
anyone mentioned in Signed-off-by lines, and 'cccmd' will avoid
184-
running the --cc-cmd. 'all' will suppress all auto cc values.
185-
Default is the value of 'sendemail.suppresscc' configuration value;
186-
if that is unspecified, default to 'self' if --suppress-from is
187-
specified, as well as 'sob' if --no-signed-off-cc is specified.
180+
auto-cc of:
181+
+
182+
--
183+
- 'author' will avoid including the patch author
184+
- 'self' will avoid including the sender
185+
- 'cc' will avoid including anyone mentioned in Cc lines in the patch header
186+
except for self (use 'self' for that).
187+
- 'ccbody' will avoid including anyone mentioned in Cc lines in the
188+
patch body (commit message) except for self (use 'self' for that).
189+
- 'sob' will avoid including anyone mentioned in Signed-off-by lines except
190+
for self (use 'self' for that).
191+
- 'cccmd' will avoid running the --cc-cmd.
192+
- 'body' is equivalent to 'sob' + 'ccbody'
193+
- 'all' will suppress all auto cc values.
194+
--
195+
+
196+
Default is the value of 'sendemail.suppresscc' configuration value; if
197+
that is unspecified, default to 'self' if --suppress-from is
198+
specified, as well as 'body' if --no-signed-off-cc is specified.
188199

189200
--[no-]suppress-from::
190201
If this is set, do not add the From: address to the cc: list.

git-send-email.perl

Lines changed: 119 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
use Text::ParseWords;
2424
use Data::Dumper;
2525
use Term::ANSIColor;
26-
use File::Temp qw/ tempdir /;
26+
use File::Temp qw/ tempdir tempfile /;
2727
use Error qw(:try);
2828
use Git;
2929

@@ -68,9 +68,8 @@ sub usage {
6868
Automating:
6969
--identity <str> * Use the sendemail.<id> options.
7070
--cc-cmd <str> * Email Cc: via `<str> \$patch_path`
71-
--suppress-cc <str> * author, self, sob, cccmd, all.
72-
--[no-]signed-off-by-cc * Send to Cc: and Signed-off-by:
73-
addresses. Default on.
71+
--suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, all.
72+
--[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on.
7473
--[no-]suppress-from * Send to self. Default off.
7574
--[no-]chain-reply-to * Chain In-Reply-To: fields. Default on.
7675
--[no-]thread * Use In-Reply-To: field. Default on.
@@ -126,6 +125,7 @@ sub format_2822_time {
126125
}
127126

128127
my $have_email_valid = eval { require Email::Valid; 1 };
128+
my $have_mail_address = eval { require Mail::Address; 1 };
129129
my $smtp;
130130
my $auth;
131131

@@ -156,7 +156,7 @@ sub format_2822_time {
156156
# Behavior modification variables
157157
my ($quiet, $dry_run) = (0, 0);
158158
my $format_patch;
159-
my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
159+
my $compose_filename;
160160

161161
# Handle interactive edition of files.
162162
my $multiedit;
@@ -219,11 +219,13 @@ sub signal_handler {
219219
system "stty echo";
220220

221221
# tmp files from --compose
222-
if (-e $compose_filename) {
223-
print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
224-
}
225-
if (-e ($compose_filename . ".final")) {
226-
print "'$compose_filename.final' contains the composed email.\n"
222+
if (defined $compose_filename) {
223+
if (-e $compose_filename) {
224+
print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
225+
}
226+
if (-e ($compose_filename . ".final")) {
227+
print "'$compose_filename.final' contains the composed email.\n"
228+
}
227229
}
228230

229231
exit;
@@ -267,6 +269,9 @@ sub signal_handler {
267269
usage();
268270
}
269271

272+
die "Cannot run git format-patch from outside a repository\n"
273+
if $format_patch and not $repo;
274+
270275
# Now, let's fill any that aren't set in with defaults:
271276

272277
sub read_config {
@@ -318,13 +323,13 @@ sub read_config {
318323
if (@suppress_cc) {
319324
foreach my $entry (@suppress_cc) {
320325
die "Unknown --suppress-cc field: '$entry'\n"
321-
unless $entry =~ /^(all|cccmd|cc|author|self|sob)$/;
326+
unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/;
322327
$suppress_cc{$entry} = 1;
323328
}
324329
}
325330

326331
if ($suppress_cc{'all'}) {
327-
foreach my $entry (qw (ccmd cc author self sob)) {
332+
foreach my $entry (qw (ccmd cc author self sob body bodycc)) {
328333
$suppress_cc{$entry} = 1;
329334
}
330335
delete $suppress_cc{'all'};
@@ -334,6 +339,13 @@ sub read_config {
334339
$suppress_cc{'self'} = $suppress_from if defined $suppress_from;
335340
$suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
336341

342+
if ($suppress_cc{'body'}) {
343+
foreach my $entry (qw (sob bodycc)) {
344+
$suppress_cc{$entry} = 1;
345+
}
346+
delete $suppress_cc{'body'};
347+
}
348+
337349
# Debugging, print out the suppressions.
338350
if (0) {
339351
print "suppressions:\n";
@@ -360,6 +372,14 @@ sub read_config {
360372
die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
361373
}
362374

375+
sub parse_address_line {
376+
if ($have_mail_address) {
377+
return map { $_->format } Mail::Address->parse($_[0]);
378+
} else {
379+
return split_addrs($_[0]);
380+
}
381+
}
382+
363383
sub split_addrs {
364384
return quotewords('\s*,\s*', 1, @_);
365385
}
@@ -404,6 +424,7 @@ sub split_addrs {
404424

405425
# returns 1 if the conflict must be solved using it as a format-patch argument
406426
sub check_file_rev_conflict($) {
427+
return unless $repo;
407428
my $f = shift;
408429
try {
409430
$repo->command('rev-parse', '--verify', '--quiet', $f);
@@ -445,6 +466,8 @@ ($)
445466
}
446467

447468
if (@rev_list_opts) {
469+
die "Cannot run git format-patch from outside a repository\n"
470+
unless $repo;
448471
push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
449472
}
450473

@@ -481,6 +504,9 @@ ($)
481504
if ($compose) {
482505
# Note that this does not need to be secure, but we will make a small
483506
# effort to have it be unique
507+
$compose_filename = ($repo ?
508+
tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
509+
tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
484510
open(C,">",$compose_filename)
485511
or die "Failed to open for writing $compose_filename: $!";
486512

@@ -593,7 +619,7 @@ ($)
593619
}
594620

595621
my $to = $_;
596-
push @to, split_addrs($to);
622+
push @to, parse_address_line($to);
597623
$prompting++;
598624
}
599625

@@ -920,88 +946,102 @@ sub send_message
920946
@cc = @initial_cc;
921947
@xh = ();
922948
my $input_format = undef;
923-
my $header_done = 0;
949+
my @header = ();
924950
$message = "";
951+
# First unfold multiline header fields
925952
while(<F>) {
926-
if (!$header_done) {
927-
if (/^From /) {
928-
$input_format = 'mbox';
929-
next;
953+
last if /^\s*$/;
954+
if (/^\s+\S/ and @header) {
955+
chomp($header[$#header]);
956+
s/^\s+/ /;
957+
$header[$#header] .= $_;
958+
} else {
959+
push(@header, $_);
960+
}
961+
}
962+
# Now parse the header
963+
foreach(@header) {
964+
if (/^From /) {
965+
$input_format = 'mbox';
966+
next;
967+
}
968+
chomp;
969+
if (!defined $input_format && /^[-A-Za-z]+:\s/) {
970+
$input_format = 'mbox';
971+
}
972+
973+
if (defined $input_format && $input_format eq 'mbox') {
974+
if (/^Subject:\s+(.*)$/) {
975+
$subject = $1;
930976
}
931-
chomp;
932-
if (!defined $input_format && /^[-A-Za-z]+:\s/) {
933-
$input_format = 'mbox';
977+
elsif (/^From:\s+(.*)$/) {
978+
($author, $author_encoding) = unquote_rfc2047($1);
979+
next if $suppress_cc{'author'};
980+
next if $suppress_cc{'self'} and $author eq $sender;
981+
printf("(mbox) Adding cc: %s from line '%s'\n",
982+
$1, $_) unless $quiet;
983+
push @cc, $1;
934984
}
935-
936-
if (defined $input_format && $input_format eq 'mbox') {
937-
if (/^Subject:\s+(.*)$/) {
938-
$subject = $1;
939-
940-
} elsif (/^(Cc|From):\s+(.*)$/) {
941-
if (unquote_rfc2047($2) eq $sender) {
985+
elsif (/^Cc:\s+(.*)$/) {
986+
foreach my $addr (parse_address_line($1)) {
987+
if (unquote_rfc2047($addr) eq $sender) {
942988
next if ($suppress_cc{'self'});
943-
}
944-
elsif ($1 eq 'From') {
945-
($author, $author_encoding)
946-
= unquote_rfc2047($2);
947-
next if ($suppress_cc{'author'});
948989
} else {
949990
next if ($suppress_cc{'cc'});
950991
}
951992
printf("(mbox) Adding cc: %s from line '%s'\n",
952-
$2, $_) unless $quiet;
953-
push @cc, $2;
954-
}
955-
elsif (/^Content-type:/i) {
956-
$has_content_type = 1;
957-
if (/charset="?([^ "]+)/) {
958-
$body_encoding = $1;
959-
}
960-
push @xh, $_;
961-
}
962-
elsif (/^Message-Id: (.*)/i) {
963-
$message_id = $1;
993+
$addr, $_) unless $quiet;
994+
push @cc, $addr;
964995
}
965-
elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
966-
push @xh, $_;
967-
}
968-
969-
} else {
970-
# In the traditional
971-
# "send lots of email" format,
972-
# line 1 = cc
973-
# line 2 = subject
974-
# So let's support that, too.
975-
$input_format = 'lots';
976-
if (@cc == 0 && !$suppress_cc{'cc'}) {
977-
printf("(non-mbox) Adding cc: %s from line '%s'\n",
978-
$_, $_) unless $quiet;
979-
980-
push @cc, $_;
981-
982-
} elsif (!defined $subject) {
983-
$subject = $_;
996+
}
997+
elsif (/^Content-type:/i) {
998+
$has_content_type = 1;
999+
if (/charset="?([^ "]+)/) {
1000+
$body_encoding = $1;
9841001
}
1002+
push @xh, $_;
9851003
}
986-
987-
# A whitespace line will terminate the headers
988-
if (m/^\s*$/) {
989-
$header_done = 1;
1004+
elsif (/^Message-Id: (.*)/i) {
1005+
$message_id = $1;
9901006
}
1007+
elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
1008+
push @xh, $_;
1009+
}
1010+
9911011
} else {
992-
$message .= $_;
993-
if (/^(Signed-off-by|Cc): (.*)$/i) {
994-
next if ($suppress_cc{'sob'});
995-
chomp;
996-
my $c = $2;
997-
chomp $c;
998-
next if ($c eq $sender and $suppress_cc{'self'});
999-
push @cc, $c;
1000-
printf("(sob) Adding cc: %s from line '%s'\n",
1001-
$c, $_) unless $quiet;
1012+
# In the traditional
1013+
# "send lots of email" format,
1014+
# line 1 = cc
1015+
# line 2 = subject
1016+
# So let's support that, too.
1017+
$input_format = 'lots';
1018+
if (@cc == 0 && !$suppress_cc{'cc'}) {
1019+
printf("(non-mbox) Adding cc: %s from line '%s'\n",
1020+
$_, $_) unless $quiet;
1021+
push @cc, $_;
1022+
} elsif (!defined $subject) {
1023+
$subject = $_;
10021024
}
10031025
}
10041026
}
1027+
# Now parse the message body
1028+
while(<F>) {
1029+
$message .= $_;
1030+
if (/^(Signed-off-by|Cc): (.*)$/i) {
1031+
chomp;
1032+
my ($what, $c) = ($1, $2);
1033+
chomp $c;
1034+
if ($c eq $sender) {
1035+
next if ($suppress_cc{'self'});
1036+
} else {
1037+
next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
1038+
next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
1039+
}
1040+
push @cc, $c;
1041+
printf("(body) Adding cc: %s from line '%s'\n",
1042+
$c, $_) unless $quiet;
1043+
}
1044+
}
10051045
close F;
10061046

10071047
if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
@@ -1020,7 +1060,7 @@ sub send_message
10201060
or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
10211061
}
10221062

1023-
if (defined $author) {
1063+
if (defined $author and $author ne $sender) {
10241064
$message = "From: $author\n\n$message";
10251065
if (defined $author_encoding) {
10261066
if ($has_content_type) {

0 commit comments

Comments
 (0)