ref:main

UploadPackV2: git fetch fails with 'expected no other sections to be sent after no ready' #27

closed Opened by cole.christensen@gmail.com

Links

No links yet.

Problem

git fetch against a repo served by UploadPackV2 fails when the client’s local history is unrelated to what’s on the server:

fatal: expected no other sections to be sent after no 'ready'

Reproduced against fangorn/hephaestus on 2026-04-18 — a local git fetch errors out because the client sent have lines that don’t match any object on the server.

Root cause

handle_fetch/2 calls build_acknowledgments/2 without passing whether the client sent done. When the client sends done + haves but none match, the server emits:

0014acknowledgments
0008NAK
0001000dpackfile
...<sideband>...
0000

Per protocol v2 (Documentation/technical/protocol-v2.txt):

acknowledgments = PKT-LINE(\"acknowledgments\" LF)
(nak | *ack)
[ready]

and:

If the server has found a common base commit, or the client has sent a done line, the server will send ready followed by the packfile section.

So: once done is in the request, the acks section must either end with ready or be omitted entirely. Sending NAK followed by a packfile is a protocol violation, and real git clients reject it with the error above.

Fix

  • Parse done from the fetch args.
  • done + no matching haves → omit the acks section (packfile sent unconditionally).
  • done + some ACKs → emit ACK … / ready / delim.
  • No done (multi-round negotiation) → emit NAK / flush or ACK … / flush; client sends another round.

Tests

  • Unit: test/ex_git_objectstore/protocol/upload_pack_v2_test.exs — state-machine assertions for the protocol v2 ack/ready invariant.
  • Interop: test/ex_git_objectstore/integration/upload_pack_v2_git_client_test.exs — spins up a minimal git:// TCP daemon and drives git clone / git fetch against UploadPackV2 with a real git CLI. Includes the hephaestus regression scenario.