Merge branch 'ab/send-email-transferencoding-fix'
authorJunio C Hamano <gitster@pobox.com>
Thu, 13 Jun 2019 20:18:46 +0000 (13:18 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 13 Jun 2019 20:18:46 +0000 (13:18 -0700)
Since "git send-email" learned to take 'auto' as the value for the
transfer-encoding, it by mistake stopped honoring the values given
to the configuration variables sendemail.transferencoding and/or
sendemail.<ident>.transferencoding. This has been corrected to
(finally) redoing the order of setting the default, reading the
configuration and command line options.

* ab/send-email-transferencoding-fix:
send-email: fix regression in sendemail.identity parsing
send-email: document --no-[to|cc|bcc]
send-email: fix broken transferEncoding tests
send-email: remove cargo-culted multi-patch pattern in tests
send-email: do defaults -> config -> getopt in that order
send-email: rename the @bcclist variable for consistency
send-email: move the read_config() function above getopts

1  2 
Documentation/git-send-email.txt
git-send-email.perl
t/t9001-send-email.sh
index 1afe9fc858ea7dcd05ae5f77c994af8f17f5bf52,cf4e3d99bc896f466c12c138eccc0cb241aa3a8a..a861934c69213a790fd96cf065043d5394a0332b
@@@ -33,7 -33,7 +33,7 @@@ This is what linkgit:git-format-patch[1
  formatting are ignored.
  
  2. The original format used by Greg Kroah-Hartman's 'send_lots_of_email.pl'
 -script
 +   script
  +
  This format expects the first line of the file to contain the "Cc:" value
  and the "Subject:" of the message as the second line.
@@@ -190,9 -190,7 +190,9 @@@ $ git send-email --smtp-auth="PLAIN LOG
  If at least one of the specified mechanisms matches the ones advertised by the
  SMTP server and if it is supported by the utilized SASL library, the mechanism
  is used for authentication. If neither 'sendemail.smtpAuth' nor `--smtp-auth`
 -is specified, all mechanisms supported by the SASL library can be used.
 +is specified, all mechanisms supported by the SASL library can be used. The
 +special value 'none' maybe specified to completely disable authentication
 +independently of `--smtp-user`
  
  --smtp-pass[=<password>]::
        Password for SMTP-AUTH. The argument is optional: If no
@@@ -206,9 -204,6 +206,9 @@@ or on the command line. If a username h
  specified (with `--smtp-pass` or `sendemail.smtpPass`), then
  a password is obtained using 'git-credential'.
  
 +--no-smtp-auth::
 +      Disable SMTP authentication. Short hand for `--smtp-auth=none`
 +
  --smtp-server=<host>::
        If set, specifies the outgoing SMTP server to use (e.g.
        `smtp.example.com` or a raw IP address).  Alternatively it can
@@@ -278,6 -273,14 +278,14 @@@ must be used for each option
  Automating
  ~~~~~~~~~~
  
+ --no-[to|cc|bcc]::
+       Clears any list of "To:", "Cc:", "Bcc:" addresses previously
+       set via config.
+ --no-identity::
+       Clears the previously read value of `sendemail.identity` set
+       via config, if any.
  --to-cmd=<command>::
        Specify a command to execute once per patch file which
        should generate patch file specific "To:" entries.
        auto-cc of:
  +
  --
 -- 'author' will avoid including the patch author
 -- 'self' will avoid including the sender
 +- '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).
  - 'bodycc' 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).
 +  for self (use 'self' for that).
 +- 'misc-by' will avoid including anyone mentioned in Acked-by,
 +  Reviewed-by, Tested-by and other "-by" lines in the patch body,
 +  except Signed-off-by (use 'sob' for that).
  - 'cccmd' will avoid running the --cc-cmd.
 -- 'body' is equivalent to 'sob' + 'bodycc'
 +- 'body' is equivalent to 'sob' + 'bodycc' + 'misc-by'.
  - 'all' will suppress all auto cc values.
  --
  +
@@@ -412,7 -412,7 +420,7 @@@ have been specified, in which case defa
                        998 characters unless a suitable transfer encoding
                        ('auto', 'base64', or 'quoted-printable') is used;
                        this is due to SMTP limits as described by
 -                      http://www.ietf.org/rfc/rfc2821.txt.
 +                      http://www.ietf.org/rfc/rfc5322.txt.
  --
  +
  Default is the value of `sendemail.validate`; if this is not set,
diff --combined git-send-email.perl
index 24859a7bc37b05909cf16554c102cadc842c13ff,474598339e260fb9d63be63d148594940fb6caf4..5f92c89c1c1be5ba2fadedd1d7a8d013fb7577fd
@@@ -82,11 -82,8 +82,11 @@@ git send-email --dump-aliase
                                       Pass an empty string to disable certificate
                                       verification.
      --smtp-domain           <str>  * The domain name sent to HELO/EHLO handshake
 -    --smtp-auth             <str>  * Space-separated list of allowed AUTH mechanisms.
 +    --smtp-auth             <str>  * Space-separated list of allowed AUTH mechanisms, or
 +                                     "none" to disable authentication.
                                       This setting forces to use one of the listed mechanisms.
 +    --no-smtp-auth                   Disable SMTP authentication. Shorthand for
 +                                     `--smtp-auth=none`
      --smtp-debug            <0|1>  * Disable, enable Net::SMTP debug.
  
      --batch-size            <int>  * send max <int> message per connection.
@@@ -97,7 -94,7 +97,7 @@@
      --identity              <str>  * Use the sendemail.<id> options.
      --to-cmd                <str>  * Email To: via `<str> \$patch_path`
      --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
 -    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
 +    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
      --[no-]cc-cover                * Email Cc: addresses in the cover letter.
      --[no-]to-cover                * Email To: addresses in the cover letter.
      --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
@@@ -122,11 -119,6 +122,11 @@@ EO
        exit(1);
  }
  
 +sub completion_helper {
 +    print Git::command('format-patch', '--git-completion-helper');
 +    exit(0);
 +}
 +
  # most mail servers generate the Date: header, but not all...
  sub format_2822_time {
        my ($time) = @_;
@@@ -177,11 -169,15 +177,15 @@@ my $re_encoded_text = qr/[^? \000-\037\
  my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/;
  
  # Variables we fill in automatically, or via prompting:
- my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
+ my (@to,@cc,@xh,$envelope_sender,
        $initial_in_reply_to,$reply_to,$initial_subject,@files,
-       $author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time);
- my $envelope_sender;
+       $author,$sender,$smtp_authpass,$annotate,$compose,$time);
+ # Things we either get from config, *or* are overridden on the
+ # command-line.
+ my ($no_cc, $no_to, $no_bcc, $no_identity);
+ my (@config_to, @getopt_to);
+ my (@config_cc, @getopt_cc);
+ my (@config_bcc, @getopt_bcc);
  
  # Example reply to:
  #$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
@@@ -228,33 -224,37 +232,37 @@@ sub do_edit 
  }
  
  # Variables with corresponding config settings
