git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 4/5] send-email: fix regression in sendemail.identity parsing

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
May 17, 2019, 19:55 UTC
Message-ID
<20190517195545.29729-5-avarab@gmail.com>
In-Reply-To
<xmqqsgtd3fw3.fsf@gitster-ct.c.googlers.com>

Fix a regression in my recent 3494dfd3ee ("send-email: do defaults -> config -> getopt in that order", 2019-05-09). I missed that the $identity variable needs to be extracted from the command-line before we do the config reading, as it determines which config variable we should read first. See [1] for the report.

The sendemail.identity feature was added back in 34cc60ce2b ("send-email: Add support for SSL and SMTP-AUTH", 2007-09-03), there were no tests to assert that it worked properly.

So let's fix both the regression, and add some tests to assert that this is being parsed properly. While I'm at it I'm adding a --no-identity option to go with --[to|cc|bcc] variable, since the semantics are similar. It's like to/cc/bcc except that unlike those we don't support multiple identities, but we could now easily add it support for it if anyone cares.

In just fixing the --identity command-line parsing bug I discovered that a narrow fix to that wouldn't do. In read_config() we had a state machine that would only set config values if they weren't set already, and thus by proxy we wouldn't e.g. set "to" based on sendemail.to if we'd seen sendemail.gmail.to before, with --identity=gmail.

I'd modified some of the relevant code in 3494dfd3ee, but just reverting to that wouldn't do, since it would bring back the regression fixed in that commit.

Refactor read_config() do what we actually mean here. We don't want to set a given sendemail.VAR if a sendemail.$identity.VAR previously set it. The old code was conflating this desire with the hardcoded defaults for these variables, and as discussed in 3494dfd3ee that was never going to work. Instead pass along the state of whether an identity config set something before, as distinguished from the state of the default just being false, or the default being a non-bool or true (e.g. --transferencoding).

I'm still not happy with the test coverage here, e.g. there's nothing testing sendemail.smtpEncryption, but I only have so much time to fix this code.

1. https://public-inbox.org/git/5cddeb61.1c69fb81.47ed4.e648@mx.google.com/
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 Documentation/git-send-email.txt |  4 ++
 git-send-email.perl              | 60 ++++++++++++++++++++----------
 t/t9001-send-email.sh            | 64 ++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+), 20 deletions(-)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 8100ff4b0f..a861934c69 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -282,6 +282,10 @@ Automating
 	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.
diff --git a/git-send-email.perl b/git-send-email.perl
index b2c4a77671..80cbbfd2b8 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -182,7 +182,7 @@ sub format_2822_time {
 	$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);
+my ($no_cc, $no_to, $no_bcc, $no_identity);
 my (@config_to, @getopt_to);
 my (@config_cc, @getopt_cc);
 my (@config_bcc, @getopt_bcc);
