Merge branch 'js/send-email'
* js/send-email: send-email: add --confirm option and configuration setting 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
This commit is contained in:
		| @ -177,14 +177,25 @@ Automating | ||||
|  | ||||
| --suppress-cc:: | ||||
| 	Specify an additional category of recipients to suppress the | ||||
| 	auto-cc of.  'self' will avoid including the sender, 'author' will | ||||
| 	avoid including the patch author, 'cc' will avoid including anyone | ||||
| 	mentioned in Cc lines in the patch, 'sob' will avoid including | ||||
| 	anyone mentioned in Signed-off-by lines, and 'cccmd' will avoid | ||||
| 	running the --cc-cmd.  'all' will suppress all auto cc values. | ||||
| 	Default is the value of 'sendemail.suppresscc' configuration value; | ||||
| 	if that is unspecified, default to 'self' if --suppress-from is | ||||
| 	specified, as well as 'sob' if --no-signed-off-cc is specified. | ||||
| 	auto-cc of: | ||||
| + | ||||
| -- | ||||
| - 'author' will avoid including the patch author | ||||
| - 'self' will avoid including the sender | ||||
| - 'cc' will avoid including anyone mentioned in Cc lines in the patch header | ||||
|   except for self (use 'self' for that). | ||||
| - 'ccbody' will avoid including anyone mentioned in Cc lines in the | ||||
|   patch body (commit message) except for self (use 'self' for that). | ||||
| - 'sob' will avoid including anyone mentioned in Signed-off-by lines except | ||||
|    for self (use 'self' for that). | ||||
| - 'cccmd' will avoid running the --cc-cmd. | ||||
| - 'body' is equivalent to 'sob' + 'ccbody' | ||||
| - 'all' will suppress all auto cc values. | ||||
| -- | ||||
| + | ||||
| Default is the value of 'sendemail.suppresscc' configuration value; if | ||||
| that is unspecified, default to 'self' if --suppress-from is | ||||
| specified, as well as 'body' if --no-signed-off-cc is specified. | ||||
|  | ||||
| --[no-]suppress-from:: | ||||
| 	If this is set, do not add the From: address to the cc: list. | ||||
| @ -201,6 +212,22 @@ Automating | ||||
| Administering | ||||
| ~~~~~~~~~~~~~ | ||||
|  | ||||
| --confirm:: | ||||
| 	Confirm just before sending: | ||||
| + | ||||
| -- | ||||
| - 'always' will always confirm before sending | ||||
| - 'never' will never confirm before sending | ||||
| - 'cc' will confirm before sending when send-email has automatically | ||||
|   added addresses from the patch to the Cc list | ||||
| - 'compose' will confirm before sending the first message when using --compose. | ||||
| - 'auto' is equivalent to 'cc' + 'compose' | ||||
| -- | ||||
| + | ||||
| Default is the value of 'sendemail.confirm' configuration value; if that | ||||
| is unspecified, default to 'auto' unless any of the suppress options | ||||
| have been specified, in which case default to 'compose'. | ||||
|  | ||||
| --dry-run:: | ||||
| 	Do everything except actually send the emails. | ||||
|  | ||||
| @ -244,6 +271,11 @@ sendemail.multiedit:: | ||||
| 	summary when '--compose' is used). If false, files will be edited one | ||||
| 	after the other, spawning a new editor each time. | ||||
|  | ||||
| sendemail.confirm:: | ||||
| 	Sets the default for whether to confirm before sending. Must be | ||||
| 	one of 'always', 'never', 'cc', 'compose', or 'auto'. See '--confirm' | ||||
| 	in the previous section for the meaning of these values. | ||||
|  | ||||
|  | ||||
| Author | ||||
| ------ | ||||
|  | ||||
| @ -23,7 +23,7 @@ use Getopt::Long; | ||||
| use Text::ParseWords; | ||||
| use Data::Dumper; | ||||
| use Term::ANSIColor; | ||||
| use File::Temp qw/ tempdir /; | ||||
| use File::Temp qw/ tempdir tempfile /; | ||||
| use Error qw(:try); | ||||
| use Git; | ||||
|  | ||||
| @ -68,14 +68,15 @@ git send-email [options] <file | directory | rev-list options > | ||||
|   Automating: | ||||
|     --identity              <str>  * Use the sendemail.<id> options. | ||||
|     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path` | ||||
|     --suppress-cc           <str>  * author, self, sob, cccmd, all. | ||||
|     --[no-]signed-off-by-cc        * Send to Cc: and Signed-off-by: | ||||
|                                      addresses. Default on. | ||||
|     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all. | ||||
|     --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on. | ||||
|     --[no-]suppress-from           * Send to self. Default off. | ||||
|     --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default on. | ||||
|     --[no-]thread                  * Use In-Reply-To: field. Default on. | ||||
|  | ||||
|   Administering: | ||||
|     --confirm               <str>  * Confirm recipients before sending; | ||||
|                                      auto, cc, compose, always, or never. | ||||
|     --quiet                        * Output one line of info per email. | ||||
|     --dry-run                      * Don't actually send the emails. | ||||
|     --[no-]validate                * Perform patch sanity checks. Default on. | ||||
| @ -126,6 +127,7 @@ sub format_2822_time { | ||||
| } | ||||
|  | ||||
| my $have_email_valid = eval { require Email::Valid; 1 }; | ||||
| my $have_mail_address = eval { require Mail::Address; 1 }; | ||||
| my $smtp; | ||||
| my $auth; | ||||
|  | ||||
| @ -156,7 +158,7 @@ if ($@) { | ||||
| # Behavior modification variables | ||||
| my ($quiet, $dry_run) = (0, 0); | ||||
| my $format_patch; | ||||
| my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$"; | ||||
| my $compose_filename; | ||||
|  | ||||
| # Handle interactive edition of files. | ||||
| my $multiedit; | ||||
| @ -181,7 +183,7 @@ sub do_edit { | ||||
| my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd); | ||||
| my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption); | ||||
| my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts); | ||||
| my ($validate); | ||||
| my ($validate, $confirm); | ||||
| my (@suppress_cc); | ||||
|  | ||||
| my %config_bool_settings = ( | ||||
| @ -207,6 +209,7 @@ my %config_settings = ( | ||||
|     "suppresscc" => \@suppress_cc, | ||||
|     "envelopesender" => \$envelope_sender, | ||||
|     "multiedit" => \$multiedit, | ||||
|     "confirm"   => \$confirm, | ||||
| ); | ||||
|  | ||||
| # Handle Uncouth Termination | ||||
| @ -219,12 +222,14 @@ sub signal_handler { | ||||
| 	system "stty echo"; | ||||
|  | ||||
| 	# tmp files from --compose | ||||
| 	if (defined $compose_filename) { | ||||
| 		if (-e $compose_filename) { | ||||
| 			print "'$compose_filename' contains an intermediate version of the email you were composing.\n"; | ||||
| 		} | ||||
| 		if (-e ($compose_filename . ".final")) { | ||||
| 			print "'$compose_filename.final' contains the composed email.\n" | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	exit; | ||||
| }; | ||||
| @ -256,6 +261,7 @@ my $rc = GetOptions("sender|from=s" => \$sender, | ||||
| 		    "suppress-from!" => \$suppress_from, | ||||
| 		    "suppress-cc=s" => \@suppress_cc, | ||||
| 		    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc, | ||||
| 		    "confirm=s" => \$confirm, | ||||
| 		    "dry-run" => \$dry_run, | ||||
| 		    "envelope-sender=s" => \$envelope_sender, | ||||
| 		    "thread!" => \$thread, | ||||
| @ -267,6 +273,9 @@ unless ($rc) { | ||||
|     usage(); | ||||
| } | ||||
|  | ||||
| die "Cannot run git format-patch from outside a repository\n" | ||||
| 	if $format_patch and not $repo; | ||||
|  | ||||
| # Now, let's fill any that aren't set in with defaults: | ||||
|  | ||||
| sub read_config { | ||||
| @ -318,13 +327,13 @@ my(%suppress_cc); | ||||
| if (@suppress_cc) { | ||||
| 	foreach my $entry (@suppress_cc) { | ||||
| 		die "Unknown --suppress-cc field: '$entry'\n" | ||||
| 			unless $entry =~ /^(all|cccmd|cc|author|self|sob)$/; | ||||
| 			unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/; | ||||
| 		$suppress_cc{$entry} = 1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| if ($suppress_cc{'all'}) { | ||||
| 	foreach my $entry (qw (ccmd cc author self sob)) { | ||||
| 	foreach my $entry (qw (ccmd cc author self sob body bodycc)) { | ||||
| 		$suppress_cc{$entry} = 1; | ||||
| 	} | ||||
| 	delete $suppress_cc{'all'}; | ||||
| @ -334,6 +343,21 @@ if ($suppress_cc{'all'}) { | ||||
| $suppress_cc{'self'} = $suppress_from if defined $suppress_from; | ||||
| $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc; | ||||
|  | ||||
| if ($suppress_cc{'body'}) { | ||||
| 	foreach my $entry (qw (sob bodycc)) { | ||||
| 		$suppress_cc{$entry} = 1; | ||||
| 	} | ||||
| 	delete $suppress_cc{'body'}; | ||||
| } | ||||
|  | ||||
| # Set confirm's default value | ||||
| my $confirm_unconfigured = !defined $confirm; | ||||
| if ($confirm_unconfigured) { | ||||
| 	$confirm = scalar %suppress_cc ? 'compose' : 'auto'; | ||||
| }; | ||||
| die "Unknown --confirm setting: '$confirm'\n" | ||||
| 	unless $confirm =~ /^(?:auto|cc|compose|always|never)/; | ||||
|  | ||||
| # Debugging, print out the suppressions. | ||||
| if (0) { | ||||
| 	print "suppressions:\n"; | ||||
| @ -360,6 +384,14 @@ foreach my $entry (@bcclist) { | ||||
| 	die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/; | ||||
| } | ||||
|  | ||||
| sub parse_address_line { | ||||
| 	if ($have_mail_address) { | ||||
| 		return map { $_->format } Mail::Address->parse($_[0]); | ||||
| 	} else { | ||||
| 		return split_addrs($_[0]); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| sub split_addrs { | ||||
| 	return quotewords('\s*,\s*', 1, @_); | ||||
| } | ||||
| @ -404,6 +436,7 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { | ||||
|  | ||||
| # returns 1 if the conflict must be solved using it as a format-patch argument | ||||
| sub check_file_rev_conflict($) { | ||||
| 	return unless $repo; | ||||
| 	my $f = shift; | ||||
| 	try { | ||||
| 		$repo->command('rev-parse', '--verify', '--quiet', $f); | ||||
| @ -445,6 +478,8 @@ while (defined(my $f = shift @ARGV)) { | ||||
| } | ||||
|  | ||||
| if (@rev_list_opts) { | ||||
| 	die "Cannot run git format-patch from outside a repository\n" | ||||
| 		unless $repo; | ||||
| 	push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts); | ||||
| } | ||||
|  | ||||
| @ -481,6 +516,9 @@ sub get_patch_subject($) { | ||||
| if ($compose) { | ||||
| 	# Note that this does not need to be secure, but we will make a small | ||||
| 	# effort to have it be unique | ||||
| 	$compose_filename = ($repo ? | ||||
| 		tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) : | ||||
| 		tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1]; | ||||
| 	open(C,">",$compose_filename) | ||||
| 		or die "Failed to open for writing $compose_filename: $!"; | ||||
|  | ||||
| @ -593,7 +631,7 @@ if (!@to) { | ||||
| 	} | ||||
|  | ||||
| 	my $to = $_; | ||||
| 	push @to, split_addrs($to); | ||||
| 	push @to, parse_address_line($to); | ||||
| 	$prompting++; | ||||
| } | ||||
|  | ||||
| @ -637,25 +675,13 @@ if (!defined $smtp_server) { | ||||
| 	$smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug* | ||||
| } | ||||
|  | ||||
| if ($compose) { | ||||
| 	while (1) { | ||||
| 		$_ = $term->readline("Send this email? (y|n) "); | ||||
| 		last if defined $_; | ||||
| 		print "\n"; | ||||
| 	} | ||||
|  | ||||
| 	if (uc substr($_,0,1) ne 'Y') { | ||||
| 		cleanup_compose_files(); | ||||
| 		exit(0); | ||||
| 	} | ||||
|  | ||||
| 	if ($compose > 0) { | ||||
| if ($compose && $compose > 0) { | ||||
| 	@files = ($compose_filename . ".final", @files); | ||||
| } | ||||
| } | ||||
|  | ||||
| # Variables we set as part of the loop over files | ||||
| our ($message_id, %mail, $subject, $reply_to, $references, $message); | ||||
| our ($message_id, %mail, $subject, $reply_to, $references, $message, | ||||
| 	$needs_confirm, $message_num); | ||||
|  | ||||
| sub extract_valid_address { | ||||
| 	my $address = shift; | ||||
| @ -811,6 +837,37 @@ X-Mailer: git-send-email $gitversion | ||||
| 	unshift (@sendmail_parameters, | ||||
| 			'-f', $raw_from) if(defined $envelope_sender); | ||||
|  | ||||
| 	if ($needs_confirm && !$dry_run) { | ||||
| 		print "\n$header\n"; | ||||
| 		if ($needs_confirm eq "inform") { | ||||
| 			$confirm_unconfigured = 0; # squelch this message for the rest of this run | ||||
| 			print "    The Cc list above has been expanded by additional\n"; | ||||
| 			print "    addresses found in the patch commit message. By default\n"; | ||||
| 			print "    send-email prompts before sending whenever this occurs.\n"; | ||||
| 			print "    This behavior is controlled by the sendemail.confirm\n"; | ||||
| 			print "    configuration setting.\n"; | ||||
| 			print "\n"; | ||||
| 			print "    For additional information, run 'git send-email --help'.\n"; | ||||
| 			print "    To retain the current behavior, but squelch this message,\n"; | ||||
| 			print "    run 'git config --global sendemail.confirm auto'.\n\n"; | ||||
| 		} | ||||
| 		while (1) { | ||||
| 			chomp ($_ = $term->readline( | ||||
| 				"Send this email? ([y]es|[n]o|[q]uit|[a]ll): " | ||||
| 			)); | ||||
| 			last if /^(?:yes|y|no|n|quit|q|all|a)/i; | ||||
| 			print "\n"; | ||||
| 		} | ||||
| 		if (/^n/i) { | ||||
| 			return; | ||||
| 		} elsif (/^q/i) { | ||||
| 			cleanup_compose_files(); | ||||
| 			exit(0); | ||||
| 		} elsif (/^a/i) { | ||||
| 			$confirm = 'never'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if ($dry_run) { | ||||
| 		# We don't want to send the email. | ||||
| 	} elsif ($smtp_server =~ m#^/#) { | ||||
| @ -909,6 +966,7 @@ X-Mailer: git-send-email $gitversion | ||||
| $reply_to = $initial_reply_to; | ||||
| $references = $initial_reply_to || ''; | ||||
| $subject = $initial_subject; | ||||
| $message_num = 0; | ||||
|  | ||||
| foreach my $t (@files) { | ||||
| 	open(F,"<",$t) or die "can't open file $t"; | ||||
| @ -917,13 +975,25 @@ foreach my $t (@files) { | ||||
| 	my $author_encoding; | ||||
| 	my $has_content_type; | ||||
| 	my $body_encoding; | ||||
| 	@cc = @initial_cc; | ||||
| 	@cc = (); | ||||
| 	@xh = (); | ||||
| 	my $input_format = undef; | ||||
| 	my $header_done = 0; | ||||
| 	my @header = (); | ||||
| 	$message = ""; | ||||
| 	$message_num++; | ||||
| 	# First unfold multiline header fields | ||||
| 	while(<F>) { | ||||
| 		if (!$header_done) { | ||||
| 		last if /^\s*$/; | ||||
| 		if (/^\s+\S/ and @header) { | ||||
| 			chomp($header[$#header]); | ||||
| 			s/^\s+/ /; | ||||
| 			$header[$#header] .= $_; | ||||
| 	    } else { | ||||
| 			push(@header, $_); | ||||
| 		} | ||||
| 	} | ||||
| 	# Now parse the header | ||||
| 	foreach(@header) { | ||||
| 		if (/^From /) { | ||||
| 			$input_format = 'mbox'; | ||||
| 			next; | ||||
| @ -936,21 +1006,26 @@ foreach my $t (@files) { | ||||
| 		if (defined $input_format && $input_format eq 'mbox') { | ||||
| 			if (/^Subject:\s+(.*)$/) { | ||||
| 				$subject = $1; | ||||
|  | ||||
| 				} elsif (/^(Cc|From):\s+(.*)$/) { | ||||
| 					if (unquote_rfc2047($2) eq $sender) { | ||||
| 						next if ($suppress_cc{'self'}); | ||||
| 			} | ||||
| 					elsif ($1 eq 'From') { | ||||
| 						($author, $author_encoding) | ||||
| 						  = unquote_rfc2047($2); | ||||
| 						next if ($suppress_cc{'author'}); | ||||
| 			elsif (/^From:\s+(.*)$/) { | ||||
| 				($author, $author_encoding) = unquote_rfc2047($1); | ||||
| 				next if $suppress_cc{'author'}; | ||||
| 				next if $suppress_cc{'self'} and $author eq $sender; | ||||
| 				printf("(mbox) Adding cc: %s from line '%s'\n", | ||||
| 					$1, $_) unless $quiet; | ||||
| 				push @cc, $1; | ||||
| 			} | ||||
| 			elsif (/^Cc:\s+(.*)$/) { | ||||
| 				foreach my $addr (parse_address_line($1)) { | ||||
| 					if (unquote_rfc2047($addr) eq $sender) { | ||||
| 						next if ($suppress_cc{'self'}); | ||||
| 					} else { | ||||
| 						next if ($suppress_cc{'cc'}); | ||||
| 					} | ||||
| 					printf("(mbox) Adding cc: %s from line '%s'\n", | ||||
| 						$2, $_) unless $quiet; | ||||
| 					push @cc, $2; | ||||
| 						$addr, $_) unless $quiet; | ||||
| 					push @cc, $addr; | ||||
| 				} | ||||
| 			} | ||||
| 			elsif (/^Content-type:/i) { | ||||
| 				$has_content_type = 1; | ||||
| @ -976,30 +1051,28 @@ foreach my $t (@files) { | ||||
| 			if (@cc == 0 && !$suppress_cc{'cc'}) { | ||||
| 				printf("(non-mbox) Adding cc: %s from line '%s'\n", | ||||
| 					$_, $_) unless $quiet; | ||||
|  | ||||
| 				push @cc, $_; | ||||
|  | ||||
| 			} elsif (!defined $subject) { | ||||
| 				$subject = $_; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 			# A whitespace line will terminate the headers | ||||
| 			if (m/^\s*$/) { | ||||
| 				$header_done = 1; | ||||
| 	} | ||||
| 		} else { | ||||
| 	# Now parse the message body | ||||
| 	while(<F>) { | ||||
| 		$message .=  $_; | ||||
| 		if (/^(Signed-off-by|Cc): (.*)$/i) { | ||||
| 				next if ($suppress_cc{'sob'}); | ||||
| 			chomp; | ||||
| 				my $c = $2; | ||||
| 			my ($what, $c) = ($1, $2); | ||||
| 			chomp $c; | ||||
| 				next if ($c eq $sender and $suppress_cc{'self'}); | ||||
| 				push @cc, $c; | ||||
| 				printf("(sob) Adding cc: %s from line '%s'\n", | ||||
| 					$c, $_) unless $quiet; | ||||
| 			if ($c eq $sender) { | ||||
| 				next if ($suppress_cc{'self'}); | ||||
| 			} else { | ||||
| 				next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i; | ||||
| 				next if $suppress_cc{'bodycc'} and $what =~ /Cc/i; | ||||
| 			} | ||||
| 			push @cc, $c; | ||||
| 			printf("(body) Adding cc: %s from line '%s'\n", | ||||
| 				$c, $_) unless $quiet; | ||||
| 		} | ||||
| 	} | ||||
| 	close F; | ||||
| @ -1020,7 +1093,7 @@ foreach my $t (@files) { | ||||
| 			or die "(cc-cmd) failed to close pipe to '$cc_cmd'"; | ||||
| 	} | ||||
|  | ||||
| 	if (defined $author) { | ||||
| 	if (defined $author and $author ne $sender) { | ||||
| 		$message = "From: $author\n\n$message"; | ||||
| 		if (defined $author_encoding) { | ||||
| 			if ($has_content_type) { | ||||
| @ -1040,6 +1113,14 @@ foreach my $t (@files) { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	$needs_confirm = ( | ||||
| 		$confirm eq "always" or | ||||
| 		($confirm =~ /^(?:auto|cc)$/ && @cc) or | ||||
| 		($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1)); | ||||
| 	$needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc); | ||||
|  | ||||
| 	@cc = (@initial_cc, @cc); | ||||
|  | ||||
| 	send_message(); | ||||
|  | ||||
| 	# set up for the next message | ||||
| @ -1054,13 +1135,10 @@ foreach my $t (@files) { | ||||
| 	$message_id = undef; | ||||
| } | ||||
|  | ||||
| if ($compose) { | ||||
| cleanup_compose_files(); | ||||
| } | ||||
|  | ||||
| sub cleanup_compose_files() { | ||||
| 	unlink($compose_filename, $compose_filename . ".final"); | ||||
|  | ||||
| 	unlink($compose_filename, $compose_filename . ".final") if $compose; | ||||
| } | ||||
|  | ||||
| $smtp->quit if $smtp; | ||||
|  | ||||
| @ -32,16 +32,59 @@ clean_fake_sendmail() { | ||||
| } | ||||
|  | ||||
| test_expect_success 'Extract patches' ' | ||||
|     patches=`git format-patch -n HEAD^1` | ||||
|     patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1` | ||||
| ' | ||||
|  | ||||
| # Test no confirm early to ensure remaining tests will not hang | ||||
| test_no_confirm () { | ||||
| 	rm -f no_confirm_okay | ||||
| 	echo n | \ | ||||
| 		GIT_SEND_EMAIL_NOTTY=1 \ | ||||
| 		git send-email \ | ||||
| 		--from="Example <from@example.com>" \ | ||||
| 		--to=nobody@example.com \ | ||||
| 		--smtp-server="$(pwd)/fake.sendmail" \ | ||||
| 		$@ \ | ||||
| 		$patches > stdout && | ||||
| 		test_must_fail grep "Send this email" stdout && | ||||
| 		> no_confirm_okay | ||||
| } | ||||
|  | ||||
| # Exit immediately to prevent hang if a no-confirm test fails | ||||
| check_no_confirm () { | ||||
| 	test -f no_confirm_okay || { | ||||
| 		say 'No confirm test failed; skipping remaining tests to prevent hanging' | ||||
| 		test_done | ||||
| 	} | ||||
| } | ||||
|  | ||||
| test_expect_success 'No confirm with --suppress-cc' ' | ||||
| 	test_no_confirm --suppress-cc=sob | ||||
| ' | ||||
| check_no_confirm | ||||
|  | ||||
| test_expect_success 'No confirm with --confirm=never' ' | ||||
| 	test_no_confirm --confirm=never | ||||
| ' | ||||
| check_no_confirm | ||||
|  | ||||
| # leave sendemail.confirm set to never after this so that none of the | ||||
| # remaining tests prompt unintentionally. | ||||
| test_expect_success 'No confirm with sendemail.confirm=never' ' | ||||
| 	git config sendemail.confirm never && | ||||
| 	test_no_confirm --compose --subject=foo | ||||
| ' | ||||
| check_no_confirm | ||||
|  | ||||
| test_expect_success 'Send patches' ' | ||||
|      git send-email --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors | ||||
|      git send-email --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors | ||||
| ' | ||||
|  | ||||
| cat >expected <<\EOF | ||||
| !nobody@example.com! | ||||
| !author@example.com! | ||||
| !one@example.com! | ||||
| !two@example.com! | ||||
| EOF | ||||
| test_expect_success \ | ||||
|     'Verify commandline' \ | ||||
| @ -50,13 +93,15 @@ test_expect_success \ | ||||
| cat >expected-show-all-headers <<\EOF | ||||
| 0001-Second.patch | ||||
| (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' | ||||
| (mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' | ||||
| (mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<bcc@example.com> | ||||
| RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<bcc@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Cc: cc@example.com, A <author@example.com> | ||||
| Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| @ -70,6 +115,7 @@ EOF | ||||
| test_expect_success 'Show all headers' ' | ||||
| 	git send-email \ | ||||
| 		--dry-run \ | ||||
| 		--suppress-cc=sob \ | ||||
| 		--from="Example <from@example.com>" \ | ||||
| 		--to=to@example.com \ | ||||
| 		--cc=cc@example.com \ | ||||
| @ -104,6 +150,28 @@ test_expect_success 'no patch was sent' ' | ||||
| 	! test -e commandline1 | ||||
| ' | ||||
|  | ||||
| test_expect_success 'Author From: in message body' ' | ||||
| 	clean_fake_sendmail && | ||||
| 	git send-email \ | ||||
| 		--from="Example <nobody@example.com>" \ | ||||
| 		--to=nobody@example.com \ | ||||
| 		--smtp-server="$(pwd)/fake.sendmail" \ | ||||
| 		$patches && | ||||
| 	sed "1,/^$/d" < msgtxt1 > msgbody1 | ||||
| 	grep "From: A <author@example.com>" msgbody1 | ||||
| ' | ||||
|  | ||||
| test_expect_success 'Author From: not in message body' ' | ||||
| 	clean_fake_sendmail && | ||||
| 	git send-email \ | ||||
| 		--from="A <author@example.com>" \ | ||||
| 		--to=nobody@example.com \ | ||||
| 		--smtp-server="$(pwd)/fake.sendmail" \ | ||||
| 		$patches && | ||||
| 	sed "1,/^$/d" < msgtxt1 > msgbody1 | ||||
| 	! grep "From: A <author@example.com>" msgbody1 | ||||
| ' | ||||
|  | ||||
| test_expect_success 'allow long lines with --no-validate' ' | ||||
| 	git send-email \ | ||||
| 		--from="Example <nobody@example.com>" \ | ||||
| @ -148,8 +216,6 @@ test_set_editor "$(pwd)/fake-editor" | ||||
|  | ||||
| test_expect_success '--compose works' ' | ||||
| 	clean_fake_sendmail && | ||||
| 	echo y | \ | ||||
| 		GIT_SEND_EMAIL_NOTTY=1 \ | ||||
| 	git send-email \ | ||||
| 	--compose --subject foo \ | ||||
| 	--from="Example <nobody@example.com>" \ | ||||
| @ -167,16 +233,18 @@ test_expect_success 'second message is patch' ' | ||||
| 	grep "Subject:.*Second" msgtxt2 | ||||
| ' | ||||
|  | ||||
| cat >expected-show-all-headers <<\EOF | ||||
| cat >expected-suppress-sob <<\EOF | ||||
| 0001-Second.patch | ||||
| (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' | ||||
| (mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' | ||||
| (mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com> | ||||
| RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Cc: cc@example.com, A <author@example.com> | ||||
| Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| @ -185,10 +253,10 @@ X-Mailer: X-MAILER-STRING | ||||
| Result: OK | ||||
| EOF | ||||
|  | ||||
| test_expect_success 'sendemail.cc set' ' | ||||
| 	git config sendemail.cc cc@example.com && | ||||
| test_suppression () { | ||||
| 	git send-email \ | ||||
| 		--dry-run \ | ||||
| 		--suppress-cc=$1 \ | ||||
| 		--from="Example <from@example.com>" \ | ||||
| 		--to=to@example.com \ | ||||
| 		--smtp-server relay.example.com \ | ||||
| @ -196,20 +264,27 @@ test_expect_success 'sendemail.cc set' ' | ||||
| 	sed	-e "s/^\(Date:\).*/\1 DATE-STRING/" \ | ||||
| 		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \ | ||||
| 		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \ | ||||
| 		>actual-show-all-headers && | ||||
| 	test_cmp expected-show-all-headers actual-show-all-headers | ||||
| 		>actual-suppress-$1 && | ||||
| 	test_cmp expected-suppress-$1 actual-suppress-$1 | ||||
| } | ||||
|  | ||||
| test_expect_success 'sendemail.cc set' ' | ||||
| 	git config sendemail.cc cc@example.com && | ||||
| 	test_suppression sob | ||||
| ' | ||||
|  | ||||
| cat >expected-show-all-headers <<\EOF | ||||
| cat >expected-suppress-sob <<\EOF | ||||
| 0001-Second.patch | ||||
| (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' | ||||
| (mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' | ||||
| (mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com>,<author@example.com> | ||||
| RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Cc: A <author@example.com> | ||||
| Cc: A <author@example.com>, One <one@example.com>, two@example.com | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| @ -220,17 +295,166 @@ EOF | ||||
|  | ||||
| test_expect_success 'sendemail.cc unset' ' | ||||
| 	git config --unset sendemail.cc && | ||||
| 	test_suppression sob | ||||
| ' | ||||
|  | ||||
| cat >expected-suppress-all <<\EOF | ||||
| 0001-Second.patch | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| X-Mailer: X-MAILER-STRING | ||||
|  | ||||
| Result: OK | ||||
| EOF | ||||
|  | ||||
| test_expect_success '--suppress-cc=all' ' | ||||
| 	test_suppression all | ||||
| ' | ||||
|  | ||||
| cat >expected-suppress-body <<\EOF | ||||
| 0001-Second.patch | ||||
| (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' | ||||
| (mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' | ||||
| (mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Cc: A <author@example.com>, One <one@example.com>, two@example.com | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| X-Mailer: X-MAILER-STRING | ||||
|  | ||||
| Result: OK | ||||
| EOF | ||||
|  | ||||
| test_expect_success '--suppress-cc=body' ' | ||||
| 	test_suppression body | ||||
| ' | ||||
|  | ||||
| cat >expected-suppress-sob <<\EOF | ||||
| 0001-Second.patch | ||||
| (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' | ||||
| (mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' | ||||
| (mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Cc: A <author@example.com>, One <one@example.com>, two@example.com | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| X-Mailer: X-MAILER-STRING | ||||
|  | ||||
| Result: OK | ||||
| EOF | ||||
|  | ||||
| test_expect_success '--suppress-cc=sob' ' | ||||
| 	test_suppression sob | ||||
| ' | ||||
|  | ||||
| cat >expected-suppress-bodycc <<\EOF | ||||
| 0001-Second.patch | ||||
| (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' | ||||
| (mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com' | ||||
| (mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com' | ||||
| (body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>' | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@example.com> | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| X-Mailer: X-MAILER-STRING | ||||
|  | ||||
| Result: OK | ||||
| EOF | ||||
|  | ||||
| test_expect_success '--suppress-cc=bodycc' ' | ||||
| 	test_suppression bodycc | ||||
| ' | ||||
|  | ||||
| cat >expected-suppress-cc <<\EOF | ||||
| 0001-Second.patch | ||||
| (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>' | ||||
| (body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>' | ||||
| Dry-OK. Log says: | ||||
| Server: relay.example.com | ||||
| MAIL FROM:<from@example.com> | ||||
| RCPT TO:<to@example.com>,<author@example.com>,<committer@example.com> | ||||
| From: Example <from@example.com> | ||||
| To: to@example.com | ||||
| Cc: A <author@example.com>, C O Mitter <committer@example.com> | ||||
| Subject: [PATCH 1/1] Second. | ||||
| Date: DATE-STRING | ||||
| Message-Id: MESSAGE-ID-STRING | ||||
| X-Mailer: X-MAILER-STRING | ||||
|  | ||||
| Result: OK | ||||
| EOF | ||||
|  | ||||
| test_expect_success '--suppress-cc=cc' ' | ||||
| 	test_suppression cc | ||||
| ' | ||||
|  | ||||
| test_confirm () { | ||||
| 	echo y | \ | ||||
| 		GIT_SEND_EMAIL_NOTTY=1 \ | ||||
| 		git send-email \ | ||||
| 		--dry-run \ | ||||
| 		--from="Example <from@example.com>" \ | ||||
| 		--to=to@example.com \ | ||||
| 		--smtp-server relay.example.com \ | ||||
| 		$patches | | ||||
| 	sed	-e "s/^\(Date:\).*/\1 DATE-STRING/" \ | ||||
| 		-e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \ | ||||
| 		-e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \ | ||||
| 		>actual-show-all-headers && | ||||
| 	test_cmp expected-show-all-headers actual-show-all-headers | ||||
| 		--from="Example <nobody@example.com>" \ | ||||
| 		--to=nobody@example.com \ | ||||
| 		--smtp-server="$(pwd)/fake.sendmail" \ | ||||
| 		$@ \ | ||||
| 		$patches | grep "Send this email" | ||||
| } | ||||
|  | ||||
| test_expect_success '--confirm=always' ' | ||||
| 	test_confirm --confirm=always --suppress-cc=all | ||||
| ' | ||||
|  | ||||
| test_expect_success '--confirm=auto' ' | ||||
| 	test_confirm --confirm=auto | ||||
| ' | ||||
|  | ||||
| test_expect_success '--confirm=cc' ' | ||||
| 	test_confirm --confirm=cc | ||||
| ' | ||||
|  | ||||
| test_expect_success '--confirm=compose' ' | ||||
| 	test_confirm --confirm=compose --compose | ||||
| ' | ||||
|  | ||||
| test_expect_success 'confirm by default (due to cc)' ' | ||||
| 	CONFIRM=$(git config --get sendemail.confirm) && | ||||
| 	git config --unset sendemail.confirm && | ||||
| 	test_confirm && | ||||
| 	git config sendemail.confirm $CONFIRM | ||||
| ' | ||||
|  | ||||
| test_expect_success 'confirm by default (due to --compose)' ' | ||||
| 	CONFIRM=$(git config --get sendemail.confirm) && | ||||
| 	git config --unset sendemail.confirm && | ||||
| 	test_confirm --suppress-cc=all --compose | ||||
| 	ret="$?" | ||||
| 	git config sendemail.confirm ${CONFIRM:-never} | ||||
| 	test $ret = "0" | ||||
| ' | ||||
|  | ||||
| test_expect_success '--compose adds MIME for utf8 body' ' | ||||
| @ -239,9 +463,7 @@ test_expect_success '--compose adds MIME for utf8 body' ' | ||||
| 	 echo "echo utf8 body: àéìöú >>\"\$1\"" | ||||
| 	) >fake-editor-utf8 && | ||||
| 	chmod +x fake-editor-utf8 && | ||||
| 	echo y | \ | ||||
| 	  GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \ | ||||
| 	  GIT_SEND_EMAIL_NOTTY=1 \ | ||||
| 	  git send-email \ | ||||
| 	  --compose --subject foo \ | ||||
| 	  --from="Example <nobody@example.com>" \ | ||||
| @ -263,9 +485,7 @@ test_expect_success '--compose respects user mime type' ' | ||||
| 	 echo " echo utf8 body: àéìöú) >\"\$1\"" | ||||
| 	) >fake-editor-utf8-mime && | ||||
| 	chmod +x fake-editor-utf8-mime && | ||||
| 	echo y | \ | ||||
| 	  GIT_EDITOR="\"$(pwd)/fake-editor-utf8-mime\"" \ | ||||
| 	  GIT_SEND_EMAIL_NOTTY=1 \ | ||||
| 	  git send-email \ | ||||
| 	  --compose --subject foo \ | ||||
| 	  --from="Example <nobody@example.com>" \ | ||||
| @ -279,9 +499,7 @@ test_expect_success '--compose respects user mime type' ' | ||||
|  | ||||
| test_expect_success '--compose adds MIME for utf8 subject' ' | ||||
| 	clean_fake_sendmail && | ||||
| 	echo y | \ | ||||
| 	  GIT_EDITOR="\"$(pwd)/fake-editor\"" \ | ||||
| 	  GIT_SEND_EMAIL_NOTTY=1 \ | ||||
| 	  git send-email \ | ||||
| 	  --compose --subject utf8-sübjëct \ | ||||
| 	  --from="Example <nobody@example.com>" \ | ||||
| @ -303,7 +521,7 @@ test_expect_success 'detects ambiguous reference/file conflict' ' | ||||
| test_expect_success 'feed two files' ' | ||||
| 	rm -fr outdir && | ||||
| 	git format-patch -2 -o outdir && | ||||
| 	GIT_SEND_EMAIL_NOTTY=1 git send-email \ | ||||
| 	git send-email \ | ||||
| 	--dry-run \ | ||||
| 	--from="Example <nobody@example.com>" \ | ||||
| 	--to=nobody@example.com \ | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Junio C Hamano
					Junio C Hamano