git-remote-entire: don't fail push on feeder errors after send-pack exited cleanly · Entire

git-remote-entire: don't fail push on feeder errors after send-pack exited cleanly

6c0e477→main·

pjbgf·1mo ago·1 file·+17 added/-8 removed

handlePush waited on the feeder goroutine before sp.Wait(), and any non-nil feedErr aborted the push. But the feeder's io.Copy reads from resp's underlying TCP socket, which can drop after send-pack has already drained the full receive-pack report-status and exited 0 -- e.g. when httptest.Server.Close runs in a parallel CI test or an idle-conn reaper closes the keep-alive. The push was complete at the protocol level (ref committed server-side, send-pack happy, helper-status emitted) but the helper still surfaced "fatal: piping receive-pack response to send-pack: read tcp ...: use of closed network connection" and git exited non-zero.

c70d7cddf moved resp.Close into the feeder so main couldn't slam the body shut mid-Copy, but it didn't cover the externally-closed-socket case. Treat send-pack's exit code as the authoritative signal: if it exited 0, the push succeeded and any feeder error is cleanup noise; if it failed, surface both errors as before.

Assisted-by: Claude Opus 4.7 noreply@anthropic.com Signed-off-by: Paulo Gomes paulo@entire.io

Sessions

b409c750e629View transcript

Changes

1

172 unmodified lines

173
174
175
176
177
178
179
180
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
182
183
191
192
193
185
194
195
196
197

172 unmodified lines

return fmt.Errorf("writing push terminator: %w", err)
    
    if err := <-feedErr; err != nil {
        if waitErr := sp.Wait(); waitErr != nil {
            return errors.Join(err, fmt.Errorf("send-pack exited after feeder error: %w", waitErr))
        }
        return err
    // Wait for send-pack first: its exit code is the authoritative
    // signal for push success. If send-pack exited 0, it parsed a
    // valid receive-pack report-status — the protocol completed.
    // Any feeder error after that point is cleanup noise: io.Copy
    // reading from resp can race with the server-side close of the
    // underlying TCP socket (httptest.Server.Close in parallel CI
    // tests, idle-conn reaping in long-running daemons) and surface
    // "use of closed network connection" *after* send-pack has
    // already drained everything it needed. Failing the push on
    // that turned successful pushes into fatal errors.
    spErr := sp.Wait()
    feedRes := <-feedErr
    if spErr == nil {
        return nil
    }
    if err := sp.Wait(); err != nil {
        return fmt.Errorf("send-pack exited with error: %w", err)
    if feedRes != nil {
        return errors.Join(feedRes, fmt.Errorf("send-pack exited after feeder error: %w", spErr))
    }
    return nil
    return fmt.Errorf("send-pack exited with error: %w", spErr)
}

// readPushBatch collects the "push <src>:<dst>" lines that follow the

Minternal/remotehelper/githelper/push.go+17/-8