- my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
+ my ($suppress_from, $signed_off_by_cc);
  my ($cover_cc, $cover_to);
  my ($to_cmd, $cc_cmd);
  my ($smtp_server, $smtp_server_port, @smtp_server_options);
  my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
  my ($batch_size, $relogin_delay);
  my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);
- my ($validate, $confirm);
+ my ($confirm);
  my (@suppress_cc);
  my ($auto_8bit_encoding);
  my ($compose_encoding);
- my $target_xfer_encoding = 'auto';
+ # Variables with corresponding config settings & hardcoded defaults
  my ($debug_net_smtp) = 0;             # Net::SMTP, see send_message()
+ my $thread = 1;
+ my $chain_reply_to = 0;
+ my $use_xmailer = 1;
+ my $validate = 1;
+ my $target_xfer_encoding = 'auto';
  
  my %config_bool_settings = (
-     "thread" => [\$thread, 1],
-     "chainreplyto" => [\$chain_reply_to, 0],
-     "suppressfrom" => [\$suppress_from, undef],
-     "signedoffbycc" => [\$signed_off_by_cc, undef],
-     "cccover" => [\$cover_cc, undef],
-     "tocover" => [\$cover_to, undef],
-     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
-     "validate" => [\$validate, 1],
-     "multiedit" => [\$multiedit, undef],
-     "annotate" => [\$annotate, undef],
-     "xmailer" => [\$use_xmailer, 1]
+     "thread" => \$thread,
+     "chainreplyto" => \$chain_reply_to,
+     "suppressfrom" => \$suppress_from,
+     "signedoffbycc" => \$signed_off_by_cc,
+     "cccover" => \$cover_cc,
+     "tocover" => \$cover_to,
+     "signedoffcc" => \$signed_off_by_cc,
+     "validate" => \$validate,
+     "multiedit" => \$multiedit,
+     "annotate" => \$annotate,
+     "xmailer" => \$use_xmailer,
  );
  
  my %config_settings = (
      "smtpauth" => \$smtp_auth,
      "smtpbatchsize" => \$batch_size,
      "smtprelogindelay" => \$relogin_delay,
-     "to" => \@initial_to,
+     "to" => \@config_to,
      "tocmd" => \$to_cmd,
-     "cc" => \@initial_cc,
+     "cc" => \@config_cc,
      "cccmd" => \$cc_cmd,
      "aliasfiletype" => \$aliasfiletype,
-     "bcc" => \@bcclist,
+     "bcc" => \@config_bcc,
      "suppresscc" => \@suppress_cc,
      "envelopesender" => \$envelope_sender,
      "confirm"   => \$confirm,
@@@ -315,13 -315,86 +323,87 @@@ sub signal_handler 
  $SIG{TERM} = \&signal_handler;
  $SIG{INT}  = \&signal_handler;
  
+ # Read our sendemail.* config
+ sub read_config {
+       my ($configured, $prefix) = @_;
+       foreach my $setting (keys %config_bool_settings) {
+               my $target = $config_bool_settings{$setting};
+               my $v = Git::config_bool(@repo, "$prefix.$setting");
+               next unless defined $v;
+               next if $configured->{$setting}++;
+               $$target = $v;
+       }
+       foreach my $setting (keys %config_path_settings) {
+               my $target = $config_path_settings{$setting};
+               if (ref($target) eq "ARRAY") {
+                       my @values = Git::config_path(@repo, "$prefix.$setting");
+                       next unless @values;
+                       next if $configured->{$setting}++;
+                       @$target = @values;
+               }
+               else {
+                       my $v = Git::config_path(@repo, "$prefix.$setting");
+                       next unless defined $v;
+                       next if $configured->{$setting}++;
+                       $$target = $v;
+               }
+       }
+       foreach my $setting (keys %config_settings) {
+               my $target = $config_settings{$setting};
+               if (ref($target) eq "ARRAY") {
+                       my @values = Git::config(@repo, "$prefix.$setting");
+                       next unless @values;
+                       next if $configured->{$setting}++;
+                       @$target = @values;
+               }
+               else {
+                       my $v = Git::config(@repo, "$prefix.$setting");
+                       next unless defined $v;
+                       next if $configured->{$setting}++;
+                       $$target = $v;
+               }
+       }
+       if (!defined $smtp_encryption) {
+               my $setting = "$prefix.smtpencryption";
+               my $enc = Git::config(@repo, $setting);
+               return unless defined $enc;
+               return if $configured->{$setting}++;
+               if (defined $enc) {
+                       $smtp_encryption = $enc;
+               } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) {
+                       $smtp_encryption = 'ssl';
+               }
+       }
+ }
+ # sendemail.identity yields to --identity. We must parse this
+ # special-case first before the rest of the config is read.
+ $identity = Git::config(@repo, "sendemail.identity");
+ my $rc = GetOptions(
+       "identity=s" => \$identity,
+       "no-identity" => \$no_identity,
+ );
+ usage() unless $rc;
+ undef $identity if $no_identity;
+ # Now we know enough to read the config
+ {
+     my %configured;
+     read_config(\%configured, "sendemail.$identity") if defined $identity;
+     read_config(\%configured, "sendemail");
+ }
  # Begin by accumulating all the variables (defined above), that we will end up
  # needing, first, from the command line:
  
  my $help;
