We'll be using it for more than just cat-file.
Adding a `popen' API for internal use allows us to save a bunch
of code in other places.
lib/PublicInbox/Config.pm
lib/PublicInbox/Feed.pm
lib/PublicInbox/Filter.pm
-lib/PublicInbox/GitCatFile.pm
+lib/PublicInbox/Git.pm
lib/PublicInbox/Hval.pm
lib/PublicInbox/MDA.pm
lib/PublicInbox/View.pm
my $path = "HEAD:" . mid2path($mid);
foreach my $n (@nox) {
- my @cmd = ('git', "--git-dir=$n->{git_dir}", 'cat-file',
- '-t', $path);
- my $pid = open my $fh, '-|';
- defined $pid or die "fork failed: $!\n";
-
- if ($pid == 0) {
- open STDERR, '>', '/dev/null'; # ignore errors
- exec @cmd or die "exec failed: $!\n";
- } else {
- my $type = eval { local $/; <$fh> };
- close $fh;
- if ($? == 0 && $type eq "blob\n") {
- return r302($n->{url}, $mid);
- }
- }
+ # TODO: reuse existing PublicInbox::Git objects to save forks
+ my $git = PublicInbox::Git->new($n->{git_dir});
+ my (undef, $type, undef) = $git->check($path);
+ return r302($n->{url}, $mid) if ($type eq 'blob');
}
# fall back to partial MID matching
use Email::MIME;
use Date::Parse qw(strptime);
use PublicInbox::Hval;
-use PublicInbox::GitCatFile;
+use PublicInbox::Git;
use PublicInbox::View;
use PublicInbox::MID qw/mid_clean mid2path/;
use POSIX qw/strftime/;
my $max = $ctx->{max} || MAX_PER_PAGE;
my $feed_opts = get_feedopts($ctx);
my $x = atom_header($feed_opts);
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+ my $git = $ctx->{git} ||= PublicInbox::Git->new($ctx->{git_dir});
each_recent_blob($ctx, sub {
my ($path, undef, $ts) = @_;
if (defined $x) {
}
add_to_feed($feed_opts, $fh, $path, $git);
});
- $git = undef; # destroy pipes
end_feed($fh);
}
$feed_opts->{url} = $html_url;
$feed_opts->{emit_header} = 1;
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+ my $git = $ctx->{git} ||= PublicInbox::Git->new($ctx->{git_dir});
foreach my $msg (@{$res->{msgs}}) {
add_to_feed($feed_opts, $fh, mid2path($msg->mid), $git);
}
- $git = undef; # destroy pipes
end_feed($fh);
}
sub emit_index_nosrch {
my ($ctx, $state, $fh) = @_;
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+ my $git = $ctx->{git} ||= PublicInbox::Git->new($ctx->{git_dir});
my (undef, $last) = each_recent_blob($ctx, sub {
my ($path, $commit, $ts, $u, $subj) = @_;
$state->{first} ||= $commit;
# get recent messages
# we could use git log -z, but, we already know ssoma will not
# leave us with filenames with spaces in them..
- my @cmd = ('git', "--git-dir=$ctx->{git_dir}",
- qw/log --no-notes --no-color --raw -r
- --abbrev=16 --abbrev-commit/,
- "--format=%h%x00%ct%x00%an%x00%s%x00");
- push @cmd, $range;
-
- my $pid = open(my $log, '-|', @cmd) or
- die('open `'.join(' ', @cmd) . " pipe failed: $!\n");
+ my $git = $ctx->{git} ||= PublicInbox::Git->new($ctx->{git_dir});
+ my $log = $git->popen(qw/log --no-notes --no-color --raw -r
+ --abbrev=16 --abbrev-commit/,
+ "--format=%h%x00%ct%x00%an%x00%s%x00",
+ $range);
my %deleted; # only an optimization at this point
my $last;
my $nr = 0;
# This is based on code in Git.pm which is GPLv2, but modified to avoid
# dependence on environment variables for compatibility with mod_perl.
# There are also API changes to simplify our usage and data set.
-package PublicInbox::GitCatFile;
+package PublicInbox::Git;
use strict;
use warnings;
use POSIX qw(dup2);
die $msg;
}
+sub popen {
+ my ($self, @cmd) = @_;
+ my $mode = '-|';
+ $mode = shift @cmd if ($cmd[0] eq '|-');
+ @cmd = ('git', "--git-dir=$self->{git_dir}", @cmd);
+ my $pid = open my $fh, $mode, @cmd or
+ die('open `'.join(' ', @cmd) . " pipe failed: $!\n");
+ $fh;
+}
+
sub cleanup {
my ($self) = @_;
_destroy($self, qw(in out pid));
my $fh = $response->([200, ['Content-Type' => "application/$type"]]);
$fh = PublicInbox::MboxGz->new($fh) if $sfx;
- require PublicInbox::GitCatFile;
+ require PublicInbox::Git;
my $mid = $ctx->{mid};
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+ my $git = $ctx->{git} ||= PublicInbox::Git->new($ctx->{git_dir});
my %opts = (offset => 0);
my $nr;
do {
use fields qw(nntpd article rbuf ng long_res);
use PublicInbox::Search;
use PublicInbox::Msgmap;
-use PublicInbox::GitCatFile;
+use PublicInbox::Git;
use PublicInbox::MID qw(mid2path);
use Email::MIME;
use Data::Dumper qw(Dumper);
require Danga::Socket;
require PublicInbox::Msgmap;
require PublicInbox::Search;
-require PublicInbox::GitCatFile;
+require PublicInbox::Git;
sub new {
my ($class, $name, $git_dir, $address) = @_;
sub gcf {
my ($self) = @_;
$self->{gcf} ||= eval {
- my $gcf = PublicInbox::GitCatFile->new($self->{git_dir});
+ my $gcf = PublicInbox::Git->new($self->{git_dir});
# git repos may be repacked and old packs unlinked
defer_weaken($self, 'gcf');
use warnings;
use base qw(PublicInbox::Search);
use PublicInbox::MID qw/mid_clean id_compress/;
+require PublicInbox::Git;
*xpfx = *PublicInbox::Search::xpfx;
use constant MAX_MID_SIZE => 244; # max term size - 1 in Xapian
my $h40 = $hex .'{40}';
my $addmsg = qr!^:000000 100644 \S+ ($h40) A\t${hex}{2}/${hex}{38}$!;
my $delmsg = qr!^:100644 000000 ($h40) \S+ D\t${hex}{2}/${hex}{38}$!;
- my $git_dir = $self->{git_dir};
- require PublicInbox::GitCatFile;
- my $git = PublicInbox::GitCatFile->new($git_dir);
- my @cmd = ('git', "--git-dir=$git_dir", "log",
- qw/--reverse --no-notes --no-color --raw -r --no-abbrev/,
- $range);
+ my $git = PublicInbox::Git->new($self->{git_dir});
+ my $log = $git->popen(qw/log --reverse --no-notes --no-color
+ --raw -r --no-abbrev/, $range);
my $latest;
my $bytes;
- my $pid = open(my $log, '-|', @cmd) or
- die('open` '.join(' ', @cmd) . " pipe failed: $!\n");
while (my $line = <$log>) {
if ($line =~ /$addmsg/o) {
my $mime = do_cat_mail($git, $1, \$bytes) or next;
sub _read_git_config_perm {
my ($self) = @_;
- my @cmd = ('git', "--git-dir=$self->{git_dir}",
- qw(config core.sharedRepository));
- my $pid = open(my $fh, '-|', @cmd) or
- die('open `'.join(' ', @cmd) . " pipe failed: $!\n");
+ my @cmd = qw(config core.sharedRepository);
+ my $fh = PublicInbox::Git->new($self->{git_dir})->popen(@cmd);
my $perm = <$fh>;
close $fh;
chomp $perm if defined $perm;
use PublicInbox::View;
use PublicInbox::MID qw(mid2path mid_clean);
use Email::MIME;
+require PublicInbox::Git;
our $LIM = 50;
sub sres_top_html {
$th->order(*PublicInbox::View::rsort_ts);
}
- require PublicInbox::GitCatFile;
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+ my $git = $ctx->{git} ||= PublicInbox::Git->new($ctx->{git_dir});
my $state = { ctx => $ctx, anchor_idx => 0, pct => \%pct };
$ctx->{searchview} = 1;
tdump_ent($fh, $git, $state, $_, 0) for $th->rootset;
- $git = undef;
Email::Address->purge_cache;
$fh->write(search_nav_bot($mset, $q). "\n\n" .
sub adump {
my ($cb, $mset, $q, $ctx) = @_;
my $fh = $cb->([ 200, ['Content-Type' => 'application/atom+xml']]);
- require PublicInbox::GitCatFile;
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+ my $git = $ctx->{git_dir} ||= PublicInbox::Git->new($ctx->{git_dir});
my $feed_opts = PublicInbox::Feed::get_feedopts($ctx);
my $x = PublicInbox::Hval->new_oneline($q->{q})->as_html;
$x = qq{$x - search results};
$x = mid2path($x);
PublicInbox::Feed::add_to_feed($feed_opts, $fh, $x, $git);
}
- $git = undef;
PublicInbox::Feed::end_feed($fh);
}
anchor_idx => 0,
};
- require PublicInbox::GitCatFile;
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
+ require PublicInbox::Git;
+ my $git = $ctx->{git} ||= PublicInbox::Git->new($ctx->{git_dir});
if ($flat) {
pre_anchor_entry($seen, $_) for (@$msgs);
__thread_entry(\$cb, $git, $state, $_, 0) for (@$msgs);
use URI::Escape qw(uri_escape_utf8 uri_unescape);
use constant SSOMA_URL => 'http://ssoma.public-inbox.org/';
use constant PI_URL => 'http://public-inbox.org/';
+require PublicInbox::Git;
our $LISTNAME_RE = qr!\A/([\w\.\-]+)!;
our $MID_RE = qr!([^/]+)!;
our $END_RE = qr!(f/|T/|t/|t\.mbox(?:\.gz)?|t\.atom|raw|)!;
require PublicInbox::Feed;
require PublicInbox::View;
require PublicInbox::Thread;
- require PublicInbox::GitCatFile;
require Email::MIME;
require Digest::SHA;
require POSIX;
my $git_dir = $pi_config->get($listname, "mainrepo");
if (defined $git_dir) {
$ctx->{git_dir} = $git_dir;
+ $ctx->{git} = PublicInbox::Git->new($git_dir);
$ctx->{listname} = $listname;
return;
}
my ($ctx) = @_;
require PublicInbox::MID;
my $path = PublicInbox::MID::mid2path($ctx->{mid});
- my @cmd = ('git', "--git-dir=$ctx->{git_dir}",
- qw(cat-file blob), "HEAD:$path");
- my $pid = open my $fh, '-|';
- defined $pid or die "fork failed: $!\n";
- if ($pid == 0) {
- open STDERR, '>', '/dev/null'; # ignore errors
- exec @cmd or die "exec failed: $!\n";
- } else {
- my $blob = eval { local $/; <$fh> };
- close $fh;
- $? == 0 ? \$blob : undef;
- }
+ $ctx->{git}->cat_file("HEAD:$path");
}
# /$LISTNAME/$MESSAGE_ID/raw -> raw mbox
my $dir = tempdir(CLEANUP => 1);
use Cwd qw/getcwd/;
-use_ok 'PublicInbox::GitCatFile';
+use_ok 'PublicInbox::Git';
{
is(system(qw(git init -q --bare), $dir), 0, 'created git directory');
my @cmd = ('git', "--git-dir=$dir", 'fast-import', '--quiet');
}
{
- my $gcf = PublicInbox::GitCatFile->new($dir);
+ my $gcf = PublicInbox::Git->new($dir);
my $f = 'HEAD:foo.txt';
my @x = $gcf->check($f);
is(scalar @x, 3, 'returned 3 element array for existing file');
is(0, $?, 'hashed object successfully');
chomp $buf;
- my $gcf = PublicInbox::GitCatFile->new($dir);
+ my $gcf = PublicInbox::Git->new($dir);
my $rsize;
is($gcf->cat_file($buf, sub {
$rsize = ${$_[1]};