From f170d220f8765e952c9a102dd35eb694810739df Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 24 Sep 2023 05:42:13 +0000 Subject: lei: fix `-c NAME=VALUE' config support We can pass `-c NAME=VALUE' args directly to git-config without needing a temporary directory nor file. Furthermore, this opens the door to us being able to correctly handle `-c NAME=VALUE' after `delete $lei->{cfg}' if we need to reload the config during a command. This tightens up error-checking for `lei config' and ensures we can make config settings changes while using `-c NAME=VALUE' instead of editing the temporary file. The non-obvious part was avoiding the use of the -f/--file arg for `git config' for read-only operations and include relying on `-c include.path=$ABS_PATH'. This is done by parsing the switches to be passed to `git config' to determine if it's a read-only operation or not. --- lib/PublicInbox/Config.pm | 52 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 11 deletions(-) (limited to 'lib/PublicInbox/Config.pm') diff --git a/lib/PublicInbox/Config.pm b/lib/PublicInbox/Config.pm index f6236d84..533f4a52 100644 --- a/lib/PublicInbox/Config.pm +++ b/lib/PublicInbox/Config.pm @@ -22,7 +22,7 @@ sub _array ($) { ref($_[0]) eq 'ARRAY' ? $_[0] : [ $_[0] ] } # returns key-value pairs of config directives in a hash # if keys may be multi-value, the value is an array ref containing all values sub new { - my ($class, $file, $errfh) = @_; + my ($class, $file, $lei) = @_; $file //= default_file(); my $self; my $set_dedupe; @@ -36,7 +36,7 @@ sub new { $self = $DEDUPE->{$file} and return $self; $set_dedupe = 1; } - $self = git_config_dump($class, $file, $errfh); + $self = git_config_dump($class, $file, $lei); $self->{'-f'} = $file; } # caches @@ -174,13 +174,34 @@ sub config_fh_parse ($$$) { \%rv; } +sub tmp_cmd_opt ($$) { + my ($env, $opt) = @_; + # quiet global and system gitconfig if supported by installed git, + # but normally harmless if too noisy (NOGLOBAL no longer exists) + $env->{GIT_CONFIG_NOSYSTEM} = 1; + $env->{GIT_CONFIG_GLOBAL} = '/dev/null'; # git v2.32+ + $opt->{-C} = '/'; # avoid $worktree/.git/config on MOST systems :P +} + sub git_config_dump { - my ($class, $file, $errfh) = @_; - return bless {}, $class unless -e $file; - my $cmd = [ qw(git config -z -l --includes), "--file=$file" ]; - my $fh = popen_rd($cmd, undef, { 2 => $errfh // 2 }); + my ($class, $file, $lei) = @_; + my @opt_c = map { ('-c', $_) } @{$lei->{opt}->{c} // []}; + $file = undef if !-e $file; + # XXX should we set {-f} if !-e $file? + return bless {}, $class if (!@opt_c && !defined($file)); + my %env; + my $opt = { 2 => $lei->{2} // 2 }; + if (@opt_c) { + unshift(@opt_c, '-c', "include.path=$file") if defined($file); + tmp_cmd_opt(\%env, $opt); + } + my @cmd = ('git', @opt_c, qw(config -z -l --includes)); + push(@cmd, '-f', $file) if !@opt_c && defined($file); + my $fh = popen_rd(\@cmd, \%env, $opt); my $rv = config_fh_parse($fh, "\0", "\n"); - close $fh or die "@$cmd failed: \$?=$?\n"; + close $fh or die "@cmd failed: \$?=$?\n"; + $rv->{-opt_c} = \@opt_c if @opt_c; # for ->urlmatch + $rv->{-f} = $file; bless $rv, $class; } @@ -544,14 +565,23 @@ sub _fill_ei ($$) { $es; } +sub config_cmd { + my ($self, $env, $opt) = @_; + my $f = $self->{-f} // default_file(); + my @opt_c = @{$self->{-opt_c} // []}; + my @cmd = ('git', @opt_c, 'config'); + @opt_c ? tmp_cmd_opt($env, $opt) : push(@cmd, '-f', $f); + \@cmd; +} + sub urlmatch { my ($self, $key, $url, $try_git) = @_; state $urlmatch_broken; # requires git 1.8.5 return if $urlmatch_broken; - my $file = $self->{'-f'} // default_file(); - my $cmd = [qw/git config -z --includes --get-urlmatch/, - "--file=$file", $key, $url ]; - my $fh = popen_rd($cmd); + my (%env, %opt); + my $cmd = $self->config_cmd(\%env, \%opt); + push @$cmd, qw(-z --includes --get-urlmatch), $key, $url; + my $fh = popen_rd($cmd, \%env, \%opt); local $/ = "\0"; my $val = <$fh>; if (!close($fh)) { -- cgit v1.2.3-24-ge0c7