- my $rc = GetOptions("h" => \$help,
-                     "dump-aliases" => \$dump_aliases);
 +my $git_completion_helper;
+ $rc = GetOptions("h" => \$help,
+                  "dump-aliases" => \$dump_aliases);
  usage() unless $rc;
  die __("--dump-aliases incompatible with other options\n")
      if !$help and $dump_aliases and @ARGV;
@@@ -330,12 -403,12 +412,12 @@@ $rc = GetOptions
                      "in-reply-to=s" => \$initial_in_reply_to,
                    "reply-to=s" => \$reply_to,
                    "subject=s" => \$initial_subject,
-                   "to=s" => \@initial_to,
+                   "to=s" => \@getopt_to,
                    "to-cmd=s" => \$to_cmd,
                    "no-to" => \$no_to,
-                   "cc=s" => \@initial_cc,
+                   "cc=s" => \@getopt_cc,
                    "no-cc" => \$no_cc,
-                   "bcc=s" => \@bcclist,
+                   "bcc=s" => \@getopt_bcc,
                    "no-bcc" => \$no_bcc,
                    "chain-reply-to!" => \$chain_reply_to,
                    "no-chain-reply-to" => sub {$chain_reply_to = 0},
                    "smtp-debug:i" => \$debug_net_smtp,
                    "smtp-domain:s" => \$smtp_domain,
                    "smtp-auth=s" => \$smtp_auth,
