]> git.ipfire.org Git - thirdparty/git.git/commitdiff
send-email: lazily load config for a big speedup
authorÆvar Arnfjörð Bjarmason <avarab@gmail.com>
Fri, 28 May 2021 09:23:45 +0000 (11:23 +0200)
committerJunio C Hamano <gitster@pobox.com>
Fri, 28 May 2021 09:38:07 +0000 (18:38 +0900)
Reduce the time it takes git-send-email to get to even the most
trivial of tasks (such as serving up its "-h" output) by first listing
config keys that exist, and only then only call e.g. "git config
--bool" on them if they do.

Over a lot of runs this speeds the time to "-h" up for me from ~250ms
to ~150ms, and the runtime of t9001-send-email.sh goes from ~25s to
~20s.

This introduces a race condition where we'll do the "wrong" thing if a
config key were to be inserted between us discovering the list and
calling read_config(), i.e. we won't know about the racily added
key. In theory this is a change in behavior, in practice it doesn't
matter.

The config_regexp() function being changed here was added in
dd84e528a34 (git-send-email: die if sendmail.* config is set,
2020-07-23) for use by git-send-email. So we can change its odd return
value in the case where no values are found by "git config". The
difference in the *.pm code would matter if it was invoked in scalar
context, but now it no longer is.

Arguably this caching belongs in Git.pm itself, but in lieu of
modifying it for all its callers let's only do this for "git
send-email". The other big potential win would be "git svn", but
unlike "git send-email" it doesn't check tens of config variables one
at a time at startup (in my brief testing it doesn't check any).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-send-email.perl

index 73e3d3fd26e37bb167fae0610f2a0ec0b8c257c0..de62cbf2506bd459c2b9604ceb3ce8beec583c0a 100755 (executable)
@@ -347,11 +347,13 @@ $SIG{INT}  = \&signal_handler;
 
 # Read our sendemail.* config
 sub read_config {
-       my ($configured, $prefix) = @_;
+       my ($known_keys, $configured, $prefix) = @_;
 
        foreach my $setting (keys %config_bool_settings) {
                my $target = $config_bool_settings{$setting};
-               my $v = Git::config_bool(@repo, "$prefix.$setting");
+               my $key = "$prefix.$setting";
+               next unless exists $known_keys->{$key};
+               my $v = Git::config_bool(@repo, $key);
                next unless defined $v;
                next if $configured->{$setting}++;
                $$target = $v;
@@ -359,8 +361,10 @@ sub read_config {
 
        foreach my $setting (keys %config_path_settings) {
                my $target = $config_path_settings{$setting};
+               my $key = "$prefix.$setting";
+               next unless exists $known_keys->{$key};
                if (ref($target) eq "ARRAY") {
-                       my @values = Git::config_path(@repo, "$prefix.$setting");
+                       my @values = Git::config_path(@repo, $key);
                        next unless @values;
                        next if $configured->{$setting}++;
                        @$target = @values;
@@ -375,14 +379,16 @@ sub read_config {
 
        foreach my $setting (keys %config_settings) {
                my $target = $config_settings{$setting};
+               my $key = "$prefix.$setting";
+               next unless exists $known_keys->{$key};
                if (ref($target) eq "ARRAY") {
-                       my @values = Git::config(@repo, "$prefix.$setting");
+                       my @values = Git::config(@repo, $key);
                        next unless @values;
                        next if $configured->{$setting}++;
                        @$target = @values;
                }
                else {
-                       my $v = Git::config(@repo, "$prefix.$setting");
+                       my $v = Git::config(@repo, $key);
                        next unless defined $v;
                        next if $configured->{$setting}++;
                        $$target = $v;
@@ -408,9 +414,20 @@ sub config_regexp {
        return @ret;
 }
 
+# Save ourselves a lot of work of shelling out to 'git config' (it
+# parses 'bool' etc.) by only doing so for config keys that exist.
+my %known_config_keys;
+{
+       my @known_config_keys = config_regexp("^sende?mail[.]");
+       @known_config_keys{@known_config_keys} = ();
+}
+
 # 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 $key = "sendemail.identity";
+       $identity = Git::config(@repo, $key) if exists $known_config_keys{$key};
+}
 my $rc = GetOptions(
        "identity=s" => \$identity,
        "no-identity" => \$no_identity,
@@ -421,8 +438,8 @@ 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");
+    read_config(\%known_config_keys, \%configured, "sendemail.$identity") if defined $identity;
+    read_config(\%known_config_keys, \%configured, "sendemail");
 }
 
 # Begin by accumulating all the variables (defined above), that we will end up
@@ -506,7 +523,7 @@ unless ($rc) {
     usage();
 }
 
-if ($forbid_sendmail_variables && (scalar config_regexp("^sendmail[.]")) != 0) {
+if ($forbid_sendmail_variables && grep { /^sendmail/s } keys %known_config_keys) {
        die __("fatal: found configuration options for 'sendmail'\n" .
                "git-send-email is configured with the sendemail.* options - note the 'e'.\n" .
                "Set sendemail.forbidSendmailVariables to false to disable this check.\n");