Skip to content

Commit 9264d29

Browse files
avargitster
authored andcommitted
send-email: lazily load config for a big speedup
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 dd84e52 (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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2b110e9 commit 9264d29

File tree

1 file changed

+26
-9
lines changed

1 file changed

+26
-9
lines changed

git-send-email.perl

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -347,20 +347,24 @@ sub signal_handler {
347347

348348
# Read our sendemail.* config
349349
sub read_config {
350-
my ($configured, $prefix) = @_;
350+
my ($known_keys, $configured, $prefix) = @_;
351351

352352
foreach my $setting (keys %config_bool_settings) {
353353
my $target = $config_bool_settings{$setting};
354-
my $v = Git::config_bool(@repo, "$prefix.$setting");
354+
my $key = "$prefix.$setting";
355+
next unless exists $known_keys->{$key};
356+
my $v = Git::config_bool(@repo, $key);
355357
next unless defined $v;
356358
next if $configured->{$setting}++;
357359
$$target = $v;
358360
}
359361

360362
foreach my $setting (keys %config_path_settings) {
361363
my $target = $config_path_settings{$setting};
364+
my $key = "$prefix.$setting";
365+
next unless exists $known_keys->{$key};
362366
if (ref($target) eq "ARRAY") {
363-
my @values = Git::config_path(@repo, "$prefix.$setting");
367+
my @values = Git::config_path(@repo, $key);
364368
next unless @values;
365369
next if $configured->{$setting}++;
366370
@$target = @values;
@@ -375,14 +379,16 @@ sub read_config {
375379

376380
foreach my $setting (keys %config_settings) {
377381
my $target = $config_settings{$setting};
382+
my $key = "$prefix.$setting";
383+
next unless exists $known_keys->{$key};
378384
if (ref($target) eq "ARRAY") {
379-
my @values = Git::config(@repo, "$prefix.$setting");
385+
my @values = Git::config(@repo, $key);
380386
next unless @values;
381387
next if $configured->{$setting}++;
382388
@$target = @values;
383389
}
384390
else {
385-
my $v = Git::config(@repo, "$prefix.$setting");
391+
my $v = Git::config(@repo, $key);
386392
next unless defined $v;
387393
next if $configured->{$setting}++;
388394
$$target = $v;
@@ -408,9 +414,20 @@ sub config_regexp {
408414
return @ret;
409415
}
410416

417+
# Save ourselves a lot of work of shelling out to 'git config' (it
418+
# parses 'bool' etc.) by only doing so for config keys that exist.
419+
my %known_config_keys;
420+
{
421+
my @known_config_keys = config_regexp("^sende?mail[.]");
422+
@known_config_keys{@known_config_keys} = ();
423+
}
424+
411425
# sendemail.identity yields to --identity. We must parse this
412426
# special-case first before the rest of the config is read.
413-
$identity = Git::config(@repo, "sendemail.identity");
427+
{
428+
my $key = "sendemail.identity";
429+
$identity = Git::config(@repo, $key) if exists $known_config_keys{$key};
430+
}
414431
my $rc = GetOptions(
415432
"identity=s" => \$identity,
416433
"no-identity" => \$no_identity,
@@ -421,8 +438,8 @@ sub config_regexp {
421438
# Now we know enough to read the config
422439
{
423440
my %configured;
424-
read_config(\%configured, "sendemail.$identity") if defined $identity;
425-
read_config(\%configured, "sendemail");
441+
read_config(\%known_config_keys, \%configured, "sendemail.$identity") if defined $identity;
442+
read_config(\%known_config_keys, \%configured, "sendemail");
426443
}
427444

428445
# Begin by accumulating all the variables (defined above), that we will end up
@@ -506,7 +523,7 @@ sub config_regexp {
506523
usage();
507524
}
508525

509-
if ($forbid_sendmail_variables && (scalar config_regexp("^sendmail[.]")) != 0) {
526+
if ($forbid_sendmail_variables && grep { /^sendmail/s } keys %known_config_keys) {
510527
die __("fatal: found configuration options for 'sendmail'\n" .
511528
"git-send-email is configured with the sendemail.* options - note the 'e'.\n" .
512529
"Set sendemail.forbidSendmailVariables to false to disable this check.\n");

0 commit comments

Comments
 (0)