@@ -325,44 +325,53 @@ sub signal_handler {
 
 # Read our sendemail.* config
 sub read_config {
-	my ($prefix) = @_;
+	my ($configured, $prefix) = @_;
 
 	foreach my $setting (keys %config_bool_settings) {
 		my $target = $config_bool_settings{$setting};
 		my $v = Git::config_bool(@repo, "$prefix.$setting");
-		$$target = $v if defined $v;
+		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") {
-			unless (@$target) {
-				my @values = Git::config_path(@repo, "$prefix.$setting");
-				@$target = @values if (@values && defined $values[0]);
-			}
+			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");
-			$$target = $v if defined $v;
+			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") {
-			unless (@$target) {
-				my @values = Git::config(@repo, "$prefix.$setting");
-				@$target = @values if (@values && defined $values[0]);
-			}
+			my @values = Git::config(@repo, "$prefix.$setting");
+			next unless @values;
+			next if $configured->{$setting}++;
+			@$target = @values;
 		}
 		else {
 			my $v = Git::config(@repo, "$prefix.$setting");
-			$$target = $v if defined $v;
+			next unless defined $v;
+			next if $configured->{$setting}++;
+			$$target = $v;
 		}
 	}
 
 	if (!defined $smtp_encryption) {
-		my $enc = Git::config(@repo, "$prefix.smtpencryption");
+		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")) {
@@ -371,17 +380,30 @@ sub read_config {
 	}
 }
 
+# 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");
-read_config("sendemail.$identity") if defined $identity;
-read_config("sendemail");
+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 $git_completion_helper;
-my $rc = GetOptions("h" => \$help,
-                    "dump-aliases" => \$dump_aliases);
+$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;
@@ -410,8 +432,6 @@ sub read_config {
 		    "smtp-debug:i" => \$debug_net_smtp,
 		    "smtp-domain:s" => \$smtp_domain,
 		    "smtp-auth=s" => \$smtp_auth,
-		    "no-smtp-auth" => sub {$smtp_auth = 'none'},
-		    "identity=s" => \$identity,
 		    "annotate!" => \$annotate,
 		    "no-annotate" => sub {$annotate = 0},
 		    "compose" => \$compose,
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 61d484d1a6..890e2874c3 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1228,6 +1228,61 @@ test_expect_success $PREREQ 'sendemail.to works' '
 	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 \
@@ -1785,6 +1840,15 @@ test_expect_success '--dump-aliases must be used alone' '
 	test_must_fail git send-email --dump-aliases --to=janice@example.com -1 refs/heads/accounting
 '
 
+test_expect_success '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="$@" &&
-- 
2.21.0.1020.gf2820cf01a
Previous: Ævar Arnfjörð BjarmasonNext: Junio C Hamano
Message 28 of 39 in “send-email: fix transferencoding config option”
  1. 1/1 send-email: fix transferencoding config optionHeinrich Schuchardt, Apr 9, 2019
  2. Jonathan NiederApr 9, 2019
  3. Heinrich SchuchardtApr 9, 2019
  4. Junio C HamanoApr 10, 2019
  5. Heinrich SchuchardtApr 10, 2019
  6. brian m. carlsonApr 10, 2019
  7. Re* [PATCH 1/1] send-email: fix transferencoding config optionJunio C Hamano, May 8, 2019
  8. 2/2 send-email: honor transferencoding config option againJunio C Hamano, May 8, 2019
  9. Junio C HamanoMay 8, 2019
  10. 0/2 send-email: set xfer encoding correctlyJunio C Hamano, May 8, 2019
  11. 0/3 send-email: fix cli->config parsing crazynessÆvar Arnfjörð Bjarmason, May 9, 2019
  12. Junio C HamanoMay 10, 2019
  13. 1/3 send-email: move the read_config() function above getoptsÆvar Arnfjörð Bjarmason, May 9, 2019
  14. 2/3 send-email: rename the @bcclist variable for consistencyÆvar Arnfjörð Bjarmason, May 9, 2019
  15. 3/3 send-email: do defaults -> config -> getopt in that orderÆvar Arnfjörð Bjarmason, May 9, 2019
  16. Eric SunshineMay 9, 2019
  17. Junio C HamanoMay 13, 2019
  18. brian m. carlsonMay 9, 2019
  19. Junio C HamanoMay 13, 2019
  20. Ævar Arnfjörð BjarmasonMay 13, 2019
  21. Stephen BoydMay 16, 2019
  22. Junio C HamanoMay 16, 2019
  23. Junio C HamanoMay 17, 2019
  24. 0/5 ab/send-email-transferencoding-fix-for-the-fixÆvar Arnfjörð Bjarmason, May 17, 2019
  25. 1/5 send-email: remove cargo-culted multi-patch pattern in testsÆvar Arnfjörð Bjarmason, May 17, 2019
  26. 2/5 send-email: fix broken transferEncoding testsÆvar Arnfjörð Bjarmason, May 17, 2019
  27. 3/5 send-email: document --no-[to|cc|bcc]Ævar Arnfjörð Bjarmason, May 17, 2019
  28. 4/5 send-email: fix regression in sendemail.identity parsingÆvar Arnfjörð Bjarmason, May 17, 2019
  29. Junio C HamanoMay 19, 2019
  30. Johannes SchindelinMay 22, 2019
  31. Johannes SchindelinMay 29, 2019
  32. 5/5 send-email: remove support for deprecated sendemail.smtpsslÆvar Arnfjörð Bjarmason, May 17, 2019
  33. 2/2 send-email: honor transferencoding config option againJunio C Hamano, May 8, 2019
  34. Eric SunshineMay 8, 2019
  35. Junio C HamanoMay 9, 2019
  36. brian m. carlsonMay 8, 2019
  37. 1/2 send-email: update the mechanism to set default configuration valuesJunio C Hamano, May 8, 2019
  38. brian m. carlsonApr 9, 2019
  39. Heinrich SchuchardtApr 9, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.