+ my $uri = URI->new($self->{cur_src} // $self->{src});
+ defined($lei->{opt}->{epoch}) and
+ die "$uri is a v1 inbox, --epoch is not supported\n";
+ $self->{-torsocks} //= $curl->torsocks($lei, $uri) or return;
+ my $dst = $self->{cur_dst} // $self->{dst};
+ my $fini = PublicInbox::OnDestroy->new($$, \&v1_done, $self);
+ my $cmd = [ @{$self->{-torsocks}}, clone_cmd($lei, my $opt = {}),
+ "$uri", $dst ];
+ my $fgrp = forkgroup_prep($self, $uri);
+ if (!defined($fgrp) && defined($self->{-ent})) {
+ if (defined(my $ref = $self->{-ent}->{reference})) {
+ -e "$self->{dst}$ref" and
+ push @$cmd, '--reference', "$self->{dst}$ref";
+ }
+ }
+ $fgrp ? fgrp_fetch($fgrp, $fini) :
+ start_clone($self, $cmd, $opt, $fini);
+
+ if (!$self->{-is_epoch} && $lei->{opt}->{'inbox-config'} =~
+ /\A(?:always|v1)\z/s) {
+ _get_txt_start($self, '_/text/config/raw', $fini);
+ }
+
+ my $d = $self->{-ent} ? $self->{-ent}->{description} : undef;
+ $self->{'txt.description'} = $d if defined $d;
+ (!defined($d) && !$nohang) and
+ _get_txt_start($self, 'description', $fini);
+
+ $nohang or do_reap($self, 1); # for non-manifest clone
+}
+
+sub parse_epochs ($$) {
+ my ($opt_epochs, $v2_epochs) = @_; # $epochs "LOW..HIGH"
+ $opt_epochs // return; # undef => all epochs
+ my ($lo, $dotdot, $hi, @extra) = split(/(\.\.)/, $opt_epochs);
+ undef($lo) if ($lo // '') eq '';
+ my $re = qr/\A~?[0-9]+\z/;
+ if (@extra || (($lo // '0') !~ $re) ||
+ (($hi // '0') !~ $re) ||
+ !(grep(defined, $lo, $hi))) {
+ die <<EOM;
+--epoch=$opt_epochs not in the form of `LOW..HIGH', `LOW..', nor `..HIGH'
+EOM
+ }
+ my @n = sort { $a <=> $b } keys %$v2_epochs;
+ for (grep(defined, $lo, $hi)) {
+ if (/\A[0-9]+\z/) {
+ $_ > $n[-1] and die
+"`$_' exceeds maximum available epoch ($n[-1])\n";
+ $_ < $n[0] and die
+"`$_' is lower than minimum available epoch ($n[0])\n";
+ } elsif (/\A~([0-9]+)/) {
+ my $off = -$1 - 1;
+ $n[$off] // die "`$_' is out of range\n";
+ $_ = $n[$off];
+ } else { die "`$_' not understood\n" }
+ }
+ defined($lo) && defined($hi) && $lo > $hi and die
+"low value (`$lo') exceeds high (`$hi')\n";
+ $lo //= $n[0] if $dotdot;
+ $hi //= $n[-1] if $dotdot;
+ $hi //= $lo;
+ my $want = {};
+ for ($lo..$hi) {
+ if (defined $v2_epochs->{$_}) {
+ $want->{$_} = 1;
+ } else {
+ warn
+"# epoch $_ is not available (non-fatal, $lo..$hi)\n";
+ }
+ }
+ $want
+}
+
+sub init_placeholder ($$$) {
+ my ($src, $edst, $ent) = @_;
+ PublicInbox::Import::init_bare($edst);
+ my $f = "$edst/config";
+ open my $fh, '>>', $f or die "open($f): $!";
+ print $fh <<EOM or die "print($f): $!";
+[remote "origin"]
+ url = $src
+ fetch = +refs/*:refs/*
+ mirror = true
+
+; This git epoch was created read-only and "public-inbox-fetch"
+; will not fetch updates for it unless write permission is added.
+; Hint: chmod +w $edst
+EOM
+ if (defined($ent->{owner})) {
+ print $fh <<EOM or die "print($f): $!";
+[gitweb]
+ owner = $ent->{owner}
+EOM
+ }
+ close $fh or die "close($f): $!";
+ my %map = (head => 'HEAD', description => undef);
+ while (my ($key, $fn) = each %map) {
+ my $val = $ent->{$key} // next;
+ $fn //= $key;
+ $fn = "$edst/$fn";
+ open $fh, '>', $fn or die "open($fn): $!";
+ print $fh $val, "\n" or die "print($fn): $!";
+ close $fh or die "close($fn): $!";
+ }
+}
+
+sub reap_cmd { # async, called via SIGCHLD
+ my ($self, $cmd) = @_;
+ my $cerr = $?;
+ $? = 0; # don't let it influence normal exit
+ if ($cerr) {
+ kill('TERM', keys %$LIVE);
+ $self->{lei}->child_error($cerr, "@$cmd failed");
+ }
+}
+
+sub v1_done { # called via OnDestroy
+ my ($self) = @_;
+ return if $self->{dry_run} || !$LIVE;
+ _write_inbox_config($self);
+ my $dst = $self->{cur_dst} // $self->{dst};
+ if (defined(my $o = $self->{-ent} ? $self->{-ent}->{owner} : undef)) {
+ run_die([qw(git config -f), "$dst/config", 'gitweb.owner', $o]);
+ }
+ my $o = "$dst/objects";
+ if (open(my $fh, '<', my $fn = "$o/info/alternates")) {;
+ my $base = File::Spec->rel2abs($o);
+ my @l = <$fh>;
+ my $ft;
+ for (@l) {
+ next unless m!\A/!;
+ $_ = File::Spec->abs2rel($_, $base);
+ $ft //= File::Temp->new(TEMPLATE => '.XXXX',
+ DIR => "$o/info");
+ }
+ if ($ft) {
+ print $ft @l or die "print($ft): $!";
+ $ft->flush or die "flush($ft): $!";
+ ft_rename($ft, $fn, 0666, $fh);
+ }
+ }
+ pack_refs($self, $dst) if delete $self->{-do_pack_refs};
+ eval { set_description($self) };
+ warn $@ if $@;
+ return if ($self->{-is_epoch} ||
+ $self->{lei}->{opt}->{'inbox-config'} ne 'always');
+ write_makefile($dst, 1);