+ my $dst = $self->{cur_dst} // $self->{dst};
+ my $fini = PublicInbox::OnDestroy->new($$, \&v1_done, $self);
+ my $cmd = [ @$pfx, clone_cmd($lei, my $opt = {}), "$uri", $dst ];
+ my $ref = $self->{-ent} ? $self->{-ent}->{reference} : undef;
+ defined($ref) && -e "$self->{dst}$ref" and
+ push @$cmd, '--reference', "$self->{dst}$ref";
+ start_clone($self, $cmd, $opt, $fini);
+
+ $lei->{opt}->{'inbox-config'} =~ /\A(?:always|v1)\z/s and
+ _get_txt_start($self, '_/text/config/raw', $fini);
+ my $d = $self->{-ent} ? $self->{-ent}->{description} : undef;
+ defined($d) ? ($self->{'txt.description'} = $d) :
+ _get_txt_start($self, 'description', $fini);
+
+ reap_live() until ($nohang || !keys(%$LIVE)); # 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): $!";
+ if (defined $ent->{head}) {
+ $f = "$edst/HEAD";
+ open $fh, '>', $f or die "open($f): $!";
+ print $fh $ent->{head}, "\n" or die "print($f): $!";
+ close $fh or die "close($f): $!";
+ }
+}
+
+sub reap_clone { # 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, '<', "$o/info/alternates")) {
+ chomp(my @l = <$fh>);
+ for (@l) { $_ = File::Spec->abs2rel($_, $o)."\n" }
+ my $f = File::Temp->new(TEMPLATE => '.XXXX', DIR => "$o/info");
+ print $f @l;
+ $f->flush or die "flush($f): $!";
+ rename($f->filename, "$o/info/alternates") or
+ die "rename($f, $o/info/alternates): $!";
+ $f->unlink_on_destroy(0);
+ }
+ write_makefile($dst, 1);