-                   "identity=s" => \$identity,
 +                  "no-smtp-auth" => sub {$smtp_auth = 'none'},
                    "annotate!" => \$annotate,
                    "no-annotate" => sub {$annotate = 0},
                    "compose" => \$compose,
                    "no-xmailer" => sub {$use_xmailer = 0},
                    "batch-size=i" => \$batch_size,
                    "relogin-delay=i" => \$relogin_delay,
 +                  "git-completion-helper" => \$git_completion_helper,
         );
  
+ # Munge any "either config or getopt, not both" variables
+ my @initial_to = @getopt_to ? @getopt_to : ($no_to ? () : @config_to);
+ my @initial_cc = @getopt_cc ? @getopt_cc : ($no_cc ? () : @config_cc);
+ my @initial_bcc = @getopt_bcc ? @getopt_bcc : ($no_bcc ? () : @config_bcc);
  usage() if $help;
 +completion_helper() if $git_completion_helper;
  unless ($rc) {
      usage();
  }
@@@ -399,65 -473,6 +485,6 @@@ die __("`batch-size` and `relogin` mus
        "(via command-line or configuration option)\n")
        if defined $relogin_delay and not defined $batch_size;
  
- # Now, let's fill any that aren't set in with defaults:
- sub read_config {
-       my ($prefix) = @_;
-       foreach my $setting (keys %config_bool_settings) {
-               my $target = $config_bool_settings{$setting}->[0];
-               $$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target);
-       }
-       foreach my $setting (keys %config_path_settings) {
-               my $target = $config_path_settings{$setting};
-               if (ref($target) eq "ARRAY") {
-                       unless (@$target) {
-                               my @values = Git::config_path(@repo, "$prefix.$setting");
-                               @$target = @values if (@values && defined $values[0]);
-                       }
-               }
-               else {
-                       $$target = Git::config_path(@repo, "$prefix.$setting") unless (defined $$target);
-               }
-       }
-       foreach my $setting (keys %config_settings) {
-               my $target = $config_settings{$setting};
-               next if $setting eq "to" and defined $no_to;
-               next if $setting eq "cc" and defined $no_cc;
-               next if $setting eq "bcc" and defined $no_bcc;
-               if (ref($target) eq "ARRAY") {
-                       unless (@$target) {
-                               my @values = Git::config(@repo, "$prefix.$setting");
-                               @$target = @values if (@values && defined $values[0]);
-                       }
-               }
-               else {
-                       $$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target);
-               }
-       }
-       if (!defined $smtp_encryption) {
-               my $enc = Git::config(@repo, "$prefix.smtpencryption");
-               if (defined $enc) {
-                       $smtp_encryption = $enc;
-               } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) {
-                       $smtp_encryption = 'ssl';
-               }
-       }
- }
- # read configuration from [sendemail "$identity"], fall back on [sendemail]
- $identity = Git::config(@repo, "sendemail.identity") unless (defined $identity);
- read_config("sendemail.$identity") if (defined $identity);
- read_config("sendemail");
- # fall back on builtin bool defaults
- foreach my $setting (values %config_bool_settings) {
-       ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
- }
  # 'default' encryption is none -- this only prevents a warning
  $smtp_encryption = '' unless (defined $smtp_encryption);
  
  my(%suppress_cc);
  if (@suppress_cc) {
        foreach my $entry (@suppress_cc) {
 +              # Please update $__git_send_email_suppresscc_options
 +              # in git-completion.bash when you add new options.
                die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
 -                      unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
 +                      unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/;
                $suppress_cc{$entry} = 1;
        }
  }
  
  if ($suppress_cc{'all'}) {
 -      foreach my $entry (qw (cccmd cc author self sob body bodycc)) {
 +      foreach my $entry (qw (cccmd cc author self sob body bodycc misc-by)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'all'};
@@@ -485,7 -498,7 +512,7 @@@ $suppress_cc{'self'} = $suppress_from i
  $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
  
  if ($suppress_cc{'body'}) {
 -      foreach my $entry (qw (sob bodycc)) {
 +      foreach my $entry (qw (sob bodycc misc-by)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'body'};
@@@ -496,8 -509,6 +523,8 @@@ my $confirm_unconfigured = !defined $co
  if ($confirm_unconfigured) {
        $confirm = scalar %suppress_cc ? 'compose' : 'auto';
  };
 +# Please update $__git_send_email_confirm_options in
 +# git-completion.bash when you add new options.
  die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
        unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
  
@@@ -591,8 -602,6 +618,8 @@@ my %parse_alias = 
                if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
                        $aliases{$1} = [ $2 ];
                }}}
 +      # Please update _git_config() in git-completion.bash when you
 +      # add new MUAs.
  );
  
  if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
