about summary refs log tree commit homepage
path: root/lib/PublicInbox/Git.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PublicInbox/Git.pm')
-rw-r--r--lib/PublicInbox/Git.pm47
1 files changed, 25 insertions, 22 deletions
diff --git a/lib/PublicInbox/Git.pm b/lib/PublicInbox/Git.pm
index aea389e8..6b722023 100644
--- a/lib/PublicInbox/Git.pm
+++ b/lib/PublicInbox/Git.pm
@@ -10,6 +10,7 @@ package PublicInbox::Git;
 use strict;
 use v5.10.1;
 use parent qw(Exporter PublicInbox::DS);
+use PublicInbox::DS qw(now);
 use autodie qw(socketpair read);
 use POSIX ();
 use Socket qw(AF_UNIX SOCK_STREAM);
@@ -25,7 +26,7 @@ use PublicInbox::SHA qw(sha_all);
 our %HEXLEN2SHA = (40 => 1, 64 => 256);
 our %OFMT2HEXLEN = (sha1 => 40, sha256 => 64);
 our @EXPORT_OK = qw(git_unquote git_quote %HEXLEN2SHA %OFMT2HEXLEN
-                        $ck_unlinked_packs);
+                        $ck_unlinked_packs git_exe);
 our $in_cleanup;
 our $async_warn; # true in read-only daemons
 
@@ -54,7 +55,11 @@ my %ESC_GIT = map { $GIT_ESC{$_} => $_ } keys %GIT_ESC;
 my $EXE_ST = ''; # pack('dd', st_dev, st_ino); # no `q' in some 32-bit builds
 my ($GIT_EXE, $GIT_VER);
 
-sub check_git_exe () {
+sub git_exe () {
+        my $now = now;
+        state $next_check = $now - 10;
+        return $GIT_EXE if $now < $next_check;
+        $next_check = $now + 10;
         $GIT_EXE = which('git') // die "git not found in $ENV{PATH}";
         my @st = stat(_) or die "stat($GIT_EXE): $!"; # can't do HiRes w/ _
         my $st = pack('dd', $st[0], $st[1]);
@@ -69,8 +74,8 @@ sub check_git_exe () {
         $GIT_EXE;
 }
 
-sub git_version {
-        check_git_exe();
+sub git_version () {
+        git_exe;
         $GIT_VER;
 }
 
@@ -103,9 +108,10 @@ sub new {
 sub git_path ($$) {
         my ($self, $path) = @_;
         $self->{-git_path}->{$path} //= do {
-                my $d = "$self->{git_dir}/$path";
-                if (-e $d) {
-                        $d;
+                my $d = $self->{git_dir};
+                my $f = "$d/$path";
+                if (-d "$d/objects") {
+                        $f;
                 } else {
                         local $/ = "\n";
                         my $rdr = { 2 => \my $err };
@@ -114,7 +120,7 @@ sub git_path ($$) {
                         chomp $s;
 
                         # git prior to 2.5.0 did not understand --git-path
-                        $s eq "--git-path\n$path" ? $d : $s;
+                        $s eq "--git-path\n$path" ? $f : $s;
                 }
         };
 }
@@ -174,7 +180,7 @@ sub _sock_cmd {
 
         # git 2.31.0+ supports -c core.abbrev=no, don't bother with
         # core.abbrev=64 since not many releases had SHA-256 prior to 2.31
-        my $abbr = $GIT_VER lt v2.31.0 ? 40 : 'no';
+        my $abbr = git_version lt v2.31.0 ? 40 : 'no';
         my @cmd = ($GIT_EXE, "--git-dir=$gd", '-c', "core.abbrev=$abbr",
                         'cat-file', "--$batch");
         if ($err_c) {
@@ -203,7 +209,7 @@ sub cat_async_retry ($$) {
                 $oid = \$oid if !@$new_inflight; # to indicate oid retried
                 push @$new_inflight, $oid, $cb, $arg;
         }
-        $sock->close if $sock; # only safe once old_inflight is empty
+        undef $sock; # gcf_drain may run from PublicInbox::IO::DESTROY
         cat_async_step($self, $new_inflight); # take one step
 }
 
@@ -287,8 +293,7 @@ sub cat_async_wait ($) {
 
 sub batch_prepare ($) {
         my ($self) = @_;
-        check_git_exe();
-        if ($GIT_VER ge BATCH_CMD_VER) {
+        if (git_version ge BATCH_CMD_VER) {
                 $self->{-bc} = 1;
                 _sock_cmd($self, 'batch-command', 1);
         } else {
@@ -344,8 +349,7 @@ sub ck {
 sub check_async_begin ($) {
         my ($self) = @_;
         cleanup($self) if alternates_changed($self);
-        check_git_exe();
-        if ($GIT_VER ge BATCH_CMD_VER) {
+        if (git_version ge BATCH_CMD_VER) {
                 $self->{-bc} = 1;
                 _sock_cmd($self, 'batch-command', 1);
         } else {
@@ -421,15 +425,15 @@ sub async_err ($$$$$) {
 
 sub cmd {
         my $self = shift;
-        [ $GIT_EXE // check_git_exe(), "--git-dir=$self->{git_dir}", @_ ]
+        [ git_exe(), "--git-dir=$self->{git_dir}", @_ ]
 }
 
 # $git->popen(qw(show f00)); # or
 # $git->popen(qw(show f00), { GIT_CONFIG => ... }, { 2 => ... });
 sub popen {
         my ($self, $cmd) = splice(@_, 0, 2);
-        $cmd = [ 'git', "--git-dir=$self->{git_dir}",
-                ref($cmd) ? @$cmd : ($cmd, grep { defined && !ref } @_) ];
+        $cmd = $self->cmd(ref($cmd) ? @$cmd :
+                        ($cmd, grep { defined && !ref } @_));
         popen_rd($cmd, grep { !defined || ref } @_); # env and opt
 }
 
@@ -577,9 +581,8 @@ sub cloneurl {
 # templates/this--description in git.git
 sub manifest_entry {
         my ($self, $epoch, $default_desc) = @_;
-        check_git_exe();
         my $gd = $self->{git_dir};
-        my @git = ($GIT_EXE, "--git-dir=$gd");
+        my @git = (git_exe, "--git-dir=$gd");
         my $sr = popen_rd([@git, 'show-ref']);
         my $own = popen_rd([@git, qw(config gitweb.owner)]);
         my $mod = popen_rd([@git, @MODIFIED_DATE]);
@@ -637,7 +640,8 @@ sub event_step {
         my ($self) = @_;
         my $inflight = gcf_inflight($self);
         if ($inflight && @$inflight) {
-                $self->cat_async_step($inflight);
+                eval { $self->cat_async_step($inflight) };
+                warn "E: $self->{git_dir}: $@" if $@;
                 return $self->close unless $self->{sock};
                 # don't loop here to keep things fair, but we must requeue
                 # if there's already-read data in pi_io_rbuf
@@ -662,10 +666,9 @@ sub watch_async ($) {
 
 sub close {
         my ($self) = @_;
-        my $sock = $self->{sock};
         delete @$self{qw(-bc err_c inflight)};
         delete($self->{epwatch}) ? $self->SUPER::close : delete($self->{sock});
-        $sock->close if $sock; # calls gcf_drain via awaitpid
+        # gcf_drain may run from PublicInbox::IO::DESTROY
 }
 
 package PublicInbox::GitCheck; # only for git <2.36