# Cover push pack closure across cancellation paths

`3780862`→[main](/content/gh/entireio/git-sync/commits/main/index.html)· Soph·3mo ago·2 files·+46 added/-0 removed

## Sessions

61b6f9608f08View transcript

[?\
Can you take a look at the go code (wasm) in /Users/soph/Work/entire/devenv/entire-io-worktree1 based a bit on that I wonder if something like this can be build:Codex·GPT-5.4·1 step](/content/gh/entireio/git-sync/session/019d6d29-8cf7-7fe3-adc9-8c3e4d9d5603#timeline-61b6f9608f08/index.html)

## Changes

2

- docs  
    
  - Mrewrite-issue-list.md+1

- internal/gitproto

- Mpush_test.go+45

```
159 unmodified lines

160
161
162
163
164
165
166

159 unmodified lines

- Direct strategy-level error-path tests now verify that relay bootstrap and incremental paths close source pack streams when pushes fail.
- Batched integration coverage now also exercises a failed checkpoint pack push followed by a resume-from-temp-ref retry.
- Lower-level `gitproto.PushPack` rejection paths now also close the provided pack stream instead of leaking it on preflight command errors.
- `gitproto.PushPack` now also has direct closure coverage for cancellation, server-side receive-pack errors, and success.
- `gitproto` fetch tests now verify response-body closure symmetry for both v1 and v2 decode-failure paths.
- This still wants a fuller close-audit around lower-level transport interruption paths before it should be considered fully done.
```

Mdocs/rewrite-issue-list.md+1

```
114 unmodified lines

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

114 unmodified lines

}
}

func TestPushPackClosesPackOnContextCanceled(t *testing.T) {
    started := make(chan struct{}, 1)
    ep, err := transport.NewEndpoint("https://example.com/repo.git")
    if err != nil {
        t.Fatalf("parse endpoint: %v", err)
    }
    conn := NewConn(ep, "target", nil, roundTripperFunc(func(req *http.Request) (*http.Response, error) {
        started <- struct{}{}
        <-req.Context().Done()
        return nil, req.Context().Err()
    }))

pack := &trackingReadCloser{ReadCloser: io.NopCloser(bytes.NewBufferString("PACK"))}
    adv := packp.NewAdvRefs()
    adv.Capabilities = capability.NewList()

ctx, cancel := context.WithCancel(context.Background())
    done := make(chan error, 1)
    go func() {
        done <- PushPack(ctx, conn, adv, []PushCommand{{
            Name: "refs/heads/main",
            New:  plumbing.NewHash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
        }}, pack, false)
    }()

select {
    case <-started:
    case <-time.After(2 * time.Second):
        t.Fatal("request did not reach server before timeout")
    }
    cancel()

select {
    case err = <-done:
    case <-time.After(2 * time.Second):
        t.Fatal("PushPack did not return after cancellation")
    }
    if err == nil {
        t.Fatal("expected context cancellation error")
    }
    if !pack.closed {
        t.Fatal("expected pack to be closed on cancellation")
    }
}

func TestPushPackStartsHTTPBeforePackFullyRead(t *testing.T) {
    started := make(chan struct{}, 1)
    release := make(chan struct{})
```