@@@ -941,7 -950,7 +968,7 @@@ sub expand_one_alias 
  
  @initial_to = process_address_list(@initial_to);
  @initial_cc = process_address_list(@initial_cc);
- @bcclist = process_address_list(@bcclist);
+ @initial_bcc = process_address_list(@initial_bcc);
  
  if ($thread && !defined $initial_in_reply_to && $prompting) {
        $initial_in_reply_to = ask(
@@@ -1259,7 -1268,7 +1286,7 @@@ sub smtp_host_string 
  # (smtp_user was not specified), and 0 otherwise.
  
  sub smtp_auth_maybe {
 -      if (!defined $smtp_authuser || $auth) {
 +      if (!defined $smtp_authuser || $auth || (defined $smtp_auth && $smtp_auth eq "none")) {
                return 1;
        }
  
@@@ -1364,7 -1373,7 +1391,7 @@@ sub send_message 
                    }
               @cc);
        my $to = join (",\n\t", @recipients);
-       @recipients = unique_email_list(@recipients,@cc,@bcclist);
+       @recipients = unique_email_list(@recipients,@cc,@initial_bcc);
        @recipients = (map { extract_valid_address_or_die($_) } @recipients);
        my $date = format_2822_time($time++);
        my $gitversion = '@@GIT_VERSION@@';
@@@ -1497,7 -1506,7 +1524,7 @@@ EO
                                                         SSL => 1);
                        }
                }
 -              else {
 +              elsif (!$smtp) {
                        $smtp_server_port ||= 25;
                        $smtp ||= Net::SMTP->new($smtp_server,
                                                 Hello => $smtp_domain,
                                        $smtp->starttls(ssl_verify_params())
                                                or die sprintf(__("STARTTLS failed! %s"), IO::Socket::SSL::errstr());
                                }
 -                              $smtp_encryption = '';
                                # Send EHLO again to receive fresh
                                # supported commands
                                $smtp->hello($smtp_domain);
@@@ -1699,7 -1709,7 +1726,7 @@@ sub process_file 
        # Now parse the message body
        while(<$fh>) {
                $message .=  $_;
 -              if (/^(Signed-off-by|Cc): (.*)/i) {
 +              if (/^([a-z][a-z-]*-by|Cc): (.*)/i) {
                        chomp;
                        my ($what, $c) = ($1, $2);
                        # strip garbage for the address we'll use:
                        if ($sc 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;
 +                              if ($what =~ /^Signed-off-by$/i) {
 +                                      next if $suppress_cc{'sob'};
 +                              } elsif ($what =~ /-by$/i) {
 +                                      next if $suppress_cc{'misc-by'};
 +                              } elsif ($what =~ /Cc/i) {
 +                                      next if $suppress_cc{'bodycc'};
 +                              }
 +                      }
 +                      if ($c !~ /.+@.+|<.+>/) {
 +                              printf("(body) Ignoring %s from line '%s'\n",
 +                                      $what, $_) unless $quiet;
 +                              next;
                        }
                        push @cc, $c;
                        printf(__("(body) Adding cc: %s from line '%s'\n"),
@@@ -1862,7 -1862,7 +1889,7 @@@ sub apply_transfer_encoding 
        my $from = shift;
        my $to = shift;
  
 -      return $message if ($from eq $to and $from ne '7bit');
 +      return ($message, $to) if ($from eq $to and $from ne '7bit');
  
        require MIME::QuotedPrint;
        require MIME::Base64;
        $message = MIME::Base64::decode($message)
                if ($from eq 'base64');
  
 -      $to = ($message =~ /.{999,}/) ? 'quoted-printable' : '8bit'
 +      $to = ($message =~ /(?:.{999,}|\r)/) ? 'quoted-printable' : '8bit'
                if $to eq 'auto';
  
        die __("cannot send message as 7bit")
diff --combined t/t9001-send-email.sh
index 1e3ac3c3846342ac84baa096ab00ed602e4cdf27,4be4b50ff9f2d49de9562f3337ead97dea70e4eb..997f90b42b3e513328b23f718ac7215eb9a960b8
@@@ -253,9 -253,10 +253,9 @@@ test_suppress_self () 
  
        mv msgtxt1 msgtxt1-$3 &&
        sed -e '/^$/q' msgtxt1-$3 >"msghdr1-$3" &&
 -      >"expected-no-cc-$3" &&
  
        (grep '^Cc:' msghdr1-$3 >"actual-no-cc-$3";
 -       test_cmp expected-no-cc-$3 actual-no-cc-$3)
 +       test_must_be_empty actual-no-cc-$3)
  }
  
  test_suppress_self_unquoted () {
@@@ -331,7 -332,7 +331,7 @@@ test_expect_success $PREREQ 'Show all h
  
  test_expect_success $PREREQ 'Prompting works' '
        clean_fake_sendmail &&
 -      (echo "to@example.com"
 +      (echo "to@example.com" &&
         echo ""
        ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
@@@ -481,20 -482,6 +481,20 @@@ test_expect_success $PREREQ 'long line
        grep "Content-Transfer-Encoding: quoted-printable" msgtxt1
  '
  
 +test_expect_success $PREREQ 'carriage returns with auto encoding are quoted-printable' '
 +      clean_fake_sendmail &&
 +      cp $patches cr.patch &&
 +      printf "this is a line\r\n" >>cr.patch &&
 +      git send-email \
 +              --from="Example <nobody@example.com>" \
 +              --to=nobody@example.com \
 +              --smtp-server="$(pwd)/fake.sendmail" \
 +              --transfer-encoding=auto \
 +              --no-validate \
 +              cr.patch &&
 +      grep "Content-Transfer-Encoding: quoted-printable" msgtxt1
 +'
 +
  for enc in auto quoted-printable base64
  do
        test_expect_success $PREREQ "--validate passes with encoding $enc" '
                        --validate \
                        $patches longline.patch
        '
 +
 +done
 +
 +for enc in 7bit 8bit quoted-printable base64
 +do
 +      test_expect_success $PREREQ "--transfer-encoding=$enc produces correct header" '
 +              clean_fake_sendmail &&
 +              git send-email \
 +                      --from="Example <nobody@example.com>" \
 +                      --to=nobody@example.com \
 +                      --smtp-server="$(pwd)/fake.sendmail" \
 +                      --transfer-encoding=$enc \
 +                      $patches &&
 +              grep "Content-Transfer-Encoding: $enc" msgtxt1
 +      '
  done
  
  test_expect_success $PREREQ 'Invalid In-Reply-To' '
  
  test_expect_success $PREREQ 'Valid In-Reply-To when prompting' '
        clean_fake_sendmail &&
 -      (echo "From Example <from@example.com>"
 -       echo "To Example <to@example.com>"
 +      (echo "From Example <from@example.com>" &&
 +       echo "To Example <to@example.com>" &&
         echo ""
        ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
@@@ -1204,7 -1176,7 +1204,7 @@@ test_expect_success $PREREQ 'no in-repl
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --no-thread \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        ! grep "In-Reply-To: " stdout
  '
  
@@@ -1224,17 -1196,72 +1224,72 @@@ test_expect_success $PREREQ 'sendemail.
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "To: Somebody <somebody@ex.com>" stdout
  '
  
+ test_expect_success $PREREQ 'setup sendemail.identity' '
+       git config --replace-all sendemail.to "default@example.com" &&
+       git config --replace-all sendemail.isp.to "isp@example.com" &&
+       git config --replace-all sendemail.cloud.to "cloud@example.com"
+ '
+ test_expect_success $PREREQ 'sendemail.identity: reads the correct identity config' '
+       git -c sendemail.identity=cloud send-email \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: cloud@example.com" stdout
+ '
+ test_expect_success $PREREQ 'sendemail.identity: identity overrides sendemail.identity' '
+       git -c sendemail.identity=cloud send-email \
+               --identity=isp \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: isp@example.com" stdout
+ '
+ test_expect_success $PREREQ 'sendemail.identity: --no-identity clears previous identity' '
+       git -c sendemail.identity=cloud send-email \
+               --no-identity \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: default@example.com" stdout
+ '
+ test_expect_success $PREREQ 'sendemail.identity: bool identity variable existance overrides' '
+       git -c sendemail.identity=cloud \
+               -c sendemail.xmailer=true \
+               -c sendemail.cloud.xmailer=false \
+               send-email \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: cloud@example.com" stdout &&
+       ! grep "X-Mailer" stdout
+ '
+ test_expect_success $PREREQ 'sendemail.identity: bool variable fallback' '
+       git -c sendemail.identity=cloud \
+               -c sendemail.xmailer=false \
+               send-email \
+               --dry-run \
+               --from="nobody@example.com" \
+               $patches >stdout &&
+       grep "To: cloud@example.com" stdout &&
+       ! grep "X-Mailer" stdout
+ '
  test_expect_success $PREREQ '--no-to overrides sendemail.to' '
        git send-email \
                --dry-run \
                --from="Example <nobody@example.com>" \
                --no-to \
                --to=nobody@example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "To: nobody@example.com" stdout &&
        ! grep "To: Somebody <somebody@ex.com>" stdout
  '
@@@ -1245,7 -1272,7 +1300,7 @@@ test_expect_success $PREREQ 'sendemail.
                --dry-run \
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "Cc: Somebody <somebody@ex.com>" stdout
  '
  
@@@ -1256,7 -1283,7 +1311,7 @@@ test_expect_success $PREREQ '--no-cc ov
                --no-cc \
                --cc=bodies@example.com \
                --to=nobody@example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "Cc: bodies@example.com" stdout &&
        ! grep "Cc: Somebody <somebody@ex.com>" stdout
  '
@@@ -1268,7 -1295,7 +1323,7 @@@ test_expect_success $PREREQ 'sendemail.
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --smtp-server relay.example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "RCPT TO:<other@ex.com>" stdout
  '
  
@@@ -1280,7 -1307,7 +1335,7 @@@ test_expect_success $PREREQ '--no-bcc o
                --bcc=bodies@example.com \
                --to=nobody@example.com \
                --smtp-server relay.example.com \
-               $patches $patches >stdout &&
+               $patches >stdout &&
        grep "RCPT TO:<bodies@example.com>" stdout &&
        ! grep "RCPT TO:<other@ex.com>" stdout
  '
@@@ -1437,10 -1464,10 +1492,10 @@@ test_expect_success $PREREQ 'setup expe
        EOF
  '
  
- test_expect_success $PREREQ 'sendemail.transferencoding=7bit fails on 8bit data' '
+ test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
        clean_fake_sendmail &&
-       git config sendemail.transferEncoding 7bit &&
-       test_must_fail git send-email \
+       test_must_fail git -c sendemail.transferEncoding=8bit \
+               send-email \
                --transfer-encoding=7bit \
                --smtp-server="$(pwd)/fake.sendmail" \
                email-using-8bit \
        test -z "$(ls msgtxt*)"
  '
  
- test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
+ test_expect_success $PREREQ 'sendemail.transferEncoding via config' '
        clean_fake_sendmail &&
-       git config sendemail.transferEncoding 8bit &&
-       test_must_fail git send-email \
-               --transfer-encoding=7bit \
+       test_must_fail git -c sendemail.transferEncoding=7bit \
+               send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
                email-using-8bit \
                2>errors >out &&
        test -z "$(ls msgtxt*)"
  '
  
- test_expect_success $PREREQ 'sendemail.transferencoding=8bit' '
+ test_expect_success $PREREQ 'sendemail.transferEncoding via cli' '
        clean_fake_sendmail &&
-       git send-email \
-               --transfer-encoding=8bit \
+       test_must_fail git send-email \
+               --transfer-encoding=7bit \
                --smtp-server="$(pwd)/fake.sendmail" \
                email-using-8bit \
                2>errors >out &&
-       sed '1,/^$/d' msgtxt1 >actual &&
-       sed '1,/^$/d' email-using-8bit >expected &&
-       test_cmp expected actual
+       grep "cannot send message as 7bit" errors &&
+       test -z "$(ls msgtxt*)"
  '
  
  test_expect_success $PREREQ 'setup expect' '
@@@ -1787,6 -1812,15 +1840,15 @@@ test_expect_success '--dump-aliases mus
        test_must_fail git send-email --dump-aliases --to=janice@example.com -1 refs/heads/accounting
  '
  
+ test_expect_success $PREREQ 'aliases and sendemail.identity' '
+       test_must_fail git \
+               -c sendemail.identity=cloud \
+               -c sendemail.aliasesfile=default-aliases \
+               -c sendemail.cloud.aliasesfile=cloud-aliases \
+               send-email -1 2>stderr &&
+       test_i18ngrep "cloud-aliases" stderr
+ '
  test_sendmail_aliases () {
        msg="$1" && shift &&
        expect="$@" &&
@@@ -2051,11 -2085,11 +2113,11 @@@ test_expect_success $PREREQ 'invoke hoo
  
                # Verify error message when a patch is rejected by the hook
                sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
 -              git send-email \
 +              test_must_fail git send-email \
                        --from="Example <nobody@example.com>" \
                        --to=nobody@example.com \
                        --smtp-server="$(pwd)/../fake.sendmail" \
 -                      ../another.patch 2>err
 +                      ../another.patch 2>err &&
                test_i18ngrep "rejected by sendemail-validate hook" err
        )
  '