- $head =~ /^([0-9a-f]{40}) (\S+) ([0-9]+)$/ or
- fail($self, "Unexpected result from async git cat-file: $head");
- my ($oid_hex, $type, $size) = ($1, $2, $3 + 0);
- my $bref = read_cat_in_full($self, $in, $size);
- eval { $cb->($bref, $oid_hex, $type, $size, $arg) };
+sub cat_async_step ($$) {
+ my ($self, $inflight) = @_;
+ die 'BUG: inflight empty or odd' if scalar(@$inflight) < 3;
+ my ($req, $cb, $arg) = @$inflight[0, 1, 2];
+ my $rbuf = delete($self->{rbuf}) // \(my $new = '');
+ my ($bref, $oid, $type, $size);
+ my $head = my_readline($self->{in}, $rbuf);
+ # ->fail may be called via Gcf2Client.pm
+ if ($head =~ /^([0-9a-f]{40,}) (\S+) ([0-9]+)$/) {
+ ($oid, $type, $size) = ($1, $2, $3 + 0);
+ $bref = my_read($self->{in}, $rbuf, $size + 1) or
+ $self->fail(defined($bref) ? 'read EOF' : "read: $!");
+ chop($$bref) eq "\n" or $self->fail('LF missing after blob');
+ } elsif ($head =~ s/ missing\n//s) {
+ $oid = $head;
+ # ref($req) indicates it's already been retried
+ # -gcf2 retries internally, so it never hits this path:
+ if (!ref($req) && !$in_cleanup && $self->alternates_changed) {
+ return cat_async_retry($self, $inflight);
+ }
+ $type = 'missing';
+ $oid = ref($req) ? $$req : $req if $oid eq '';
+ } else {
+ my $err = $! ? " ($!)" : '';
+ $self->fail("bad result from async cat-file: $head$err");
+ }
+ $self->{rbuf} = $rbuf if $$rbuf ne '';
+ splice(@$inflight, 0, 3); # don't retry $cb on ->fail
+ eval { $cb->($bref, $oid, $type, $size, $arg) };
+ async_err($self, $req, $oid, $@, 'cat') if $@;